summaryrefslogtreecommitdiffstats
path: root/src/parse/ast.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse/ast.rs')
-rw-r--r--src/parse/ast.rs92
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) =