<<<<<<< HEAD use crate::syntax_shape::SyntaxShape; use crate::type_shape::Type; use indexmap::IndexMap; use nu_source::{DbgDocBldr, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource}; use serde::{Deserialize, Serialize}; /// The types of named parameter that a command can have #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum NamedType { /// A flag without any associated argument. eg) `foo --bar, foo -b` Switch(Option), /// A mandatory flag, with associated argument. eg) `foo --required xyz, foo -r xyz` Mandatory(Option, SyntaxShape), /// An optional flag, with associated argument. eg) `foo --optional abc, foo -o abc` Optional(Option, SyntaxShape), } impl NamedType { pub fn get_short(&self) -> Option { match self { NamedType::Switch(s) => *s, NamedType::Mandatory(s, _) => *s, NamedType::Optional(s, _) => *s, } } pub fn get_type_description(&self) -> (String, String, String) { let empty_string = ("".to_string(), "".to_string(), "".to_string()); match self { NamedType::Switch(f) => match f { Some(flag) => ("switch_flag".to_string(), flag.to_string(), "".to_string()), None => empty_string, }, NamedType::Mandatory(f, shape) => match f { Some(flag) => ( "mandatory_flag".to_string(), flag.to_string(), shape.syntax_shape_name().to_string(), ), None => empty_string, }, NamedType::Optional(f, shape) => match f { Some(flag) => ( "optional_flag".to_string(), flag.to_string(), shape.syntax_shape_name().to_string(), ), None => empty_string, }, } } } /// The type of positional arguments #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum PositionalType { /// A mandatory positional argument with the expected shape of the value Mandatory(String, SyntaxShape), /// An optional positional argument with the expected shape of the value Optional(String, SyntaxShape), } impl PrettyDebug for PositionalType { /// Prepare the PositionalType for pretty-printing fn pretty(&self) -> DebugDocBuilder { match self { PositionalType::Mandatory(string, shape) => { DbgDocBldr::description(string) + DbgDocBldr::delimit("(", shape.pretty(), ")") .into_kind() .group() } PositionalType::Optional(string, shape) => { DbgDocBldr::description(string) + DbgDocBldr::operator("?") + DbgDocBldr::delimit("(", shape.pretty(), ")") .into_kind() .group() } } } } impl PositionalType { /// Helper to create a mandatory positional argument type pub fn mandatory(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Mandatory(name.to_string(), ty) } /// Helper to create a mandatory positional argument with an "any" type pub fn mandatory_any(name: &str) -> PositionalType { PositionalType::Mandatory(name.to_string(), SyntaxShape::Any) } /// Helper to create a mandatory positional argument with a block type pub fn mandatory_block(name: &str) -> PositionalType { PositionalType::Mandatory(name.to_string(), SyntaxShape::Block) } /// Helper to create a optional positional argument type pub fn optional(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Optional(name.to_string(), ty) } /// Helper to create a optional positional argument with an "any" type pub fn optional_any(name: &str) -> PositionalType { PositionalType::Optional(name.to_string(), SyntaxShape::Any) } /// Gets the name of the positional argument pub fn name(&self) -> &str { match self { PositionalType::Mandatory(s, _) => s, PositionalType::Optional(s, _) => s, } } /// Gets the expected type of a positional argument pub fn syntax_type(&self) -> SyntaxShape { match *self { PositionalType::Mandatory(_, t) => t, PositionalType::Optional(_, t) => t, } } pub fn get_type_description(&self) -> (String, String) { match &self { PositionalType::Mandatory(c, s) => (c.to_string(), s.syntax_shape_name().to_string()), PositionalType::Optional(c, s) => (c.to_string(), s.syntax_shape_name().to_string()), } } } type Description = String; /// The full signature of a command. All commands have a signature similar to a function signature. /// Commands will use this information to register themselves with Nu's core engine so that the command /// can be invoked, help can be displayed, and calls to the command can be error-checked. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Signature { /// The name of the command. Used when calling the command pub name: String, /// Usage instructions about the command pub usage: String, /// Longer or more verbose usage statement pub extra_usage: String, /// The list of positional arguments, both required and optional, and their corresponding types and help text pub positional: Vec<(PositionalType, Description)>, /// After the positional arguments, a catch-all for the rest of the arguments that might follow, their type, and help text pub rest_positional: Option<(String, SyntaxShape, Description)>, /// The named flags with corresponding type and help text pub named: IndexMap, /// The type of values being sent out from the command into the pipeline, if any pub yields: Option, /// The type of values being read in from the pipeline into the command, if any pub input: Option, /// If the command is expected to filter data, or to consume it (as a sink) pub is_filter: bool, ======= use serde::Deserialize; use serde::Serialize; use crate::ast::Call; use crate::engine::Command; use crate::engine::EngineState; use crate::engine::Stack; use crate::BlockId; use crate::PipelineData; use crate::SyntaxShape; use crate::VarId; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Flag { pub long: String, pub short: Option, pub arg: Option, pub required: bool, pub desc: String, // For custom commands pub var_id: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PositionalArg { pub name: String, pub desc: String, pub shape: SyntaxShape, // For custom commands pub var_id: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Category { Default, Conversions, Core, Date, Env, Experimental, FileSystem, Filters, Formats, Math, Network, Random, Platform, Shells, Strings, System, Viewers, Hash, Generators, Custom(String), } impl std::fmt::Display for Category { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let msg = match self { Category::Default => "default", Category::Conversions => "conversions", Category::Core => "core", Category::Date => "date", Category::Env => "env", Category::Experimental => "experimental", Category::FileSystem => "filesystem", Category::Filters => "filters", Category::Formats => "formats", Category::Math => "math", Category::Network => "network", Category::Random => "random", Category::Platform => "platform", Category::Shells => "shells", Category::Strings => "strings", Category::System => "system", Category::Viewers => "viewers", Category::Hash => "hash", Category::Generators => "generators", Category::Custom(name) => name, }; write!(f, "{}", msg) } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Signature { pub name: String, pub usage: String, pub extra_usage: String, pub required_positional: Vec, pub optional_positional: Vec, pub rest_positional: Option, pub named: Vec, pub is_filter: bool, pub creates_scope: bool, // Signature category used to classify commands stored in the list of declarations pub category: Category, >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce } impl PartialEq for Signature { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.usage == other.usage <<<<<<< HEAD && self.positional == other.positional ======= && self.required_positional == other.required_positional && self.optional_positional == other.optional_positional >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce && self.rest_positional == other.rest_positional && self.is_filter == other.is_filter } } impl Eq for Signature {} impl Signature { <<<<<<< HEAD pub fn shift_positional(&mut self) { self.positional = Vec::from(&self.positional[1..]); } pub fn remove_named(&mut self, name: &str) { self.named.remove(name); } pub fn allowed(&self) -> Vec { let mut allowed = indexmap::IndexSet::new(); for (name, (t, _)) in &self.named { if let Some(c) = t.get_short() { allowed.insert(format!("-{}", c)); } allowed.insert(format!("--{}", name)); } for (ty, _) in &self.positional { let shape = ty.syntax_type(); allowed.insert(shape.display()); } if let Some((_, shape, _)) = &self.rest_positional { allowed.insert(shape.display()); } allowed.into_iter().collect() } } impl PrettyDebugWithSource for Signature { /// Prepare a Signature for pretty-printing fn pretty_debug(&self, source: &str) -> DebugDocBuilder { DbgDocBldr::typed( "signature", DbgDocBldr::description(&self.name) + DbgDocBldr::preceded( DbgDocBldr::space(), DbgDocBldr::intersperse( self.positional .iter() .map(|(ty, _)| ty.pretty_debug(source)), DbgDocBldr::space(), ), ), ) } } impl Signature { /// Create a new command signature with the given name pub fn new(name: impl Into) -> Signature { ======= pub fn new(name: impl Into) -> Signature { // default help flag let flag = Flag { long: "help".into(), short: Some('h'), arg: None, desc: "Display this help message".into(), required: false, var_id: None, }; >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce Signature { name: name.into(), usage: String::new(), extra_usage: String::new(), <<<<<<< HEAD positional: vec![], rest_positional: None, named: indexmap::indexmap! {"help".into() => (NamedType::Switch(Some('h')), "Display this help message".into())}, is_filter: false, yields: None, input: None, } } /// Create a new signature ======= required_positional: vec![], optional_positional: vec![], rest_positional: None, named: vec![flag], is_filter: false, creates_scope: false, category: Category::Default, } } >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce pub fn build(name: impl Into) -> Signature { Signature::new(name.into()) } /// Add a description to the signature pub fn desc(mut self, usage: impl Into) -> Signature { self.usage = usage.into(); self } /// Add a required positional argument to the signature pub fn required( mut self, name: impl Into, <<<<<<< HEAD ty: impl Into, desc: impl Into, ) -> Signature { self.positional.push(( PositionalType::Mandatory(name.into(), ty.into()), desc.into(), )); ======= shape: impl Into, desc: impl Into, ) -> Signature { self.required_positional.push(PositionalArg { name: name.into(), desc: desc.into(), shape: shape.into(), var_id: None, }); >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce self } <<<<<<< HEAD /// Add an optional positional argument to the signature pub fn optional( mut self, name: impl Into, ty: impl Into, desc: impl Into, ) -> Signature { self.positional.push(( PositionalType::Optional(name.into(), ty.into()), desc.into(), )); ======= /// Add a required positional argument to the signature pub fn optional( mut self, name: impl Into, shape: impl Into, desc: impl Into, ) -> Signature { self.optional_positional.push(PositionalArg { name: name.into(), desc: desc.into(), shape: shape.into(), var_id: None, }); self } pub fn rest( mut self, name: &str, shape: impl Into, desc: impl Into, ) -> Signature { self.rest_positional = Some(PositionalArg { name: name.into(), desc: desc.into(), shape: shape.into(), var_id: None, }); >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce self } /// Add an optional named flag argument to the signature pub fn named( mut self, name: impl Into, <<<<<<< HEAD ty: impl Into, desc: impl Into, short: Option, ) -> Signature { let s = short.map(|c| { debug_assert!(!self.get_shorts().contains(&c)); c }); self.named.insert( name.into(), (NamedType::Optional(s, ty.into()), desc.into()), ); ======= shape: impl Into, desc: impl Into, short: Option, ) -> Signature { let (name, s) = self.check_names(name, short); self.named.push(Flag { long: name, short: s, arg: Some(shape.into()), required: false, desc: desc.into(), var_id: None, }); >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce self } /// Add a required named flag argument to the signature pub fn required_named( mut self, name: impl Into, <<<<<<< HEAD ty: impl Into, desc: impl Into, short: Option, ) -> Signature { let s = short.map(|c| { debug_assert!(!self.get_shorts().contains(&c)); c }); self.named.insert( name.into(), (NamedType::Mandatory(s, ty.into()), desc.into()), ); ======= shape: impl Into, desc: impl Into, short: Option, ) -> Signature { let (name, s) = self.check_names(name, short); self.named.push(Flag { long: name, short: s, arg: Some(shape.into()), required: true, desc: desc.into(), var_id: None, }); >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce self } /// Add a switch to the signature pub fn switch( mut self, name: impl Into, desc: impl Into, short: Option, ) -> Signature { <<<<<<< HEAD ======= let (name, s) = self.check_names(name, short); self.named.push(Flag { long: name, short: s, arg: None, required: false, desc: desc.into(), var_id: None, }); self } /// Changes the signature category pub fn category(mut self, category: Category) -> Signature { self.category = category; self } /// Sets that signature will create a scope as it parses pub fn creates_scope(mut self) -> Signature { self.creates_scope = true; self } pub fn call_signature(&self) -> String { let mut one_liner = String::new(); one_liner.push_str(&self.name); one_liner.push(' '); // Note: the call signature needs flags first because on the nu commandline, // flags will precede the script file name. Flags for internal commands can come // either before or after (or around) positional parameters, so there isn't a strong // preference, so we default to the more constrained example. if self.named.len() > 1 { one_liner.push_str("{flags} "); } for positional in &self.required_positional { one_liner.push_str(&get_positional_short_name(positional, true)); } for positional in &self.optional_positional { one_liner.push_str(&get_positional_short_name(positional, false)); } if let Some(rest) = &self.rest_positional { one_liner.push_str(&format!("...{}", get_positional_short_name(rest, false))); } // if !self.subcommands.is_empty() { // one_liner.push_str(" "); // } one_liner } /// Get list of the short-hand flags pub fn get_shorts(&self) -> Vec { self.named.iter().filter_map(|f| f.short).collect() } /// Get list of the long-hand flags pub fn get_names(&self) -> Vec<&str> { self.named.iter().map(|f| f.long.as_str()).collect() } /// Checks if short or long are already present /// Panics if one of them is found fn check_names(&self, name: impl Into, short: Option) -> (String, Option) { >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce let s = short.map(|c| { debug_assert!( !self.get_shorts().contains(&c), "There may be duplicate short flags, such as -h" ); c }); <<<<<<< HEAD self.named .insert(name.into(), (NamedType::Switch(s), desc.into())); self ======= let name = { let name: String = name.into(); debug_assert!( !self.get_names().contains(&name.as_str()), "There may be duplicate name flags, such as --help" ); name }; (name, s) } pub fn get_positional(&self, position: usize) -> Option { if position < self.required_positional.len() { self.required_positional.get(position).cloned() } else if position < (self.required_positional.len() + self.optional_positional.len()) { self.optional_positional .get(position - self.required_positional.len()) .cloned() } else { self.rest_positional.clone() } } pub fn num_positionals(&self) -> usize { let mut total = self.required_positional.len() + self.optional_positional.len(); for positional in &self.required_positional { if let SyntaxShape::Keyword(..) = positional.shape { // Keywords have a required argument, so account for that total += 1; } } for positional in &self.optional_positional { if let SyntaxShape::Keyword(..) = positional.shape { // Keywords have a required argument, so account for that total += 1; } } total } pub fn num_positionals_after(&self, idx: usize) -> usize { let mut total = 0; for (curr, positional) in self.required_positional.iter().enumerate() { match positional.shape { SyntaxShape::Keyword(..) => { // Keywords have a required argument, so account for that if curr > idx { total += 2; } } _ => { if curr > idx { total += 1; } } } } total } /// Find the matching long flag pub fn get_long_flag(&self, name: &str) -> Option { for flag in &self.named { if flag.long == name { return Some(flag.clone()); } } None } /// Find the matching long flag pub fn get_short_flag(&self, short: char) -> Option { for flag in &self.named { if let Some(short_flag) = &flag.short { if *short_flag == short { return Some(flag.clone()); } } } None >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce } /// Set the filter flag for the signature pub fn filter(mut self) -> Signature { self.is_filter = true; self } <<<<<<< HEAD /// Set the type for the "rest" of the positional arguments /// Note: Not naming the field in your struct holding the rest values "rest", can /// cause errors when deserializing pub fn rest( mut self, name: impl Into, ty: SyntaxShape, desc: impl Into, ) -> Signature { self.rest_positional = Some((name.into(), ty, desc.into())); self } /// Add a type for the output of the command to the signature pub fn yields(mut self, ty: Type) -> Signature { self.yields = Some(ty); self } /// Add a type for the input of the command to the signature pub fn input(mut self, ty: Type) -> Signature { self.input = Some(ty); self } /// Get list of the short-hand flags pub fn get_shorts(&self) -> Vec { let mut shorts = Vec::new(); for (_, (t, _)) in &self.named { if let Some(c) = t.get_short() { shorts.push(c); } } shorts ======= /// Create a placeholder implementation of Command as a way to predeclare a definition's /// signature so other definitions can see it. This placeholder is later replaced with the /// full definition in a second pass of the parser. pub fn predeclare(self) -> Box { Box::new(Predeclaration { signature: self }) } /// Combines a signature and a block into a runnable block pub fn into_block_command(self, block_id: BlockId) -> Box { Box::new(BlockCommand { signature: self, block_id, }) } } #[derive(Clone)] struct Predeclaration { signature: Signature, } impl Command for Predeclaration { fn name(&self) -> &str { &self.signature.name } fn signature(&self) -> Signature { self.signature.clone() } fn usage(&self) -> &str { &self.signature.usage } fn run( &self, _engine_state: &EngineState, _stack: &mut Stack, _call: &Call, _input: PipelineData, ) -> Result { panic!("Internal error: can't run a predeclaration without a body") } } fn get_positional_short_name(arg: &PositionalArg, is_required: bool) -> String { match &arg.shape { SyntaxShape::Keyword(name, ..) => { if is_required { format!("{} <{}> ", String::from_utf8_lossy(name), arg.name) } else { format!("({} <{}>) ", String::from_utf8_lossy(name), arg.name) } } _ => { if is_required { format!("<{}> ", arg.name) } else { format!("({}) ", arg.name) } } } } #[derive(Clone)] struct BlockCommand { signature: Signature, block_id: BlockId, } impl Command for BlockCommand { fn name(&self) -> &str { &self.signature.name } fn signature(&self) -> Signature { self.signature.clone() } fn usage(&self) -> &str { &self.signature.usage } fn run( &self, _engine_state: &EngineState, _stack: &mut Stack, _call: &Call, _input: PipelineData, ) -> Result { panic!("Internal error: can't run custom command with 'run', use block_id"); } fn get_block_id(&self) -> Option { Some(self.block_id) >>>>>>> 9259a56a28f1dd3a4b720ad815aa19c6eaf6adce } }