use std::{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, DataAny, DataKey}, event::Replier}; #[derive(Clone, Debug, Default)] pub struct Action { pub name: SStr, pub args: HashMap, pub layer: Layer, pub source: Source, } impl Action { 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 action 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 action = Self::new(name, Source::Relay, None).unwrap_or(Self::null()); action.args = args.into_iter().enumerate().map(|(i, a)| (DataKey::Integer(i as i64), a.into())).collect(); action } 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_list(mut self, name: impl Into, values: I) -> Self where I: IntoIterator, I::Item: Into, { self.args.insert(name.into(), values.into_iter().map(Into::into).collect()); self } pub fn with_any(mut self, name: impl Into, data: impl DataAny) -> Self { self.args.insert(name.into(), Data::Any(Box::new(data))); self } pub fn with_opt(mut self, name: impl Into, value: Option>) -> Self { if let Some(value) = value { self.args.insert(name.into(), value.into()); } self } pub fn with_seq(mut self, values: I) -> Self where I: IntoIterator, I::Item: Into, { for (i, v) in values.into_iter().enumerate() { self.args.insert(DataKey::Integer(i as i64), v.into()); } self } pub fn with_replier(mut self, tx: Replier) -> Self { self.args.insert("replier".into(), Data::Any(Box::new(tx))); 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 any(&self, name: impl Into) -> Option<&T> { self.args.get(&name.into())?.as_any() } pub fn replier(&self) -> Option<&Replier> { self.any("replier") } 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_second(&mut self) -> Result where T: TryFrom, T::Error: Into, { self.take(1) } 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) } pub fn take_any_iter(&mut self) -> impl Iterator { (0..self.len()).filter_map(|i| self.args.remove(&DataKey::from(i))?.into_any()) } pub fn take_replier(&mut self) -> Option { self.take_any("replier") } // Parse pub fn parse_args(words: I, last: Option) -> Result> where I: IntoIterator, { 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(|&s| normal && !s.is_empty()) else { i += 1; return Ok((DataKey::Integer(i - 1), word.into())); }; let mut parts = arg.splitn(2, '='); let key = parts.next().expect("at least one part"); let val = parts.next().map_or(Data::Boolean(true), Data::from); Ok((key.to_owned().into(), val)) }) .collect() } } impl Display for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name)?; for i in 0..self.args.len() { let Ok(s) = self.get::<&str>(i) else { break }; write!(f, " {s}")?; } for (k, v) in &self.args { if let DataKey::String(k) = 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 Action { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let (mut words, last) = crate::shell::unix::split(s, true)?; if words.is_empty() || words[0].is_empty() { bail!("action 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)?; Ok(me) } } impl<'de> Deserialize<'de> for Action { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { <_>::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom) } }