summaryrefslogtreecommitdiffstats
path: root/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/ast.rs43
-rw-r--r--src/parse/mod.rs17
-rw-r--r--src/parse/test_ast.rs16
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")));
+}