mirror of
https://github.com/nushell/nushell.git
synced 2024-11-07 09:04:18 +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 nu_engine::command_prelude::*;
|
||||
use nu_protocol::engine::StateWorkingSet;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
|
@ -45,20 +45,6 @@ impl Command for DetectColumns {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
@ -109,33 +95,87 @@ none 8150224 4 8150220 1% /mnt/c' | detect columns --gue
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
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,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
use super::guess_width::GuessWidth;
|
||||
let input_span = input.span().unwrap_or(call.head);
|
||||
|
||||
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) = num_rows_to_skip {
|
||||
if let Some(rows) = args.num_rows_to_skip {
|
||||
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 noheader = call.has_flag(engine_state, stack, "no-headers")?;
|
||||
|
||||
let result = guess_width.read_all();
|
||||
|
||||
if result.is_empty() {
|
||||
return Ok(Value::nothing(input_span).into_pipeline_data());
|
||||
}
|
||||
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
|
||||
if !noheader {
|
||||
if !args.noheader {
|
||||
let columns = result[0].clone();
|
||||
Ok(result
|
||||
.into_iter()
|
||||
@ -152,7 +192,7 @@ fn guess_width(
|
||||
let record =
|
||||
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
||||
match record {
|
||||
Ok(r) => match &range {
|
||||
Ok(r) => match &args.range {
|
||||
Some(range) => merge_record(r, range, input_span),
|
||||
None => Value::record(r, input_span),
|
||||
},
|
||||
@ -177,7 +217,7 @@ fn guess_width(
|
||||
let record =
|
||||
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
|
||||
match record {
|
||||
Ok(r) => match &range {
|
||||
Ok(r) => match &args.range {
|
||||
Some(range) => merge_record(r, range, input_span),
|
||||
None => Value::record(r, input_span),
|
||||
},
|
||||
@ -190,21 +230,18 @@ fn guess_width(
|
||||
|
||||
fn detect_columns(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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 config = engine_state.get_config();
|
||||
let input = input.collect_string("", config)?;
|
||||
|
||||
let input: Vec<_> = input
|
||||
.lines()
|
||||
.skip(num_rows_to_skip.unwrap_or_default())
|
||||
.skip(args.num_rows_to_skip.unwrap_or_default())
|
||||
.map(|x| x.to_string())
|
||||
.collect();
|
||||
|
||||
@ -214,13 +251,14 @@ fn detect_columns(
|
||||
if let Some(orig_headers) = headers {
|
||||
let mut headers = find_columns(&orig_headers);
|
||||
|
||||
if noheader {
|
||||
if args.noheader {
|
||||
for header in headers.iter_mut().enumerate() {
|
||||
header.1.item = format!("column{}", header.0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(noheader
|
||||
Ok(args
|
||||
.noheader
|
||||
.then_some(orig_headers)
|
||||
.into_iter()
|
||||
.chain(input)
|
||||
@ -273,7 +311,7 @@ fn detect_columns(
|
||||
}
|
||||
}
|
||||
|
||||
match &range {
|
||||
match &args.range {
|
||||
Some(range) => merge_record(record, range, name_span),
|
||||
None => Value::record(record, name_span),
|
||||
}
|
||||
|
@ -7,10 +7,9 @@ use base64::{
|
||||
Engine,
|
||||
};
|
||||
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{EngineState, Stack},
|
||||
engine::EngineState,
|
||||
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(
|
||||
action_type: ActionType,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
cell_paths: Vec<CellPath>,
|
||||
args: Base64CommandArguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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);
|
||||
|
||||
// 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,
|
||||
None => Spanned {
|
||||
item: "standard".to_string(),
|
||||
@ -68,9 +69,9 @@ pub fn operate(
|
||||
let args = Arguments {
|
||||
encoding_config: Base64Config {
|
||||
character_set,
|
||||
action_type,
|
||||
action_type: args.action_type,
|
||||
},
|
||||
binary,
|
||||
binary: args.binary,
|
||||
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(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -53,49 +57,67 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let encoding: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
||||
run(call, input, encoding)
|
||||
}
|
||||
|
||||
match input {
|
||||
PipelineData::ByteStream(stream, ..) => {
|
||||
let span = stream.span();
|
||||
let bytes = stream.into_bytes()?;
|
||||
match 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 {
|
||||
PipelineData::ByteStream(stream, ..) => {
|
||||
let span = stream.span();
|
||||
let bytes = stream.into_bytes()?;
|
||||
match encoding {
|
||||
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
|
||||
None => super::encoding::detect_encoding_name(head, span, &bytes)
|
||||
.map(|encoding| encoding.decode(&bytes).0.into_owned())
|
||||
.map(|s| Value::string(s, head)),
|
||||
}
|
||||
.map(|val| val.into_pipeline_data())
|
||||
}
|
||||
PipelineData::Value(v, ..) => {
|
||||
let input_span = v.span();
|
||||
match v {
|
||||
Value::Binary { val: bytes, .. } => match encoding {
|
||||
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
|
||||
None => super::encoding::detect_encoding_name(head, span, &bytes)
|
||||
None => super::encoding::detect_encoding_name(head, input_span, &bytes)
|
||||
.map(|encoding| encoding.decode(&bytes).0.into_owned())
|
||||
.map(|s| Value::string(s, head)),
|
||||
}
|
||||
.map(|val| val.into_pipeline_data())
|
||||
.map(|val| val.into_pipeline_data()),
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
_ => Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: v.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: v.span(),
|
||||
}),
|
||||
}
|
||||
PipelineData::Value(v, ..) => {
|
||||
let input_span = v.span();
|
||||
match v {
|
||||
Value::Binary { val: bytes, .. } => match encoding {
|
||||
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
|
||||
None => super::encoding::detect_encoding_name(head, input_span, &bytes)
|
||||
.map(|encoding| encoding.decode(&bytes).0.into_owned())
|
||||
.map(|s| Value::string(s, head)),
|
||||
}
|
||||
.map(|val| val.into_pipeline_data()),
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
_ => Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: v.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: v.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
// This should be more precise, but due to difficulties in getting spans
|
||||
// from PipelineData::ListData, this is as it is.
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "non-binary input".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: input.span().unwrap_or(head),
|
||||
}),
|
||||
}
|
||||
// This should be more precise, but due to difficulties in getting spans
|
||||
// from PipelineData::ListData, this is as it is.
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "non-binary input".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: input.span().unwrap_or(head),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -66,6 +66,10 @@ impl Command for DecodeBase64 {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -73,7 +77,34 @@ impl Command for DecodeBase64 {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -76,42 +80,62 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
||||
run(call, input, encoding, ignore_errors)
|
||||
}
|
||||
|
||||
match input {
|
||||
PipelineData::ByteStream(stream, ..) => {
|
||||
let span = stream.span();
|
||||
let s = stream.into_string()?;
|
||||
super::encoding::encode(head, encoding, &s, span, ignore_errors)
|
||||
.map(|val| val.into_pipeline_data())
|
||||
}
|
||||
PipelineData::Value(v, ..) => {
|
||||
let span = v.span();
|
||||
match v {
|
||||
Value::String { val: s, .. } => {
|
||||
super::encoding::encode(head, encoding, &s, span, ignore_errors)
|
||||
.map(|val| val.into_pipeline_data())
|
||||
}
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
_ => Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string".into(),
|
||||
wrong_type: v.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: v.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
// This should be more precise, but due to difficulties in getting spans
|
||||
// from PipelineData::ListStream, this is as it is.
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "non-string input".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: input.span().unwrap_or(head),
|
||||
}),
|
||||
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 {
|
||||
PipelineData::ByteStream(stream, ..) => {
|
||||
let span = stream.span();
|
||||
let s = stream.into_string()?;
|
||||
super::encoding::encode(head, encoding, &s, span, ignore_errors)
|
||||
.map(|val| val.into_pipeline_data())
|
||||
}
|
||||
PipelineData::Value(v, ..) => {
|
||||
let span = v.span();
|
||||
match v {
|
||||
Value::String { val: s, .. } => {
|
||||
super::encoding::encode(head, encoding, &s, span, ignore_errors)
|
||||
.map(|val| val.into_pipeline_data())
|
||||
}
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
_ => Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string".into(),
|
||||
wrong_type: v.get_type().to_string(),
|
||||
dst_span: head,
|
||||
src_span: v.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
// This should be more precise, but due to difficulties in getting spans
|
||||
// from PipelineData::ListStream, this is as it is.
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "non-string input".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: input.span().unwrap_or(head),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -70,6 +70,10 @@ impl Command for EncodeBase64 {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -77,7 +81,34 @@ impl Command for EncodeBase64 {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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"]
|
||||
}
|
||||
|
||||
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> {
|
||||
vec![
|
||||
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
|
||||
|
@ -53,6 +53,10 @@ impl Command for FormatDuration {
|
||||
vec!["convert", "display", "pattern", "human readable"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::format_filesize;
|
||||
use nu_protocol::{engine::StateWorkingSet, format_filesize};
|
||||
|
||||
struct Arguments {
|
||||
format_value: String,
|
||||
@ -50,6 +50,10 @@ impl Command for FormatFilesize {
|
||||
vec!["convert", "display", "pattern", "human readable"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use fancy_regex::{Captures, Regex};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ListStream;
|
||||
use nu_protocol::{engine::StateWorkingSet, ListStream};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
@ -99,6 +99,10 @@ impl Command for Parse {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -106,19 +110,31 @@ impl Command for Parse {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
pattern: Spanned<String>,
|
||||
regex: bool,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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_span = pattern.span;
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::grapheme_flags;
|
||||
use crate::{grapheme_flags, grapheme_flags_const};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -88,6 +89,10 @@ impl Command for SubCommand {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -95,19 +100,28 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
graphemes: bool,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
|
||||
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
||||
input.map(
|
||||
move |x| split_chars_helper(&x, span, graphemes),
|
||||
engine_state.ctrlc.clone(),
|
||||
|
@ -43,16 +43,6 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
@ -103,35 +93,83 @@ 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 separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
|
||||
let has_regex = call.has_flag(engine_state, stack, "regex")?;
|
||||
|
||||
let args = Arguments {
|
||||
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,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let name_span = call.head;
|
||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
|
||||
|
||||
let regex = if call.has_flag(engine_state, stack, "regex")? {
|
||||
Regex::new(&separator.item)
|
||||
let regex = if args.has_regex {
|
||||
Regex::new(&args.separator.item)
|
||||
} else {
|
||||
let escaped = regex::escape(&separator.item);
|
||||
let escaped = regex::escape(&args.separator.item);
|
||||
Regex::new(&escaped)
|
||||
}
|
||||
.map_err(|e| ShellError::GenericError {
|
||||
error: "Error with regular expression".into(),
|
||||
msg: e.to_string(),
|
||||
span: Some(separator.span),
|
||||
span: Some(args.separator.span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
})?;
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
@ -36,16 +36,6 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
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 {
|
||||
@ -188,15 +205,15 @@ impl Matcher {
|
||||
|
||||
fn split_list(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
has_regex: bool,
|
||||
separator: Value,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let separator: Value = call.req(engine_state, stack, 0)?;
|
||||
let mut temp_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 {
|
||||
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
||||
break;
|
||||
|
@ -43,16 +43,6 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
@ -109,32 +99,77 @@ 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 separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
||||
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,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let name_span = call.head;
|
||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let regex = if call.has_flag(engine_state, stack, "regex")? {
|
||||
Regex::new(&separator.item)
|
||||
let regex = if args.has_regex {
|
||||
Regex::new(&args.separator.item)
|
||||
} else {
|
||||
let escaped = regex::escape(&separator.item);
|
||||
let escaped = regex::escape(&args.separator.item);
|
||||
Regex::new(&escaped)
|
||||
}
|
||||
.map_err(|e| ShellError::GenericError {
|
||||
error: "Error with regular expression".into(),
|
||||
msg: e.to_string(),
|
||||
span: Some(separator.span),
|
||||
span: Some(args.separator.span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
})?;
|
||||
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::grapheme_flags;
|
||||
use crate::{grapheme_flags, grapheme_flags_const};
|
||||
use fancy_regex::Regex;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
@ -96,6 +96,10 @@ impl Command for SubCommand {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -103,40 +107,76 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
// let ignore_hyphenated = call.has_flag(engine_state, stack, "ignore-hyphenated")?;
|
||||
// let ignore_apostrophes = call.has_flag(engine_state, stack, "ignore-apostrophes")?;
|
||||
// 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 call.has_flag(engine_state, stack, "grapheme-clusters")? {
|
||||
if args.word_length.is_none() {
|
||||
if args.has_grapheme {
|
||||
return Err(ShellError::IncompatibleParametersSingle {
|
||||
msg: "--grapheme-clusters (-g) requires --min-word-length (-l)".to_string(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
if call.has_flag(engine_state, stack, "utf-8-bytes")? {
|
||||
if args.has_utf8 {
|
||||
return Err(ShellError::IncompatibleParametersSingle {
|
||||
msg: "--utf-8-bytes (-b) requires --min-word-length (-l)".to_string(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
let graphemes = grapheme_flags(engine_state, stack, call)?;
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
||||
vec!["convert", "style", "caps", "upper"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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> {
|
||||
@ -72,12 +87,11 @@ impl Command for SubCommand {
|
||||
|
||||
fn operate(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
column_paths: Vec<CellPath>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
||||
vec!["lower case", "lowercase"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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> {
|
||||
@ -80,12 +95,11 @@ impl Command for SubCommand {
|
||||
|
||||
fn operate(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
column_paths: Vec<CellPath>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
|
@ -36,6 +36,10 @@ impl Command for SubCommand {
|
||||
vec!["uppercase", "upper case"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -43,7 +47,18 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> 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> {
|
||||
@ -57,12 +72,11 @@ impl Command for SubCommand {
|
||||
|
||||
fn operate(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
column_paths: Vec<CellPath>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
|
@ -52,6 +52,10 @@ impl Command for SubCommand {
|
||||
vec!["substring", "match", "find", "search"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -84,6 +88,43 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::levenshtein_distance;
|
||||
use nu_protocol::{engine::StateWorkingSet, levenshtein_distance};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -49,6 +49,10 @@ impl Command for SubCommand {
|
||||
vec!["edit", "levenshtein"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -66,6 +70,28 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![Example {
|
||||
description: "get the edit distance between two strings",
|
||||
|
@ -50,6 +50,10 @@ impl Command for SubCommand {
|
||||
vec!["suffix", "match", "find", "search"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -67,6 +71,28 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -179,6 +179,10 @@ impl Command for SubCommand {
|
||||
]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -186,32 +190,51 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
if matches!(input, PipelineData::Empty) {
|
||||
return Err(ShellError::PipelineEmpty { dst_span: span });
|
||||
}
|
||||
let is_path = call.has_flag(engine_state, stack, "path")?;
|
||||
input.map(
|
||||
move |v| {
|
||||
let value_span = v.span();
|
||||
match v.coerce_into_string() {
|
||||
Ok(s) => {
|
||||
let contents = if is_path { s.replace('\\', "\\\\") } else { s };
|
||||
str_expand(&contents, span, value_span)
|
||||
}
|
||||
Err(_) => Value::error(
|
||||
ShellError::PipelineMismatch {
|
||||
exp_input_type: "string".into(),
|
||||
dst_span: span,
|
||||
src_span: value_span,
|
||||
},
|
||||
span,
|
||||
),
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
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> {
|
||||
let span = call.head;
|
||||
if matches!(input, PipelineData::Empty) {
|
||||
return Err(ShellError::PipelineEmpty { dst_span: span });
|
||||
}
|
||||
input.map(
|
||||
move |v| {
|
||||
let value_span = v.span();
|
||||
match v.coerce_into_string() {
|
||||
Ok(s) => {
|
||||
let contents = if is_path { s.replace('\\', "\\\\") } else { s };
|
||||
str_expand(&contents, span, value_span)
|
||||
}
|
||||
Err(_) => Value::error(
|
||||
ShellError::PipelineMismatch {
|
||||
exp_input_type: "string".into(),
|
||||
dst_span: span,
|
||||
src_span: value_span,
|
||||
},
|
||||
span,
|
||||
),
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn str_expand(contents: &str, span: Span, value_span: Span) -> Value {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::grapheme_flags;
|
||||
use crate::{grapheme_flags, grapheme_flags_const};
|
||||
use nu_cmd_base::{
|
||||
input_handler::{operate, CmdArgument},
|
||||
util,
|
||||
};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::Range;
|
||||
use nu_protocol::{engine::StateWorkingSet, Range};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
struct Arguments {
|
||||
@ -72,6 +72,10 @@ impl Command for SubCommand {
|
||||
vec!["match", "find", "search"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -92,6 +96,31 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -32,6 +33,10 @@ impl Command for StrJoin {
|
||||
vec!["collect", "concatenate"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -40,41 +45,17 @@ impl Command for StrJoin {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let separator: Option<String> = call.opt(engine_state, stack, 0)?;
|
||||
run(engine_state, call, input, separator)
|
||||
}
|
||||
|
||||
let config = engine_state.config.clone();
|
||||
|
||||
let span = call.head;
|
||||
|
||||
let metadata = input.metadata();
|
||||
let mut iter = input.into_iter();
|
||||
let mut first = true;
|
||||
|
||||
let output = ByteStream::from_fn(span, None, ByteStreamType::String, move |buffer| {
|
||||
// Write each input to the buffer
|
||||
if let Some(value) = iter.next() {
|
||||
// Write the separator if this is not the first
|
||||
if first {
|
||||
first = false;
|
||||
} else if let Some(separator) = &separator {
|
||||
write!(buffer, "{}", separator)?;
|
||||
}
|
||||
|
||||
match value {
|
||||
Value::Error { error, .. } => {
|
||||
return Err(*error);
|
||||
}
|
||||
// Hmm, not sure what we actually want.
|
||||
// `to_expanded_string` formats dates as human readable which feels funny.
|
||||
Value::Date { val, .. } => write!(buffer, "{val:?}")?,
|
||||
value => write!(buffer, "{}", value.to_expanded_string("\n", &config))?,
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(PipelineData::ByteStream(output, metadata))
|
||||
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> {
|
||||
@ -93,6 +74,48 @@ impl Command for StrJoin {
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
separator: Option<String>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let config = engine_state.config.clone();
|
||||
|
||||
let span = call.head;
|
||||
|
||||
let metadata = input.metadata();
|
||||
let mut iter = input.into_iter();
|
||||
let mut first = true;
|
||||
|
||||
let output = ByteStream::from_fn(span, None, ByteStreamType::String, move |buffer| {
|
||||
// Write each input to the buffer
|
||||
if let Some(value) = iter.next() {
|
||||
// Write the separator if this is not the first
|
||||
if first {
|
||||
first = false;
|
||||
} else if let Some(separator) = &separator {
|
||||
write!(buffer, "{}", separator)?;
|
||||
}
|
||||
|
||||
match value {
|
||||
Value::Error { error, .. } => {
|
||||
return Err(*error);
|
||||
}
|
||||
// Hmm, not sure what we actually want.
|
||||
// `to_expanded_string` formats dates as human readable which feels funny.
|
||||
Value::Date { val, .. } => write!(buffer, "{val:?}")?,
|
||||
value => write!(buffer, "{}", value.to_expanded_string("\n", &config))?,
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(PipelineData::ByteStream(output, metadata))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{grapheme_flags, grapheme_flags_const};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::engine::StateWorkingSet;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
struct Arguments {
|
||||
|
@ -73,6 +73,10 @@ impl Command for SubCommand {
|
||||
vec!["search", "shift", "switch", "regex"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -101,6 +105,39 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -37,6 +37,10 @@ impl Command for SubCommand {
|
||||
vec!["convert", "inverse", "flip"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -49,6 +53,23 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -51,6 +51,10 @@ impl Command for SubCommand {
|
||||
vec!["prefix", "match", "find", "search"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -69,6 +73,29 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use fancy_regex::Regex;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::{fmt, str};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
@ -29,6 +30,10 @@ impl Command for SubCommand {
|
||||
vec!["count", "word", "character", "unicode", "wc"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -39,6 +44,15 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::grapheme_flags;
|
||||
use crate::{grapheme_flags, grapheme_flags_const};
|
||||
use nu_cmd_base::{
|
||||
input_handler::{operate, CmdArgument},
|
||||
util,
|
||||
};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::Range;
|
||||
use nu_protocol::{engine::StateWorkingSet, Range};
|
||||
use std::cmp::Ordering;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
@ -77,6 +77,10 @@ impl Command for SubCommand {
|
||||
vec!["slice"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -103,6 +107,37 @@ impl Command for SubCommand {
|
||||
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> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -71,6 +71,10 @@ impl Command for SubCommand {
|
||||
vec!["whitespace", "strip", "lstrip", "rstrip"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -79,44 +83,37 @@ impl Command for SubCommand {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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 = (!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 right = call.has_flag(engine_state, stack, "right")?;
|
||||
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,
|
||||
run(
|
||||
character,
|
||||
cell_paths,
|
||||
mode,
|
||||
};
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
(left, right),
|
||||
call,
|
||||
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> {
|
||||
@ -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)]
|
||||
pub enum ActionMode {
|
||||
Local,
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub use crate::CallExt;
|
||||
pub use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||
record, ByteStream, ByteStreamType, Category, ErrSpan, Example, IntoInterruptiblePipelineData,
|
||||
IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Value,
|
||||
|
Loading…
Reference in New Issue
Block a user