use crate::parser::SyntaxShape; #[derive(Debug, Clone)] pub struct Flag { pub long: String, pub short: Option, pub arg: Option, pub required: bool, pub desc: String, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct PositionalArg { pub name: String, pub desc: String, pub shape: SyntaxShape, } #[derive(Clone, Debug)] 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, } impl PartialEq for Signature { fn eq(&self, other: &Self) -> bool { self.name == other.name && self.usage == other.usage && self.required_positional == other.required_positional && self.optional_positional == other.optional_positional && self.rest_positional == other.rest_positional && self.is_filter == other.is_filter } } impl Eq for Signature {} impl Signature { pub fn new(name: impl Into) -> Signature { Signature { name: name.into(), usage: String::new(), extra_usage: String::new(), required_positional: vec![], optional_positional: vec![], rest_positional: None, named: vec![], is_filter: false, } } 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, shape: impl Into, desc: impl Into, ) -> Signature { self.required_positional.push(PositionalArg { name: name.into(), desc: desc.into(), shape: shape.into(), }); self } /// 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(), }); self } /// Add an optional named flag argument to the signature pub fn named( mut self, name: impl Into, shape: impl Into, desc: impl Into, short: Option, ) -> Signature { let s = short.map(|c| { debug_assert!(!self.get_shorts().contains(&c)); c }); self.named.push(Flag { long: name.into(), short: s, arg: Some(shape.into()), required: false, desc: desc.into(), }); self } /// Add a required named flag argument to the signature pub fn required_named( mut self, name: impl Into, shape: impl Into, desc: impl Into, short: Option, ) -> Signature { let s = short.map(|c| { debug_assert!(!self.get_shorts().contains(&c)); c }); self.named.push(Flag { long: name.into(), short: s, arg: Some(shape.into()), required: true, desc: desc.into(), }); self } /// Add a switch to the signature pub fn switch( mut self, name: impl Into, desc: impl Into, short: Option, ) -> Signature { let s = short.map(|c| { debug_assert!( !self.get_shorts().contains(&c), "There may be duplicate short flags, such as -h" ); c }); self.named.push(Flag { long: name.into(), short: s, arg: None, required: false, desc: desc.into(), }); self } /// Get list of the short-hand flags pub fn get_shorts(&self) -> Vec { let mut shorts = Vec::new(); for Flag { short, .. } in &self.named { if let Some(c) = short { shorts.push(*c); } } shorts } 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 { self.required_positional.len() + self.optional_positional.len() } /// 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 } }