use std::{any::Any, borrow::Cow, fmt::{self, Display}, mem, str::FromStr}; use anyhow::{Result, anyhow, bail}; use hashbrown::HashMap; use serde::{Deserialize, de}; use crate::{Layer, SStr, Source, data::{Data, DataKey}}; #[derive(Debug, Default)] pub struct Cmd { pub name: SStr, pub args: HashMap, pub layer: Layer, pub source: Source, } impl Cmd { pub fn new(name: N, source: Source, default: Option) -> Result where N: Into, { let cow: SStr = name.into(); let (layer, name) = match cow.find(':') { None => (default.ok_or_else(|| anyhow!("Cannot infer layer from command name: {cow}"))?, cow), Some(i) => (cow[..i].parse()?, match cow { Cow::Borrowed(s) => Cow::Borrowed(&s[i + 1..]), Cow::Owned(mut s) => { s.drain(..i + 1); Cow::Owned(s) } }), }; Ok(Self { name, args: Default::default(), layer, source }) } pub fn new_relay(name: N) -> Self where N: Into, { Self::new(name, Source::Relay, None).unwrap_or(Self::null()) } pub fn new_relay_args(name: N, args: I) -> Self where N: Into, D: Into, I: IntoIterator, { let mut cmd = Self::new(name, Source::Relay, None).unwrap_or(Self::null()); cmd.args = args.into_iter().enumerate().map(|(i, a)| (DataKey::Integer(i as i64), a.into())).collect(); cmd } fn null() -> Self { Self { name: Cow::Borrowed("null"), ..Default::default() } } pub fn len(&self) -> usize { self.args.len() } pub fn is_empty(&self) -> bool { self.args.is_empty() } // --- With pub fn with(mut self, name: impl Into, value: impl Into) -> Self { self.args.insert(name.into(), value.into()); self } pub fn with_opt(mut self, name: impl Into, value: Option>) -> Self { if let Some(v) = value { self.args.insert(name.into(), v.into()); } self } pub fn with_any(mut self, name: impl Into, data: impl Any + Send + Sync) -> Self { self.args.insert(name.into(), Data::Any(Box::new(data))); self } // --- Get pub fn get<'a, T>(&'a self, name: impl Into) -> Result where T: TryFrom<&'a Data>, T::Error: Into, { let name = name.into(); match self.args.get(&name) { Some(data) => data.try_into().map_err(Into::into), None => bail!("argument not found: {:?}", name), } } pub fn str(&self, name: impl Into) -> &str { self.get(name).unwrap_or_default() } pub fn bool(&self, name: impl Into) -> bool { self.get(name).unwrap_or(false) } pub fn first<'a, T>(&'a self) -> Result where T: TryFrom<&'a Data>, T::Error: Into, { self.get(0) } pub fn second<'a, T>(&'a self) -> Result where T: TryFrom<&'a Data>, T::Error: Into, { self.get(1) } pub fn seq<'a, T>(&'a self) -> Vec where T: TryFrom<&'a Data>, { let mut seq = Vec::with_capacity(self.len()); for i in 0..self.len() { if let Ok(data) = self.get::<&Data>(i) && let Ok(v) = data.try_into() { seq.push(v); } else { break; } } seq } // --- Take pub fn take(&mut self, name: impl Into) -> Result where T: TryFrom, T::Error: Into, { let name = name.into(); match self.args.remove(&name) { Some(data) => data.try_into().map_err(Into::into), None => bail!("argument not found: {:?}", name), } } pub fn take_first(&mut self) -> Result where T: TryFrom, T::Error: Into, { self.take(0) } pub fn take_seq(&mut self) -> Vec where T: TryFrom, { let mut seq = Vec::with_capacity(self.len()); for i in 0..self.len() { if let Ok(data) = self.take::(i) && let Ok(v) = data.try_into() { seq.push(v); } else { break; } } seq } pub fn take_any(&mut self, name: impl Into) -> Option { self.args.remove(&name.into())?.into_any() } pub fn take_any2(&mut self, name: impl Into) -> Option> { self.args.remove(&name.into()).map(Data::into_any2) } // Parse pub fn parse_args( words: impl Iterator, last: Option, obase: bool, ) -> Result> { let mut i = 0i64; words .into_iter() .map(|s| (s, true)) .chain(last.into_iter().map(|s| (s, false))) .map(|(word, normal)| { let Some(arg) = word.strip_prefix("--").filter(|_| normal) else { i += 1; return Ok((DataKey::Integer(i - obase as i64), Data::String(word.into()))); }; let mut parts = arg.splitn(2, '='); let Some(key) = parts.next().map(|s| s.to_owned()) else { bail!("invalid argument: {arg}"); }; let val = if let Some(val) = parts.next() { Data::String(val.to_owned().into()) } else { Data::Boolean(true) }; Ok((DataKey::String(Cow::Owned(key)), val)) }) .collect() } } impl Display for Cmd { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name)?; for (k, v) in &self.args { match k { DataKey::Integer(_) => { if let Some(s) = v.as_str() { write!(f, " {s}")?; } } DataKey::String(k) => { if v.try_into().is_ok_and(|b| b) { write!(f, " --{k}")?; } else if let Some(s) = v.as_str() { write!(f, " --{k}={s}")?; } } _ => {} } } Ok(()) } } impl FromStr for Cmd { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let (mut words, last) = crate::shell::split_unix(s, true)?; if words.is_empty() || words[0].is_empty() { bail!("command name cannot be empty"); } let mut me = Self::new(mem::take(&mut words[0]), Default::default(), Some(Default::default()))?; me.args = Self::parse_args(words.into_iter().skip(1), last, true)?; Ok(me) } } impl<'de> Deserialize<'de> for Cmd { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { <_>::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom) } }