Browse Source

Enable using both script and expression cli arguments

With this modification it becomes possible to use the 'FILE' and '-e <CODE>' arguments in the same command, first executing 'FILE' and then 'CODE', regardless of the argument order.
Bzero 1 year ago
parent
commit
b9f0bcddd6
2 changed files with 50 additions and 23 deletions
  1. 32 23
      numbat-cli/src/main.rs
  2. 18 0
      numbat-cli/tests/integration.rs

+ 32 - 23
numbat-cli/src/main.rs

@@ -52,7 +52,6 @@ struct Args {
         short,
         long,
         value_name = "CODE",
-        conflicts_with = "file",
         action = clap::ArgAction::Append
     )]
     expression: Option<Vec<String>>,
@@ -201,35 +200,45 @@ impl Cli {
                 .load_currency_module_on_demand(true);
         }
 
-        let code_and_source: Option<(String, CodeSource)> = if let Some(ref path) = self.file {
-            Some((
-                fs::read_to_string(path).context(format!(
+        let mut code_and_source = Vec::new();
+
+        if let Some(ref path) = self.file {
+            code_and_source.push((
+                (fs::read_to_string(path).context(format!(
                     "Could not load source file '{}'",
                     path.to_string_lossy()
-                ))?,
+                ))?),
                 CodeSource::File(path.clone()),
-            ))
-        } else {
-            self.expression
-                .as_ref()
-                .map(|exprs| (exprs.iter().join("\n"), CodeSource::Text))
+            ));
         };
 
-        if let Some((code, code_source)) = code_and_source {
-            let result = self.parse_and_evaluate(
-                &code,
-                code_source,
-                ExecutionMode::Normal,
-                self.config.pretty_print,
-            );
+        if let Some(expressions) = &self.expression {
+            code_and_source.push((expressions.iter().join("\n"), CodeSource::Text));
+        }
 
-            match result {
-                std::ops::ControlFlow::Continue(()) => Ok(()),
-                std::ops::ControlFlow::Break(ExitStatus::Success) => Ok(()),
-                std::ops::ControlFlow::Break(ExitStatus::Error) => {
-                    bail!("Interpreter stopped due to error")
-                }
+        if !code_and_source.is_empty() {
+            let mut code_result = Ok(());
+
+            for (code, code_source) in code_and_source {
+                let result = self.parse_and_evaluate(
+                    &code,
+                    code_source,
+                    ExecutionMode::Normal,
+                    self.config.pretty_print,
+                );
+
+                let result_status = match result {
+                    std::ops::ControlFlow::Continue(()) => Ok(()),
+                    std::ops::ControlFlow::Break(ExitStatus::Success) => Ok(()),
+                    std::ops::ControlFlow::Break(ExitStatus::Error) => {
+                        bail!("Interpreter stopped due to error")
+                    }
+                };
+
+                code_result = code_result.and(result_status);
             }
+
+            code_result
         } else {
             let mut currency_fetch_thread = if self.config.load_prelude
                 && self.config.exchange_rates.fetching_policy

+ 18 - 0
numbat-cli/tests/integration.rs

@@ -77,6 +77,24 @@ fn read_code_from_file() {
         .stderr(predicates::str::contains("while parsing"));
 }
 
+#[test]
+fn process_code_from_file_and_cli_expression() {
+    numbat()
+        .arg("tests/examples/pendulum.nbt")
+        .arg("--expression")
+        .arg("oscillation_time(20cm)")
+        .assert()
+        .success();
+
+    numbat()
+        .arg("tests/examples/pendulum.nbt")
+        .arg("--expression")
+        .arg("oscillation_time(20kg)")
+        .assert()
+        .failure()
+        .stderr(predicates::str::contains("while type checking"));
+}
+
 #[test]
 fn print_calls() {
     numbat()