diff options
Diffstat (limited to 'src/parse')
-rw-r--r-- | src/parse/ast.rs | 43 | ||||
-rw-r--r-- | src/parse/mod.rs | 17 | ||||
-rw-r--r-- | src/parse/test_ast.rs | 16 |
3 files changed, 69 insertions, 7 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs index 9c99907..92c04ab 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -117,14 +117,14 @@ impl Pipeline { } #[derive(Debug, Clone, PartialEq, Eq)] -struct Exe { +pub struct Exe { exe: Word, args: Vec<Word>, redirects: Vec<Redirect>, } impl Exe { - async fn eval(self, env: &Env) -> Result<super::Exe> { + pub async fn eval(self, env: &Env) -> Result<super::Exe> { let exe = self.exe.eval(env).await?; assert_eq!(exe.len(), 1); // TODO let exe = &exe[0]; @@ -152,6 +152,15 @@ impl Exe { }) } + pub fn parse(s: &str) -> Result<Self, super::Error> { + Ok(Self::build_ast( + Shell::parse(Rule::exe, s) + .map_err(|e| super::Error::new(s.to_string(), e))? + .next() + .unwrap(), + )) + } + fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self { assert!(matches!(pair.as_rule(), Rule::subshell | Rule::exe)); if matches!(pair.as_rule(), Rule::subshell) { @@ -206,6 +215,36 @@ impl Exe { } } +impl<'de> serde::Deserialize<'de> for Exe { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = Exe; + + fn expecting( + &self, + f: &mut std::fmt::Formatter, + ) -> std::fmt::Result { + f.write_str("a command") + } + + fn visit_str<E>( + self, + value: &str, + ) -> std::result::Result<Self::Value, E> + where + E: serde::de::Error, + { + Exe::parse(value).map_err(serde::de::Error::custom) + } + } + deserializer.deserialize_string(Visitor) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Word { parts: Vec<WordPart>, diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 9663086..e2b7ec0 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -11,7 +11,7 @@ impl Pipeline { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Exe { exe: std::path::PathBuf, args: Vec<String>, @@ -27,6 +27,16 @@ impl Exe { &self.args } + pub fn append(&mut self, other: Self) { + let Self { + exe: _exe, + args, + redirects, + } = other; + self.args.extend(args); + self.redirects.extend(redirects); + } + pub fn redirects(&self) -> &[Redirect] { &self.redirects } @@ -107,10 +117,7 @@ pub struct Error { impl Error { fn new(input: String, e: pest::error::Error<ast::Rule>) -> Self { - Self { - input, - e, - } + Self { input, e } } } diff --git a/src/parse/test_ast.rs b/src/parse/test_ast.rs index 94798a3..4ac75ec 100644 --- a/src/parse/test_ast.rs +++ b/src/parse/test_ast.rs @@ -164,6 +164,16 @@ macro_rules! eval_eq { }}; } +macro_rules! deserialize_eq { + ($line:literal, $parsed:expr) => {{ + use serde::de::IntoDeserializer as _; + use serde::Deserialize as _; + let exe: Result<_, serde::de::value::Error> = + Exe::deserialize($line.into_deserializer()); + assert_eq!(exe.unwrap(), $parsed); + }}; +} + macro_rules! eval_fails { ($line:literal, $env:expr) => {{ let ast = Commands::parse($line).unwrap(); @@ -486,3 +496,9 @@ async fn test_eval_glob() { eval_fails!("echo *.doesnotexist", env); eval_fails!("echo *.{toml,doesnotexist}", env); } + +#[test] +fn test_deserialize() { + deserialize_eq!("foo", e!(w!("foo"))); + deserialize_eq!("foo bar baz", e!(w!("foo"), w!("bar"), w!("baz"))); +} |