mirror of
https://github.com/nushell/nushell.git
synced 2025-05-31 07:08:22 +02:00
# Description With the current typechecking logic this property has no effect. It was only used in the example testing, and provided some indication of this vectorizing property. With #9742 all commands that previously declared it have explicit list signatures. If we want to get it back in the future we can reconstruct it from the signature. Simplifies the example testing a bit. # User-Facing Changes Causes a breaking change for plugins that previously declared it. While this causes a compile fail, this was already broken by our more stringent type checking. This will be a good reminder for plugin authors to update their signature as well to reflect the more stringent type checking.
209 lines
7.1 KiB
Rust
209 lines
7.1 KiB
Rust
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
|
use nu_engine::CallExt;
|
|
use nu_protocol::ast::Call;
|
|
use nu_protocol::ast::CellPath;
|
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
use nu_protocol::Category;
|
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
|
|
|
#[derive(Clone)]
|
|
pub struct SubCommand;
|
|
|
|
struct Arguments {
|
|
substring: String,
|
|
cell_paths: Option<Vec<CellPath>>,
|
|
case_insensitive: bool,
|
|
not_contain: bool,
|
|
}
|
|
|
|
impl CmdArgument for Arguments {
|
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
self.cell_paths.take()
|
|
}
|
|
}
|
|
|
|
impl Command for SubCommand {
|
|
fn name(&self) -> &str {
|
|
"str contains"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build("str contains")
|
|
.input_output_types(vec![
|
|
(Type::String, Type::Bool),
|
|
// TODO figure out cell-path type behavior
|
|
(Type::Table(vec![]), Type::Table(vec![])),
|
|
(Type::Record(vec![]), Type::Record(vec![])),
|
|
(Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool)))
|
|
])
|
|
.required("string", SyntaxShape::String, "the substring to find")
|
|
.rest(
|
|
"rest",
|
|
SyntaxShape::CellPath,
|
|
"For a data structure input, check strings at the given cell paths, and replace with result",
|
|
)
|
|
.switch("ignore-case", "search is case insensitive", Some('i'))
|
|
.switch("not", "does not contain", Some('n'))
|
|
.category(Category::Strings)
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"Checks if string input contains a substring."
|
|
}
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
|
vec!["substring", "match", "find", "search"]
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
engine_state: &EngineState,
|
|
stack: &mut Stack,
|
|
call: &Call,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
|
let args = Arguments {
|
|
substring: call.req::<String>(engine_state, stack, 0)?,
|
|
cell_paths,
|
|
case_insensitive: call.has_flag("ignore-case"),
|
|
not_contain: call.has_flag("not"),
|
|
};
|
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
|
}
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
vec![
|
|
Example {
|
|
description: "Check if input contains string",
|
|
example: "'my_library.rb' | str contains '.rb'",
|
|
result: Some(Value::test_bool(true)),
|
|
},
|
|
Example {
|
|
description: "Check if input contains string case insensitive",
|
|
example: "'my_library.rb' | str contains -i '.RB'",
|
|
result: Some(Value::test_bool(true)),
|
|
},
|
|
Example {
|
|
description: "Check if input contains string in a record",
|
|
example: "{ ColA: test, ColB: 100 } | str contains 'e' ColA",
|
|
result: Some(Value::Record {
|
|
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
|
vals: vec![Value::test_bool(true), Value::test_int(100)],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
Example {
|
|
description: "Check if input contains string in a table",
|
|
example: " [[ColA ColB]; [test 100]] | str contains -i 'E' ColA",
|
|
result: Some(Value::List {
|
|
vals: vec![Value::Record {
|
|
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
|
vals: vec![Value::test_bool(true), Value::test_int(100)],
|
|
span: Span::test_data(),
|
|
}],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
Example {
|
|
description: "Check if input contains string in a table",
|
|
example: " [[ColA ColB]; [test hello]] | str contains 'e' ColA ColB",
|
|
result: Some(Value::List {
|
|
vals: vec![Value::Record {
|
|
cols: vec!["ColA".to_string(), "ColB".to_string()],
|
|
vals: vec![Value::test_bool(true), Value::test_bool(true)],
|
|
span: Span::test_data(),
|
|
}],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
Example {
|
|
description: "Check if input string contains 'banana'",
|
|
example: "'hello' | str contains 'banana'",
|
|
result: Some(Value::test_bool(false)),
|
|
},
|
|
Example {
|
|
description: "Check if list contains string",
|
|
example: "[one two three] | str contains o",
|
|
result: Some(Value::List {
|
|
vals: vec![
|
|
Value::test_bool(true),
|
|
Value::test_bool(true),
|
|
Value::test_bool(false),
|
|
],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
Example {
|
|
description: "Check if list does not contain string",
|
|
example: "[one two three] | str contains -n o",
|
|
result: Some(Value::List {
|
|
vals: vec![
|
|
Value::test_bool(false),
|
|
Value::test_bool(false),
|
|
Value::test_bool(true),
|
|
],
|
|
span: Span::test_data(),
|
|
}),
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
fn action(
|
|
input: &Value,
|
|
Arguments {
|
|
case_insensitive,
|
|
not_contain,
|
|
substring,
|
|
..
|
|
}: &Arguments,
|
|
head: Span,
|
|
) -> Value {
|
|
match input {
|
|
Value::String { val, .. } => Value::bool(
|
|
match case_insensitive {
|
|
true => {
|
|
if *not_contain {
|
|
!val.to_lowercase()
|
|
.contains(substring.to_lowercase().as_str())
|
|
} else {
|
|
val.to_lowercase()
|
|
.contains(substring.to_lowercase().as_str())
|
|
}
|
|
}
|
|
false => {
|
|
if *not_contain {
|
|
!val.contains(substring)
|
|
} else {
|
|
val.contains(substring)
|
|
}
|
|
}
|
|
},
|
|
head,
|
|
),
|
|
Value::Error { .. } => input.clone(),
|
|
_ => Value::Error {
|
|
error: Box::new(ShellError::OnlySupportsThisInputType {
|
|
exp_input_type: "string".into(),
|
|
wrong_type: input.get_type().to_string(),
|
|
dst_span: head,
|
|
src_span: input.expect_span(),
|
|
}),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_examples() {
|
|
use crate::test_examples;
|
|
|
|
test_examples(SubCommand {})
|
|
}
|
|
}
|