Repository: casey/just
Pull request #1534 (2023-01-25 19:44:42)

Analyze and Compiler tweaks
- make the `Settings` type responsible for constructing itself from an iterator of `Setting` values, which allows the analyzer code to be more concise
- use `Compiler::compile` consistently


Patch:
diff --git a/src/analyzer.rs b/src/analyzer.rs
index 172b5b20..68997963 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -41,48 +41,11 @@ impl<'src> Analyzer<'src> {
       }
     }
 
-    let mut settings = Settings::default();
-
-    for (_, set) in self.sets {
-      match set.value {
-        Setting::AllowDuplicateRecipes(allow_duplicate_recipes) => {
-          settings.allow_duplicate_recipes = allow_duplicate_recipes;
-        }
-        Setting::DotenvLoad(dotenv_load) => {
-          settings.dotenv_load = Some(dotenv_load);
-        }
-        Setting::Export(export) => {
-          settings.export = export;
-        }
-        Setting::Fallback(fallback) => {
-          settings.fallback = fallback;
-        }
-        Setting::IgnoreComments(ignore_comments) => {
-          settings.ignore_comments = ignore_comments;
-        }
-        Setting::PositionalArguments(positional_arguments) => {
-          settings.positional_arguments = positional_arguments;
-        }
-        Setting::Shell(shell) => {
-          settings.shell = Some(shell);
-        }
-        Setting::WindowsPowerShell(windows_powershell) => {
-          settings.windows_powershell = windows_powershell;
-        }
-        Setting::WindowsShell(windows_shell) => {
-          settings.windows_shell = Some(windows_shell);
-        }
-        Setting::Tempdir(tempdir) => {
-          settings.tempdir = Some(tempdir);
-        }
-      }
-    }
-
-    let assignments = self.assignments;
+    let settings = Settings::from_setting_iter(self.sets.into_iter().map(|(_, set)| set.value));
 
     let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Default::default();
 
-    AssignmentResolver::resolve_assignments(&assignments)?;
+    AssignmentResolver::resolve_assignments(&self.assignments)?;
 
     for recipe in recipes {
       if let Some(original) = recipe_table.get(recipe.name.lexeme()) {
@@ -96,7 +59,7 @@ impl<'src> Analyzer<'src> {
       recipe_table.insert(recipe.clone());
     }
 
-    let recipes = RecipeResolver::resolve_recipes(recipe_table, &assignments)?;
+    let recipes = RecipeResolver::resolve_recipes(recipe_table, &self.assignments)?;
 
     let mut aliases = Table::new();
     while let Some(alias) = self.aliases.pop() {
@@ -116,7 +79,7 @@ impl<'src> Analyzer<'src> {
           }),
         }),
       aliases,
-      assignments,
+      assignments: self.assignments,
       recipes,
       settings,
     })
