mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
Make string related commands parse-time evaluatable (#13032)
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> Related meta-issue: #10239. # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> This PR will modify some `str`-related commands so that they can be evaluated at parse time. See the following list for those implemented by this pr. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> Available now: - `str` subcommands - `trim` - `contains` - `distance` - `ends-with` - `expand` - `index-of` - `join` - `replace` - `reverse` - `starts-with` - `stats` - `substring` - `capitalize` - `downcase` - `upcase` - `split` subcommands - `chars` - `column` - `list` - `row` - `words` - `format` subcommands - `date` - `duration` - `filesize` - string related commands - `parse` - `detect columns` - `encode` & `decode` # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> Unresolved questions: - [ ] Is there any routine of testing const expressions? I haven't found any yet. - [ ] Is const expressions required to behave just like there non-const version, like what rust promises? # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --> Unresolved questions: - [ ] Do const commands need special marks in the docs?
This commit is contained in:
parent
36ad7f15c4
commit
96493b26d9
@ -1,6 +1,6 @@
|
|||||||
use indexmap::{indexmap, IndexMap};
|
use indexmap::{indexmap, IndexMap};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::sync::{atomic::AtomicBool, Arc};
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
|
|
||||||
|
@ -45,20 +45,6 @@ impl Command for DetectColumns {
|
|||||||
vec!["split", "tabular"]
|
vec!["split", "tabular"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
if call.has_flag(engine_state, stack, "guess")? {
|
|
||||||
guess_width(engine_state, stack, call, input)
|
|
||||||
} else {
|
|
||||||
detect_columns(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
@ -109,33 +95,87 @@ none 8150224 4 8150220 1% /mnt/c' | detect columns --gue
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn guess_width(
|
fn run(
|
||||||
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?;
|
||||||
|
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
|
||||||
|
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
noheader,
|
||||||
|
num_rows_to_skip,
|
||||||
|
range,
|
||||||
|
};
|
||||||
|
|
||||||
|
if call.has_flag(engine_state, stack, "guess")? {
|
||||||
|
guess_width(engine_state, call, input, args)
|
||||||
|
} else {
|
||||||
|
detect_columns(engine_state, call, input, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let num_rows_to_skip: Option<usize> = call.get_flag_const(working_set, "skip")?;
|
||||||
|
let noheader = call.has_flag_const(working_set, "no-headers")?;
|
||||||
|
let range: Option<Range> = call.get_flag_const(working_set, "combine-columns")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
noheader,
|
||||||
|
num_rows_to_skip,
|
||||||
|
range,
|
||||||
|
};
|
||||||
|
|
||||||
|
if call.has_flag_const(working_set, "guess")? {
|
||||||
|
guess_width(working_set.permanent(), call, input, args)
|
||||||
|
} else {
|
||||||
|
detect_columns(working_set.permanent(), call, input, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
num_rows_to_skip: Option<usize>,
|
||||||
|
noheader: bool,
|
||||||
|
range: Option<Range>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guess_width(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
args: Arguments,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
use super::guess_width::GuessWidth;
|
use super::guess_width::GuessWidth;
|
||||||
let input_span = input.span().unwrap_or(call.head);
|
let input_span = input.span().unwrap_or(call.head);
|
||||||
|
|
||||||
let mut input = input.collect_string("", engine_state.get_config())?;
|
let mut input = input.collect_string("", engine_state.get_config())?;
|
||||||
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?;
|
if let Some(rows) = args.num_rows_to_skip {
|
||||||
if let Some(rows) = num_rows_to_skip {
|
|
||||||
input = input.lines().skip(rows).map(|x| x.to_string()).join("\n");
|
input = input.lines().skip(rows).map(|x| x.to_string()).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut guess_width = GuessWidth::new_reader(Box::new(Cursor::new(input)));
|
let mut guess_width = GuessWidth::new_reader(Box::new(Cursor::new(input)));
|
||||||
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
|
|
||||||
|
|
||||||
let result = guess_width.read_all();
|
let result = guess_width.read_all();
|
||||||
|
|
||||||
if result.is_empty() {
|
if result.is_empty() {
|
||||||
return Ok(Value::nothing(input_span).into_pipeline_data());
|
return Ok(Value::nothing(input_span).into_pipeline_data());
|
||||||
}
|
}
|
||||||
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
|
if !args.noheader {
|
||||||
if !noheader {
|
|
||||||
let columns = result[0].clone();
|
let columns = result[0].clone();
|
||||||
Ok(result
|
Ok(result
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -152,7 +192,7 @@ fn guess_width(
|
|||||||
let record =
|
let record =
|
||||||
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
||||||
match record {
|
match record {
|
||||||
Ok(r) => match &range {
|
Ok(r) => match &args.range {
|
||||||
Some(range) => merge_record(r, range, input_span),
|
Some(range) => merge_record(r, range, input_span),
|
||||||
None => Value::record(r, input_span),
|
None => Value::record(r, input_span),
|
||||||
},
|
},
|
||||||
@ -177,7 +217,7 @@ fn guess_width(
|
|||||||
let record =
|
let record =
|
||||||
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
||||||
match record {
|
match record {
|
||||||
Ok(r) => match &range {
|
Ok(r) => match &args.range {
|
||||||
Some(range) => merge_record(r, range, input_span),
|
Some(range) => merge_record(r, range, input_span),
|
||||||
None => Value::record(r, input_span),
|
None => Value::record(r, input_span),
|
||||||
},
|
},
|
||||||
@ -190,21 +230,18 @@ fn guess_width(
|
|||||||
|
|
||||||
fn detect_columns(
|
fn detect_columns(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
args: Arguments,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name_span = call.head;
|
let name_span = call.head;
|
||||||
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?;
|
|
||||||
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
|
|
||||||
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let input = input.collect_string("", config)?;
|
let input = input.collect_string("", config)?;
|
||||||
|
|
||||||
let input: Vec<_> = input
|
let input: Vec<_> = input
|
||||||
.lines()
|
.lines()
|
||||||
.skip(num_rows_to_skip.unwrap_or_default())
|
.skip(args.num_rows_to_skip.unwrap_or_default())
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -214,13 +251,14 @@ fn detect_columns(
|
|||||||
if let Some(orig_headers) = headers {
|
if let Some(orig_headers) = headers {
|
||||||
let mut headers = find_columns(&orig_headers);
|
let mut headers = find_columns(&orig_headers);
|
||||||
|
|
||||||
if noheader {
|
if args.noheader {
|
||||||
for header in headers.iter_mut().enumerate() {
|
for header in headers.iter_mut().enumerate() {
|
||||||
header.1.item = format!("column{}", header.0);
|
header.1.item = format!("column{}", header.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(noheader
|
Ok(args
|
||||||
|
.noheader
|
||||||
.then_some(orig_headers)
|
.then_some(orig_headers)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(input)
|
.chain(input)
|
||||||
@ -273,7 +311,7 @@ fn detect_columns(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match &range {
|
match &args.range {
|
||||||
Some(range) => merge_record(record, range, name_span),
|
Some(range) => merge_record(record, range, name_span),
|
||||||
None => Value::record(record, name_span),
|
None => Value::record(record, name_span),
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,9 @@ use base64::{
|
|||||||
Engine,
|
Engine,
|
||||||
};
|
};
|
||||||
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{EngineState, Stack},
|
engine::EngineState,
|
||||||
PipelineData, ShellError, Span, Spanned, Value,
|
PipelineData, ShellError, Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,22 +41,24 @@ impl CmdArgument for Arguments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct Base64CommandArguments {
|
||||||
|
pub(super) character_set: Option<Spanned<String>>,
|
||||||
|
pub(super) action_type: ActionType,
|
||||||
|
pub(super) binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn operate(
|
pub fn operate(
|
||||||
action_type: ActionType,
|
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
cell_paths: Vec<CellPath>,
|
||||||
|
args: Base64CommandArguments,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let character_set: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "character-set")?;
|
|
||||||
let binary = call.has_flag(engine_state, stack, "binary")?;
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
|
||||||
// Default the character set to standard if the argument is not specified.
|
// Default the character set to standard if the argument is not specified.
|
||||||
let character_set = match character_set {
|
let character_set = match args.character_set {
|
||||||
Some(inner_tag) => inner_tag,
|
Some(inner_tag) => inner_tag,
|
||||||
None => Spanned {
|
None => Spanned {
|
||||||
item: "standard".to_string(),
|
item: "standard".to_string(),
|
||||||
@ -68,9 +69,9 @@ pub fn operate(
|
|||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
encoding_config: Base64Config {
|
encoding_config: Base64Config {
|
||||||
character_set,
|
character_set,
|
||||||
action_type,
|
action_type: args.action_type,
|
||||||
},
|
},
|
||||||
binary,
|
binary: args.binary,
|
||||||
cell_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,6 +46,10 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -53,8 +57,27 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
|
||||||
let encoding: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
let encoding: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
||||||
|
run(call, input, encoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let encoding: Option<Spanned<String>> = call.opt_const(working_set, 0)?;
|
||||||
|
run(call, input, encoding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
encoding: Option<Spanned<String>>,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ByteStream(stream, ..) => {
|
PipelineData::ByteStream(stream, ..) => {
|
||||||
@ -97,7 +120,6 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::base64::{operate, ActionType, CHARACTER_SET_DESC};
|
use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -66,6 +66,10 @@ impl Command for DecodeBase64 {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -73,7 +77,34 @@ impl Command for DecodeBase64 {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(ActionType::Decode, engine_state, stack, call, input)
|
let character_set: Option<Spanned<String>> =
|
||||||
|
call.get_flag(engine_state, stack, "character-set")?;
|
||||||
|
let binary = call.has_flag(engine_state, stack, "binary")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
let args = Base64CommandArguments {
|
||||||
|
action_type: ActionType::Decode,
|
||||||
|
binary,
|
||||||
|
character_set,
|
||||||
|
};
|
||||||
|
operate(engine_state, call, input, cell_paths, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let character_set: Option<Spanned<String>> =
|
||||||
|
call.get_flag_const(working_set, "character-set")?;
|
||||||
|
let binary = call.has_flag_const(working_set, "binary")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
let args = Base64CommandArguments {
|
||||||
|
action_type: ActionType::Decode,
|
||||||
|
binary,
|
||||||
|
character_set,
|
||||||
|
};
|
||||||
|
operate(working_set.permanent(), call, input, cell_paths, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,10 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -76,9 +80,30 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
|
||||||
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
||||||
|
run(call, input, encoding, ignore_errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let encoding: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let ignore_errors = call.has_flag_const(working_set, "ignore-errors")?;
|
||||||
|
run(call, input, encoding, ignore_errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
encoding: Spanned<String>,
|
||||||
|
ignore_errors: bool,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ByteStream(stream, ..) => {
|
PipelineData::ByteStream(stream, ..) => {
|
||||||
@ -113,7 +138,6 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::base64::{operate, ActionType, CHARACTER_SET_DESC};
|
use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -70,6 +70,10 @@ impl Command for EncodeBase64 {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -77,7 +81,34 @@ impl Command for EncodeBase64 {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(ActionType::Encode, engine_state, stack, call, input)
|
let character_set: Option<Spanned<String>> =
|
||||||
|
call.get_flag(engine_state, stack, "character-set")?;
|
||||||
|
let binary = call.has_flag(engine_state, stack, "binary")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
let args = Base64CommandArguments {
|
||||||
|
action_type: ActionType::Encode,
|
||||||
|
binary,
|
||||||
|
character_set,
|
||||||
|
};
|
||||||
|
operate(engine_state, call, input, cell_paths, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let character_set: Option<Spanned<String>> =
|
||||||
|
call.get_flag_const(working_set, "character-set")?;
|
||||||
|
let binary = call.has_flag_const(working_set, "binary")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
let args = Base64CommandArguments {
|
||||||
|
action_type: ActionType::Encode,
|
||||||
|
binary,
|
||||||
|
character_set,
|
||||||
|
};
|
||||||
|
operate(working_set.permanent(), call, input, cell_paths, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,36 +38,6 @@ impl Command for FormatDate {
|
|||||||
vec!["fmt", "strftime"]
|
vec!["fmt", "strftime"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
if call.has_flag(engine_state, stack, "list")? {
|
|
||||||
return Ok(PipelineData::Value(
|
|
||||||
generate_strftime_list(head, false),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let format = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| match &format {
|
|
||||||
Some(format) => format_helper(value, format.item.as_str(), format.span, head),
|
|
||||||
None => format_helper_rfc2822(value, head),
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
@ -104,6 +74,61 @@ impl Command for FormatDate {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let list = call.has_flag(engine_state, stack, "list")?;
|
||||||
|
let format = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
|
||||||
|
run(engine_state, call, input, list, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let list = call.has_flag_const(working_set, "list")?;
|
||||||
|
let format = call.opt_const::<Spanned<String>>(working_set, 0)?;
|
||||||
|
run(working_set.permanent(), call, input, list, format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
list: bool,
|
||||||
|
format: Option<Spanned<String>>,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
if list {
|
||||||
|
return Ok(PipelineData::Value(
|
||||||
|
generate_strftime_list(head, false),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
input.map(
|
||||||
|
move |value| match &format {
|
||||||
|
Some(format) => format_helper(value, format.item.as_str(), format.span, head),
|
||||||
|
None => format_helper_rfc2822(value, head),
|
||||||
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_from<Tz: TimeZone>(date_time: DateTime<Tz>, formatter: &str, span: Span) -> Value
|
fn format_from<Tz: TimeZone>(date_time: DateTime<Tz>, formatter: &str, span: Span) -> Value
|
||||||
|
@ -53,6 +53,10 @@ impl Command for FormatDuration {
|
|||||||
vec!["convert", "display", "pattern", "human readable"]
|
vec!["convert", "display", "pattern", "human readable"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -81,6 +85,33 @@ impl Command for FormatDuration {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let format_value = call
|
||||||
|
.req_const::<Value>(working_set, 0)?
|
||||||
|
.coerce_into_string()?
|
||||||
|
.to_ascii_lowercase();
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let float_precision = working_set.permanent().config.float_precision as usize;
|
||||||
|
let arg = Arguments {
|
||||||
|
format_value,
|
||||||
|
float_precision,
|
||||||
|
cell_paths,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
format_value_impl,
|
||||||
|
arg,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::format_filesize;
|
use nu_protocol::{engine::StateWorkingSet, format_filesize};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
format_value: String,
|
format_value: String,
|
||||||
@ -50,6 +50,10 @@ impl Command for FormatFilesize {
|
|||||||
vec!["convert", "display", "pattern", "human readable"]
|
vec!["convert", "display", "pattern", "human readable"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -76,6 +80,31 @@ impl Command for FormatFilesize {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let format_value = call
|
||||||
|
.req_const::<Value>(working_set, 0)?
|
||||||
|
.coerce_into_string()?
|
||||||
|
.to_ascii_lowercase();
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let arg = Arguments {
|
||||||
|
format_value,
|
||||||
|
cell_paths,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
format_value_impl,
|
||||||
|
arg,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use fancy_regex::{Captures, Regex};
|
use fancy_regex::{Captures, Regex};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::ListStream;
|
use nu_protocol::{engine::StateWorkingSet, ListStream};
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
@ -99,6 +99,10 @@ impl Command for Parse {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -106,19 +110,31 @@ impl Command for Parse {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
let pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
let regex: bool = call.has_flag(engine_state, stack, "regex")?;
|
||||||
|
operate(engine_state, pattern, regex, call, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let pattern: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let regex: bool = call.has_flag_const(working_set, "regex")?;
|
||||||
|
operate(working_set.permanent(), pattern, regex, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
pattern: Spanned<String>,
|
||||||
|
regex: bool,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
|
||||||
let regex: bool = call.has_flag(engine_state, stack, "regex")?;
|
|
||||||
|
|
||||||
let pattern_item = pattern.item;
|
let pattern_item = pattern.item;
|
||||||
let pattern_span = pattern.span;
|
let pattern_span = pattern.span;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::grapheme_flags;
|
use crate::{grapheme_flags, grapheme_flags_const};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -88,6 +89,10 @@ impl Command for SubCommand {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -95,19 +100,28 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
split_chars(engine_state, stack, call, input)
|
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
||||||
|
split_chars(engine_state, call, input, graphemes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let graphemes = grapheme_flags_const(working_set, call)?;
|
||||||
|
split_chars(working_set.permanent(), call, input, graphemes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_chars(
|
fn split_chars(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
graphemes: bool,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
|
||||||
input.map(
|
input.map(
|
||||||
move |x| split_chars_helper(&x, span, graphemes),
|
move |x| split_chars_helper(&x, span, graphemes),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
|
@ -43,16 +43,6 @@ impl Command for SubCommand {
|
|||||||
vec!["separate", "divide", "regex"]
|
vec!["separate", "divide", "regex"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
split_column(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
@ -103,35 +93,83 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_column(
|
fn run(
|
||||||
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name_span = call.head;
|
|
||||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||||
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
|
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
|
||||||
|
let has_regex = call.has_flag(engine_state, stack, "regex")?;
|
||||||
|
|
||||||
let regex = if call.has_flag(engine_state, stack, "regex")? {
|
let args = Arguments {
|
||||||
Regex::new(&separator.item)
|
separator,
|
||||||
|
rest,
|
||||||
|
collapse_empty,
|
||||||
|
has_regex,
|
||||||
|
};
|
||||||
|
split_column(engine_state, call, input, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let separator: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let rest: Vec<Spanned<String>> = call.rest_const(working_set, 1)?;
|
||||||
|
let collapse_empty = call.has_flag_const(working_set, "collapse-empty")?;
|
||||||
|
let has_regex = call.has_flag_const(working_set, "regex")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
separator,
|
||||||
|
rest,
|
||||||
|
collapse_empty,
|
||||||
|
has_regex,
|
||||||
|
};
|
||||||
|
split_column(working_set.permanent(), call, input, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
separator: Spanned<String>,
|
||||||
|
rest: Vec<Spanned<String>>,
|
||||||
|
collapse_empty: bool,
|
||||||
|
has_regex: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_column(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
args: Arguments,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let name_span = call.head;
|
||||||
|
let regex = if args.has_regex {
|
||||||
|
Regex::new(&args.separator.item)
|
||||||
} else {
|
} else {
|
||||||
let escaped = regex::escape(&separator.item);
|
let escaped = regex::escape(&args.separator.item);
|
||||||
Regex::new(&escaped)
|
Regex::new(&escaped)
|
||||||
}
|
}
|
||||||
.map_err(|e| ShellError::GenericError {
|
.map_err(|e| ShellError::GenericError {
|
||||||
error: "Error with regular expression".into(),
|
error: "Error with regular expression".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
span: Some(separator.span),
|
span: Some(args.separator.span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
input.flat_map(
|
input.flat_map(
|
||||||
move |x| split_column_helper(&x, ®ex, &rest, collapse_empty, name_span),
|
move |x| split_column_helper(&x, ®ex, &args.rest, args.collapse_empty, name_span),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -36,16 +36,6 @@ impl Command for SubCommand {
|
|||||||
vec!["separate", "divide", "regex"]
|
vec!["separate", "divide", "regex"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
split_list(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
@ -145,6 +135,33 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let has_regex = call.has_flag(engine_state, stack, "regex")?;
|
||||||
|
let separator: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
split_list(engine_state, call, input, has_regex, separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let has_regex = call.has_flag_const(working_set, "regex")?;
|
||||||
|
let separator: Value = call.req_const(working_set, 0)?;
|
||||||
|
split_list(working_set.permanent(), call, input, has_regex, separator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Matcher {
|
enum Matcher {
|
||||||
@ -188,15 +205,15 @@ impl Matcher {
|
|||||||
|
|
||||||
fn split_list(
|
fn split_list(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
has_regex: bool,
|
||||||
|
separator: Value,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let separator: Value = call.req(engine_state, stack, 0)?;
|
|
||||||
let mut temp_list = Vec::new();
|
let mut temp_list = Vec::new();
|
||||||
let mut returned_list = Vec::new();
|
let mut returned_list = Vec::new();
|
||||||
|
|
||||||
let matcher = Matcher::new(call.has_flag(engine_state, stack, "regex")?, separator)?;
|
let matcher = Matcher::new(has_regex, separator)?;
|
||||||
for val in input {
|
for val in input {
|
||||||
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
||||||
break;
|
break;
|
||||||
|
@ -43,16 +43,6 @@ impl Command for SubCommand {
|
|||||||
vec!["separate", "divide", "regex"]
|
vec!["separate", "divide", "regex"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
split_row(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
@ -109,32 +99,77 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_row(
|
fn run(
|
||||||
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name_span = call.head;
|
|
||||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let regex = if call.has_flag(engine_state, stack, "regex")? {
|
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
||||||
Regex::new(&separator.item)
|
let has_regex = call.has_flag(engine_state, stack, "regex")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
separator,
|
||||||
|
max_split,
|
||||||
|
has_regex,
|
||||||
|
};
|
||||||
|
split_row(engine_state, call, input, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let separator: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let max_split: Option<usize> = call.get_flag_const(working_set, "number")?;
|
||||||
|
let has_regex = call.has_flag_const(working_set, "regex")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
separator,
|
||||||
|
max_split,
|
||||||
|
has_regex,
|
||||||
|
};
|
||||||
|
split_row(working_set.permanent(), call, input, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
has_regex: bool,
|
||||||
|
separator: Spanned<String>,
|
||||||
|
max_split: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_row(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
args: Arguments,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let name_span = call.head;
|
||||||
|
let regex = if args.has_regex {
|
||||||
|
Regex::new(&args.separator.item)
|
||||||
} else {
|
} else {
|
||||||
let escaped = regex::escape(&separator.item);
|
let escaped = regex::escape(&args.separator.item);
|
||||||
Regex::new(&escaped)
|
Regex::new(&escaped)
|
||||||
}
|
}
|
||||||
.map_err(|e| ShellError::GenericError {
|
.map_err(|e| ShellError::GenericError {
|
||||||
error: "Error with regular expression".into(),
|
error: "Error with regular expression".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
span: Some(separator.span),
|
span: Some(args.separator.span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})?;
|
})?;
|
||||||
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
|
||||||
input.flat_map(
|
input.flat_map(
|
||||||
move |x| split_row_helper(&x, ®ex, max_split, name_span),
|
move |x| split_row_helper(&x, ®ex, args.max_split, name_span),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::grapheme_flags;
|
use crate::{grapheme_flags, grapheme_flags_const};
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
@ -96,6 +96,10 @@ impl Command for SubCommand {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -103,40 +107,76 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
split_words(engine_state, stack, call, input)
|
let word_length: Option<usize> = call.get_flag(engine_state, stack, "min-word-length")?;
|
||||||
|
let has_grapheme = call.has_flag(engine_state, stack, "grapheme-clusters")?;
|
||||||
|
let has_utf8 = call.has_flag(engine_state, stack, "utf-8-bytes")?;
|
||||||
|
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
word_length,
|
||||||
|
has_grapheme,
|
||||||
|
has_utf8,
|
||||||
|
graphemes,
|
||||||
|
};
|
||||||
|
split_words(engine_state, call, input, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let word_length: Option<usize> = call.get_flag_const(working_set, "min-word-length")?;
|
||||||
|
let has_grapheme = call.has_flag_const(working_set, "grapheme-clusters")?;
|
||||||
|
let has_utf8 = call.has_flag_const(working_set, "utf-8-bytes")?;
|
||||||
|
let graphemes = grapheme_flags_const(working_set, call)?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
word_length,
|
||||||
|
has_grapheme,
|
||||||
|
has_utf8,
|
||||||
|
graphemes,
|
||||||
|
};
|
||||||
|
split_words(working_set.permanent(), call, input, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
word_length: Option<usize>,
|
||||||
|
has_grapheme: bool,
|
||||||
|
has_utf8: bool,
|
||||||
|
graphemes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_words(
|
fn split_words(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
args: Arguments,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
// let ignore_hyphenated = call.has_flag(engine_state, stack, "ignore-hyphenated")?;
|
// let ignore_hyphenated = call.has_flag(engine_state, stack, "ignore-hyphenated")?;
|
||||||
// let ignore_apostrophes = call.has_flag(engine_state, stack, "ignore-apostrophes")?;
|
// let ignore_apostrophes = call.has_flag(engine_state, stack, "ignore-apostrophes")?;
|
||||||
// let ignore_punctuation = call.has_flag(engine_state, stack, "ignore-punctuation")?;
|
// let ignore_punctuation = call.has_flag(engine_state, stack, "ignore-punctuation")?;
|
||||||
let word_length: Option<usize> = call.get_flag(engine_state, stack, "min-word-length")?;
|
|
||||||
|
|
||||||
if word_length.is_none() {
|
if args.word_length.is_none() {
|
||||||
if call.has_flag(engine_state, stack, "grapheme-clusters")? {
|
if args.has_grapheme {
|
||||||
return Err(ShellError::IncompatibleParametersSingle {
|
return Err(ShellError::IncompatibleParametersSingle {
|
||||||
msg: "--grapheme-clusters (-g) requires --min-word-length (-l)".to_string(),
|
msg: "--grapheme-clusters (-g) requires --min-word-length (-l)".to_string(),
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if call.has_flag(engine_state, stack, "utf-8-bytes")? {
|
if args.has_utf8 {
|
||||||
return Err(ShellError::IncompatibleParametersSingle {
|
return Err(ShellError::IncompatibleParametersSingle {
|
||||||
msg: "--utf-8-bytes (-b) requires --min-word-length (-l)".to_string(),
|
msg: "--utf-8-bytes (-b) requires --min-word-length (-l)".to_string(),
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |x| split_words_helper(&x, word_length, span, graphemes),
|
move |x| split_words_helper(&x, args.word_length, span, args.graphemes),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
|||||||
vec!["convert", "style", "caps", "upper"]
|
vec!["convert", "style", "caps", "upper"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
operate(engine_state, call, input, column_paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
operate(working_set.permanent(), call, input, column_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -72,12 +87,11 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
column_paths: Vec<CellPath>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
|||||||
vec!["lower case", "lowercase"]
|
vec!["lower case", "lowercase"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
operate(engine_state, call, input, column_paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
operate(working_set.permanent(), call, input, column_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -80,12 +95,11 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
column_paths: Vec<CellPath>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
|||||||
vec!["uppercase", "upper case"]
|
vec!["uppercase", "upper case"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
operate(engine_state, call, input, column_paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
operate(working_set.permanent(), call, input, column_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -57,12 +72,11 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
column_paths: Vec<CellPath>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
|
@ -52,6 +52,10 @@ impl Command for SubCommand {
|
|||||||
vec!["substring", "match", "find", "search"]
|
vec!["substring", "match", "find", "search"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -84,6 +88,43 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
if call.has_flag_const(working_set, "not")? {
|
||||||
|
nu_protocol::report_error_new(
|
||||||
|
working_set.permanent(),
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Deprecated option".into(),
|
||||||
|
msg: "`str contains --not {string}` is deprecated and will be removed in 0.95."
|
||||||
|
.into(),
|
||||||
|
span: Some(call.head),
|
||||||
|
help: Some("Please use the `not` operator instead.".into()),
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
substring: call.req_const::<String>(working_set, 0)?,
|
||||||
|
cell_paths,
|
||||||
|
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
|
||||||
|
not_contain: call.has_flag_const(working_set, "not")?,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::levenshtein_distance;
|
use nu_protocol::{engine::StateWorkingSet, levenshtein_distance};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
@ -49,6 +49,10 @@ impl Command for SubCommand {
|
|||||||
vec!["edit", "levenshtein"]
|
vec!["edit", "levenshtein"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -66,6 +70,28 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let compare_string: String = call.req_const(working_set, 0)?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
compare_string,
|
||||||
|
cell_paths,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "get the edit distance between two strings",
|
description: "get the edit distance between two strings",
|
||||||
|
@ -50,6 +50,10 @@ impl Command for SubCommand {
|
|||||||
vec!["suffix", "match", "find", "search"]
|
vec!["suffix", "match", "find", "search"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -67,6 +71,28 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
substring: call.req_const::<String>(working_set, 0)?,
|
||||||
|
cell_paths,
|
||||||
|
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -179,18 +179,42 @@ impl Command for SubCommand {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let is_path = call.has_flag(engine_state, stack, "path")?;
|
||||||
|
run(call, input, is_path, engine_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let is_path = call.has_flag_const(working_set, "path")?;
|
||||||
|
run(call, input, is_path, working_set.permanent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
is_path: bool,
|
||||||
|
engine_state: &EngineState,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: span });
|
return Err(ShellError::PipelineEmpty { dst_span: span });
|
||||||
}
|
}
|
||||||
let is_path = call.has_flag(engine_state, stack, "path")?;
|
|
||||||
input.map(
|
input.map(
|
||||||
move |v| {
|
move |v| {
|
||||||
let value_span = v.span();
|
let value_span = v.span();
|
||||||
@ -212,7 +236,6 @@ impl Command for SubCommand {
|
|||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn str_expand(contents: &str, span: Span, value_span: Span) -> Value {
|
fn str_expand(contents: &str, span: Span, value_span: Span) -> Value {
|
||||||
use bracoxide::{
|
use bracoxide::{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grapheme_flags;
|
use crate::{grapheme_flags, grapheme_flags_const};
|
||||||
use nu_cmd_base::{
|
use nu_cmd_base::{
|
||||||
input_handler::{operate, CmdArgument},
|
input_handler::{operate, CmdArgument},
|
||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Range;
|
use nu_protocol::{engine::StateWorkingSet, Range};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
@ -72,6 +72,10 @@ impl Command for SubCommand {
|
|||||||
vec!["match", "find", "search"]
|
vec!["match", "find", "search"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -92,6 +96,31 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let substring: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
substring: substring.item,
|
||||||
|
range: call.get_flag_const(working_set, "range")?,
|
||||||
|
end: call.has_flag_const(working_set, "end")?,
|
||||||
|
cell_paths,
|
||||||
|
graphemes: grapheme_flags_const(working_set, call)?,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -32,6 +33,10 @@ impl Command for StrJoin {
|
|||||||
vec!["collect", "concatenate"]
|
vec!["collect", "concatenate"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -40,7 +45,41 @@ impl Command for StrJoin {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let separator: Option<String> = call.opt(engine_state, stack, 0)?;
|
let separator: Option<String> = call.opt(engine_state, stack, 0)?;
|
||||||
|
run(engine_state, call, input, separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let separator: Option<String> = call.opt_const(working_set, 0)?;
|
||||||
|
run(working_set.permanent(), call, input, separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Create a string from input",
|
||||||
|
example: "['nu', 'shell'] | str join",
|
||||||
|
result: Some(Value::test_string("nushell")),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Create a string from input with a separator",
|
||||||
|
example: "['nu', 'shell'] | str join '-'",
|
||||||
|
result: Some(Value::test_string("nu-shell")),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
separator: Option<String>,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
let config = engine_state.config.clone();
|
let config = engine_state.config.clone();
|
||||||
|
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
@ -77,22 +116,6 @@ impl Command for StrJoin {
|
|||||||
Ok(PipelineData::ByteStream(output, metadata))
|
Ok(PipelineData::ByteStream(output, metadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Create a string from input",
|
|
||||||
example: "['nu', 'shell'] | str join",
|
|
||||||
result: Some(Value::test_string("nushell")),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Create a string from input with a separator",
|
|
||||||
example: "['nu', 'shell'] | str join '-'",
|
|
||||||
result: Some(Value::test_string("nu-shell")),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{grapheme_flags, grapheme_flags_const};
|
use crate::{grapheme_flags, grapheme_flags_const};
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
|
@ -73,6 +73,10 @@ impl Command for SubCommand {
|
|||||||
vec!["search", "shift", "switch", "regex"]
|
vec!["search", "shift", "switch", "regex"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -101,6 +105,39 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let find: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let replace: Spanned<String> = call.req_const(working_set, 1)?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 2)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let literal_replace = call.has_flag_const(working_set, "no-expand")?;
|
||||||
|
let no_regex = !call.has_flag_const(working_set, "regex")?
|
||||||
|
&& !call.has_flag_const(working_set, "multiline")?;
|
||||||
|
let multiline = call.has_flag_const(working_set, "multiline")?;
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
all: call.has_flag_const(working_set, "all")?,
|
||||||
|
find,
|
||||||
|
replace,
|
||||||
|
cell_paths,
|
||||||
|
literal_replace,
|
||||||
|
no_regex,
|
||||||
|
multiline,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -37,6 +37,10 @@ impl Command for SubCommand {
|
|||||||
vec!["convert", "inverse", "flip"]
|
vec!["convert", "inverse", "flip"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -49,6 +53,23 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
let args = CellPathOnlyArgs::from(cell_paths);
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -51,6 +51,10 @@ impl Command for SubCommand {
|
|||||||
vec!["prefix", "match", "find", "search"]
|
vec!["prefix", "match", "find", "search"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -69,6 +73,29 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let substring: Spanned<String> = call.req_const(working_set, 0)?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
substring: substring.item,
|
||||||
|
cell_paths,
|
||||||
|
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::{fmt, str};
|
use std::{fmt, str};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
@ -29,6 +30,10 @@ impl Command for SubCommand {
|
|||||||
vec!["count", "word", "character", "unicode", "wc"]
|
vec!["count", "word", "character", "unicode", "wc"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -39,6 +44,15 @@ impl Command for SubCommand {
|
|||||||
stats(engine_state, call, input)
|
stats(engine_state, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
stats(working_set.permanent(), call, input)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grapheme_flags;
|
use crate::{grapheme_flags, grapheme_flags_const};
|
||||||
use nu_cmd_base::{
|
use nu_cmd_base::{
|
||||||
input_handler::{operate, CmdArgument},
|
input_handler::{operate, CmdArgument},
|
||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Range;
|
use nu_protocol::{engine::StateWorkingSet, Range};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
@ -77,6 +77,10 @@ impl Command for SubCommand {
|
|||||||
vec!["slice"]
|
vec!["slice"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -103,6 +107,37 @@ impl Command for SubCommand {
|
|||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let range: Range = call.req_const(working_set, 0)?;
|
||||||
|
|
||||||
|
let indexes = match util::process_range(&range) {
|
||||||
|
Ok(idxs) => idxs.into(),
|
||||||
|
Err(processing_error) => {
|
||||||
|
return Err(processing_error("could not perform substring", call.head))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let args = Arguments {
|
||||||
|
indexes,
|
||||||
|
cell_paths,
|
||||||
|
graphemes: grapheme_flags_const(working_set, call)?,
|
||||||
|
};
|
||||||
|
operate(
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
working_set.permanent().ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -71,6 +71,10 @@ impl Command for SubCommand {
|
|||||||
vec!["whitespace", "strip", "lstrip", "rstrip"]
|
vec!["whitespace", "strip", "lstrip", "rstrip"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -79,44 +83,37 @@ impl Command for SubCommand {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let character = call.get_flag::<Spanned<String>>(engine_state, stack, "char")?;
|
let character = call.get_flag::<Spanned<String>>(engine_state, stack, "char")?;
|
||||||
let to_trim = match character.as_ref() {
|
|
||||||
Some(v) => {
|
|
||||||
if v.item.chars().count() > 1 {
|
|
||||||
return Err(ShellError::GenericError {
|
|
||||||
error: "Trim only works with single character".into(),
|
|
||||||
msg: "needs single character".into(),
|
|
||||||
span: Some(v.span),
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
v.item.chars().next()
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
|
||||||
let mode = match cell_paths {
|
|
||||||
None => ActionMode::Global,
|
|
||||||
Some(_) => ActionMode::Local,
|
|
||||||
};
|
|
||||||
|
|
||||||
let left = call.has_flag(engine_state, stack, "left")?;
|
let left = call.has_flag(engine_state, stack, "left")?;
|
||||||
let right = call.has_flag(engine_state, stack, "right")?;
|
let right = call.has_flag(engine_state, stack, "right")?;
|
||||||
let trim_side = match (left, right) {
|
run(
|
||||||
(true, true) => TrimSide::Both,
|
character,
|
||||||
(true, false) => TrimSide::Left,
|
|
||||||
(false, true) => TrimSide::Right,
|
|
||||||
(false, false) => TrimSide::Both,
|
|
||||||
};
|
|
||||||
|
|
||||||
let args = Arguments {
|
|
||||||
to_trim,
|
|
||||||
trim_side,
|
|
||||||
cell_paths,
|
cell_paths,
|
||||||
mode,
|
(left, right),
|
||||||
};
|
call,
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
input,
|
||||||
|
engine_state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let character = call.get_flag_const::<Spanned<String>>(working_set, "char")?;
|
||||||
|
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
|
||||||
|
let left = call.has_flag_const(working_set, "left")?;
|
||||||
|
let right = call.has_flag_const(working_set, "right")?;
|
||||||
|
run(
|
||||||
|
character,
|
||||||
|
cell_paths,
|
||||||
|
(left, right),
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
working_set.permanent(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -150,6 +147,52 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
character: Option<Spanned<String>>,
|
||||||
|
cell_paths: Vec<CellPath>,
|
||||||
|
(left, right): (bool, bool),
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let to_trim = match character.as_ref() {
|
||||||
|
Some(v) => {
|
||||||
|
if v.item.chars().count() > 1 {
|
||||||
|
return Err(ShellError::GenericError {
|
||||||
|
error: "Trim only works with single character".into(),
|
||||||
|
msg: "needs single character".into(),
|
||||||
|
span: Some(v.span),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
v.item.chars().next()
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let mode = match cell_paths {
|
||||||
|
None => ActionMode::Global,
|
||||||
|
Some(_) => ActionMode::Local,
|
||||||
|
};
|
||||||
|
|
||||||
|
let trim_side = match (left, right) {
|
||||||
|
(true, true) => TrimSide::Both,
|
||||||
|
(true, false) => TrimSide::Left,
|
||||||
|
(false, true) => TrimSide::Right,
|
||||||
|
(false, false) => TrimSide::Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = Arguments {
|
||||||
|
to_trim,
|
||||||
|
trim_side,
|
||||||
|
cell_paths,
|
||||||
|
mode,
|
||||||
|
};
|
||||||
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum ActionMode {
|
pub enum ActionMode {
|
||||||
Local,
|
Local,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pub use crate::CallExt;
|
pub use crate::CallExt;
|
||||||
pub use nu_protocol::{
|
pub use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
record, ByteStream, ByteStreamType, Category, ErrSpan, Example, IntoInterruptiblePipelineData,
|
record, ByteStream, ByteStreamType, Category, ErrSpan, Example, IntoInterruptiblePipelineData,
|
||||||
IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
||||||
SyntaxShape, Type, Value,
|
SyntaxShape, Type, Value,
|
||||||
|
Loading…
Reference in New Issue
Block a user