From 366348dea0951760b5b4cff879393358ab2b2d14 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:29:00 -0600 Subject: [PATCH] make `char` command `const` (#11771) # Description This PR makes the `char` command a `const` command. The only real changes are to get the arguments different and I extracted code into functions so they could be called via run and run_cost. No algorithms were harmed in the making of this PR. # User-Facing Changes # Tests + Formatting # After Submitting --- crates/nu-command/src/strings/char_.rs | 230 +++++++++++++++++-------- 1 file changed, 155 insertions(+), 75 deletions(-) diff --git a/crates/nu-command/src/strings/char_.rs b/crates/nu-command/src/strings/char_.rs index ed52abb0da..7ab236b629 100644 --- a/crates/nu-command/src/strings/char_.rs +++ b/crates/nu-command/src/strings/char_.rs @@ -1,13 +1,14 @@ use indexmap::indexmap; use indexmap::map::IndexMap; use nu_engine::CallExt; -use nu_protocol::engine::{EngineState, Stack}; +use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::record; use nu_protocol::{ ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use once_cell::sync::Lazy; +use std::sync::{atomic::AtomicBool, Arc}; // Character used to separate directories in a Path Environment variable on windows is ";" #[cfg(target_family = "windows")] @@ -172,6 +173,10 @@ impl Command for Char { .category(Category::Strings) } + fn is_const(&self) -> bool { + true + } + fn usage(&self) -> &str { "Output special characters (e.g., 'newline')." } @@ -217,6 +222,40 @@ impl Command for Char { ] } + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + _input: PipelineData, + ) -> Result { + let call_span = call.head; + let list = call.has_flag_const(working_set, "list")?; + let integer = call.has_flag_const(working_set, "integer")?; + let unicode = call.has_flag_const(working_set, "unicode")?; + let ctrlc = working_set.permanent().ctrlc.clone(); + + // handle -l flag + if list { + return generate_character_list(ctrlc, call.head); + } + + // handle -i flag + if integer { + let int_args: Vec = call.rest_const(working_set, 0)?; + handle_integer_flag(int_args, call, call_span) + } + // handle -u flag + else if unicode { + let string_args: Vec = call.rest_const(working_set, 0)?; + handle_unicode_flag(string_args, call, call_span) + } + // handle the rest + else { + let string_args: Vec = call.rest_const(working_set, 0)?; + handle_the_rest(string_args, call, call_span) + } + } + fn run( &self, engine_state: &EngineState, @@ -225,84 +264,125 @@ impl Command for Char { _input: PipelineData, ) -> Result { let call_span = call.head; - // handle -l flag - if call.has_flag(engine_state, stack, "list")? { - return Ok(CHAR_MAP - .iter() - .map(move |(name, s)| { - let unicode = Value::string( - s.chars() - .map(|c| format!("{:x}", c as u32)) - .collect::>() - .join(" "), - call_span, - ); - let record = record! { - "name" => Value::string(*name, call_span), - "character" => Value::string(s, call_span), - "unicode" => unicode, - }; + let list = call.has_flag(engine_state, stack, "list")?; + let integer = call.has_flag(engine_state, stack, "integer")?; + let unicode = call.has_flag(engine_state, stack, "unicode")?; + let ctrlc = engine_state.ctrlc.clone(); - Value::record(record, call_span) - }) - .into_pipeline_data(engine_state.ctrlc.clone())); + // handle -l flag + if list { + return generate_character_list(ctrlc, call_span); + } + + // handle -i flag + if integer { + let int_args: Vec = call.rest(engine_state, stack, 0)?; + handle_integer_flag(int_args, call, call_span) } // handle -u flag - if call.has_flag(engine_state, stack, "integer")? { - let args: Vec = call.rest(engine_state, stack, 0)?; - if args.is_empty() { - return Err(ShellError::MissingParameter { - param_name: "missing at least one unicode character".into(), - span: call_span, - }); - } - let mut multi_byte = String::new(); - for (i, &arg) in args.iter().enumerate() { - let span = call - .positional_nth(i) - .expect("Unexpected missing argument") - .span; - multi_byte.push(integer_to_unicode_char(arg, span)?) - } - Ok(Value::string(multi_byte, call_span).into_pipeline_data()) - } else if call.has_flag(engine_state, stack, "unicode")? { - let args: Vec = call.rest(engine_state, stack, 0)?; - if args.is_empty() { - return Err(ShellError::MissingParameter { - param_name: "missing at least one unicode character".into(), - span: call_span, - }); - } - let mut multi_byte = String::new(); - for (i, arg) in args.iter().enumerate() { - let span = call - .positional_nth(i) - .expect("Unexpected missing argument") - .span; - multi_byte.push(string_to_unicode_char(arg, span)?) - } - Ok(Value::string(multi_byte, call_span).into_pipeline_data()) - } else { - let args: Vec = call.rest(engine_state, stack, 0)?; - if args.is_empty() { - return Err(ShellError::MissingParameter { - param_name: "missing name of the character".into(), - span: call_span, - }); - } - let special_character = str_to_character(&args[0]); - if let Some(output) = special_character { - Ok(Value::string(output, call_span).into_pipeline_data()) - } else { - Err(ShellError::TypeMismatch { - err_message: "error finding named character".into(), - span: call - .positional_nth(0) - .expect("Unexpected missing argument") - .span, - }) - } + else if unicode { + let string_args: Vec = call.rest(engine_state, stack, 0)?; + handle_unicode_flag(string_args, call, call_span) } + // handle the rest + else { + let string_args: Vec = call.rest(engine_state, stack, 0)?; + handle_the_rest(string_args, call, call_span) + } + } +} + +fn generate_character_list( + ctrlc: Option>, + call_span: Span, +) -> Result { + Ok(CHAR_MAP + .iter() + .map(move |(name, s)| { + let unicode = Value::string( + s.chars() + .map(|c| format!("{:x}", c as u32)) + .collect::>() + .join(" "), + call_span, + ); + let record = record! { + "name" => Value::string(*name, call_span), + "character" => Value::string(s, call_span), + "unicode" => unicode, + }; + + Value::record(record, call_span) + }) + .into_pipeline_data(ctrlc)) +} + +fn handle_integer_flag( + int_args: Vec, + call: &Call, + call_span: Span, +) -> Result { + if int_args.is_empty() { + return Err(ShellError::MissingParameter { + param_name: "missing at least one unicode character".into(), + span: call_span, + }); + } + let mut multi_byte = String::new(); + for (i, &arg) in int_args.iter().enumerate() { + let span = call + .positional_nth(i) + .expect("Unexpected missing argument") + .span; + multi_byte.push(integer_to_unicode_char(arg, span)?) + } + Ok(Value::string(multi_byte, call_span).into_pipeline_data()) +} + +fn handle_unicode_flag( + string_args: Vec, + call: &Call, + call_span: Span, +) -> Result { + if string_args.is_empty() { + return Err(ShellError::MissingParameter { + param_name: "missing at least one unicode character".into(), + span: call_span, + }); + } + let mut multi_byte = String::new(); + for (i, arg) in string_args.iter().enumerate() { + let span = call + .positional_nth(i) + .expect("Unexpected missing argument") + .span; + multi_byte.push(string_to_unicode_char(arg, span)?) + } + Ok(Value::string(multi_byte, call_span).into_pipeline_data()) +} + +fn handle_the_rest( + string_args: Vec, + call: &Call, + call_span: Span, +) -> Result { + if string_args.is_empty() { + return Err(ShellError::MissingParameter { + param_name: "missing name of the character".into(), + span: call_span, + }); + } + let special_character = str_to_character(&string_args[0]); + if let Some(output) = special_character { + Ok(Value::string(output, call_span).into_pipeline_data()) + } else { + Err(ShellError::TypeMismatch { + err_message: "error finding named character".into(), + span: call + .positional_nth(0) + .expect("Unexpected missing argument") + .span, + }) } }