diff options
Diffstat (limited to 'src/parse/ast.rs')
-rw-r--r-- | src/parse/ast.rs | 92 |
1 files changed, 68 insertions, 24 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs index e2d5840..5bceed5 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -15,7 +15,7 @@ impl Commands { pub fn parse(full_cmd: &str) -> Result<Self, super::Error> { Ok(Self::build_ast( Shell::parse(Rule::line, full_cmd) - .map_err(|e| super::Error::new(full_cmd, e))? + .map_err(|e| super::Error::new(full_cmd.to_string(), e))? .next() .unwrap() .into_inner() @@ -90,14 +90,14 @@ pub struct Pipeline { } impl Pipeline { - pub async fn eval(self, env: &Env) -> anyhow::Result<super::Pipeline> { + pub async fn eval(self, env: &Env) -> Result<super::Pipeline> { Ok(super::Pipeline { exes: self .exes .into_iter() .map(|exe| exe.eval(env)) .collect::<futures_util::stream::FuturesOrdered<_>>() - .collect::<Result<_, _>>() + .try_collect() .await?, }) } @@ -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) -> anyhow::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]; @@ -137,7 +137,7 @@ impl Exe { arg.eval(env).await.map(IntoIterator::into_iter) }) .collect::<futures_util::stream::FuturesOrdered<_>>() - .collect::<Result<Vec<_>, _>>() + .try_collect::<Vec<_>>() .await? .into_iter() .flatten() @@ -147,11 +147,20 @@ impl Exe { .into_iter() .map(|arg| arg.eval(env)) .collect::<futures_util::stream::FuturesOrdered<_>>() - .collect::<Result<_, _>>() + .try_collect() .await?, }) } + 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) { @@ -162,7 +171,7 @@ impl Exe { return Self { exe: Word { parts: vec![WordPart::SingleQuoted( - std::env::current_exe() + crate::info::current_exe() .unwrap() .to_str() .unwrap() @@ -206,13 +215,43 @@ 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>, } impl Word { - pub async fn eval(self, env: &Env) -> anyhow::Result<Vec<String>> { + pub async fn eval(self, env: &Env) -> Result<Vec<String>> { let mut opts = glob::MatchOptions::new(); opts.require_literal_separator = true; opts.require_literal_leading_dot = true; @@ -330,12 +369,12 @@ impl WordPart { match self { Self::Alternation(_) => unreachable!(), Self::Substitution(commands) => { - let mut cmd = async_std::process::Command::new( - std::env::current_exe().unwrap(), + let mut cmd = tokio::process::Command::new( + crate::info::current_exe().unwrap(), ); cmd.args(&["-c", &commands]); - cmd.stdin(async_std::process::Stdio::inherit()); - cmd.stderr(async_std::process::Stdio::inherit()); + cmd.stdin(std::process::Stdio::inherit()); + cmd.stderr(std::process::Stdio::inherit()); let mut out = String::from_utf8(cmd.output().await.unwrap().stdout) .unwrap(); @@ -408,15 +447,20 @@ impl Redirect { let mut iter = pair.into_inner(); let prefix = iter.next().unwrap().as_str(); - let (from, dir) = if let Some(from) = prefix.strip_suffix(">>") { - (from, super::Direction::Append) - } else if let Some(from) = prefix.strip_suffix('>') { - (from, super::Direction::Out) - } else if let Some(from) = prefix.strip_suffix('<') { - (from, super::Direction::In) - } else { - unreachable!() - }; + let (from, dir) = prefix.strip_suffix(">>").map_or_else( + || { + prefix.strip_suffix('>').map_or_else( + || { + ( + prefix.strip_suffix('<').unwrap(), + super::Direction::In, + ) + }, + |from| (from, super::Direction::Out), + ) + }, + |from| (from, super::Direction::Append), + ); let from = if from.is_empty() { match dir { super::Direction::In => 0, @@ -431,7 +475,7 @@ impl Redirect { Self { from, to, dir } } - async fn eval(self, env: &Env) -> anyhow::Result<super::Redirect> { + async fn eval(self, env: &Env) -> Result<super::Redirect> { let to = if self.to.parts.len() == 1 { if let WordPart::Bareword(s) = &self.to.parts[0] { if let Some(fd) = s.strip_prefix('&') { @@ -509,7 +553,7 @@ fn parse_fd(s: &str) -> std::os::unix::io::RawFd { } } -fn expand_home(dir: &str) -> anyhow::Result<String> { +fn expand_home(dir: &str) -> Result<String> { if dir.starts_with('~') { let path: std::path::PathBuf = dir.into(); if let std::path::Component::Normal(prefix) = |