diff --git a/src/compiler.rs b/src/compiler.rs
index cd944a72..999feead 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -3,11 +3,11 @@ use super::*;
 pub(crate) struct Compiler;
 
 impl Compiler {
-  pub(crate) fn compile(src: &str) -> CompileResult<Justfile> {
+  pub(crate) fn compile(src: &str) -> CompileResult<(Ast, Justfile)> {
     let tokens = Lexer::lex(src)?;
-
     let ast = Parser::parse(&tokens)?;
+    let justfile = Analyzer::analyze(&ast)?;
 
-    Analyzer::analyze(&ast)
+    Ok((ast, justfile))
   }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 2b44126c..c69bfb6b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,7 +18,7 @@ pub(crate) use {
     alias::Alias, analyzer::Analyzer, assignment::Assignment,
     assignment_resolver::AssignmentResolver, ast::Ast, attribute::Attribute, binding::Binding,
     color::Color, color_display::ColorDisplay, command_ext::CommandExt,
-    compile_error::CompileError, compile_error_kind::CompileErrorKind,
+    compile_error::CompileError, compile_error_kind::CompileErrorKind, compiler::Compiler,
     conditional_operator::ConditionalOperator, config::Config, config_error::ConfigError,
     count::Count, delimiter::Delimiter, dependency::Dependency, dump_format::DumpFormat,
     enclosure::Enclosure, error::Error, evaluator::Evaluator, expression::Expression,
diff --git a/src/settings.rs b/src/settings.rs
index 5c3cc109..8a7d919d 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -20,6 +20,47 @@ pub(crate) struct Settings<'src> {
 }
 
 impl<'src> Settings<'src> {
+  pub(crate) fn from_setting_iter(iter: impl Iterator<Item = Setting<'src>>) -> Self {
+    let mut settings = Self::default();
+
+    for set in iter {
+      match set {
+        Setting::AllowDuplicateRecipes(allow_duplicate_recipes) => {
+          settings.allow_duplicate_recipes = allow_duplicate_recipes;
+        }
+        Setting::DotenvLoad(dotenv_load) => {
+          settings.dotenv_load = Some(dotenv_load);
+        }
+        Setting::Export(export) => {
+          settings.export = export;
+        }
+        Setting::Fallback(fallback) => {
+          settings.fallback = fallback;
+        }
+        Setting::IgnoreComments(ignore_comments) => {
+          settings.ignore_comments = ignore_comments;
+        }
+        Setting::PositionalArguments(positional_arguments) => {
+          settings.positional_arguments = positional_arguments;
+        }
+        Setting::Shell(shell) => {
+          settings.shell = Some(shell);
+        }
+        Setting::WindowsPowerShell(windows_powershell) => {
+          settings.windows_powershell = windows_powershell;
+        }
+        Setting::WindowsShell(windows_shell) => {
+          settings.windows_shell = Some(windows_shell);
+        }
+        Setting::Tempdir(tempdir) => {
+          settings.tempdir = Some(tempdir);
+        }
+      }
+    }
+
+    settings
+  }
+
   pub(crate) fn shell_command(&self, config: &Config) -> Command {
     let (command, args) = self.shell(config);
 
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 0dbaaed0..0a57c498 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -179,9 +179,7 @@ impl Subcommand {
   ) -> Result<(&'src str, Ast<'src>, Justfile<'src>), Error<'src>> {
     let src = loader.load(&search.justfile)?;
 
-    let tokens = Lexer::lex(src)?;
-    let ast = Parser::parse(&tokens)?;
-    let justfile = Analyzer::analyze(&ast)?;
+    let (ast, justfile) = Compiler::compile(src)?;
 
     if config.verbosity.loud() {
       for warning in &justfile.warnings {
diff --git a/src/summary.rs b/src/summary.rs
index f1415f9e..091e08b9 100644
--- a/src/summary.rs
+++ b/src/summary.rs
@@ -29,7 +29,7 @@ pub fn summary(path: &Path) -> Result<Result<Summary, String>, io::Error> {
   let text = fs::read_to_string(path)?;
 
   match Compiler::compile(&text) {
-    Ok(justfile) => Ok(Ok(Summary::new(justfile))),
+    Ok((_, justfile)) => Ok(Ok(Summary::new(justfile))),
     Err(compilation_error) => Ok(Err(compilation_error.to_string())),
   }
 }


Test patch:
diff --git a/src/testing.rs b/src/testing.rs
index 1e420856..4f3a9135 100644
--- a/src/testing.rs
+++ b/src/testing.rs
@@ -2,7 +2,7 @@ use {super::*, crate::compiler::Compiler, pretty_assertions::assert_eq};
 
 pub(crate) fn compile(text: &str) -> Justfile {
   match Compiler::compile(text) {
-    Ok(justfile) => justfile,
+    Ok((_, justfile)) => justfile,
     Err(error) => panic!("Expected successful compilation but got error:\n {error}"),
   }
 }
@@ -99,6 +99,7 @@ macro_rules! run_error {
       if let Subcommand::Run{ overrides, arguments } = &config.subcommand {
         match $crate::compiler::Compiler::compile(&$crate::unindent::unindent($src))
           .expect("Expected successful compilation")
+          .1
           .run(
             &config,
             &search,