diff --git a/src/config.rs b/src/config.rs index 6572d8e..7aed229 100644 --- a/src/config.rs +++ b/src/config.rs @@ -402,13 +402,11 @@ impl Config { match &self.subcommand { Dump => self.dump(justfile), - Evaluate { overrides } => { - self.run(justfile, &search.working_directory, overrides, &Vec::new()) - } + Evaluate { overrides } => self.run(justfile, &search, overrides, &Vec::new()), Run { arguments, overrides, - } => self.run(justfile, &search.working_directory, overrides, arguments), + } => self.run(justfile, &search, overrides, arguments), List => self.list(justfile), Show { ref name } => self.show(&name, justfile), Summary => self.summary(justfile), @@ -561,7 +559,7 @@ impl Config { fn run( &self, justfile: Justfile, - working_directory: &Path, + search: &Search, overrides: &BTreeMap, arguments: &[String], ) -> Result<(), i32> { @@ -569,7 +567,7 @@ impl Config { warn!("Failed to set CTRL-C handler: {}", error) } - let result = justfile.run(&self, working_directory, overrides, arguments); + let result = justfile.run(&self, search, overrides, arguments); if !self.quiet { result.eprint(self.color) diff --git a/src/evaluator.rs b/src/evaluator.rs index c257740..8f5d103 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -6,7 +6,7 @@ pub(crate) struct Evaluator<'src: 'run, 'run> { dotenv: &'run BTreeMap, scope: Scope<'src, 'run>, settings: &'run Settings<'run>, - working_directory: &'run Path, + search: &'run Search, } impl<'src, 'run> Evaluator<'src, 'run> { @@ -16,7 +16,7 @@ impl<'src, 'run> Evaluator<'src, 'run> { dotenv: &'run BTreeMap, overrides: Scope<'src, 'run>, settings: &'run Settings<'run>, - working_directory: &'run Path, + search: &'run Search, ) -> RunResult<'src, Scope<'src, 'run>> { let mut evaluator = Evaluator { scope: overrides, @@ -24,7 +24,7 @@ impl<'src, 'run> Evaluator<'src, 'run> { config, dotenv, settings, - working_directory, + search, }; for assignment in assignments.values() { @@ -67,9 +67,9 @@ impl<'src, 'run> Evaluator<'src, 'run> { } Expression::Call { thunk } => { let context = FunctionContext { - invocation_directory: &self.config.invocation_directory, - working_directory: &self.working_directory, dotenv: self.dotenv, + invocation_directory: &self.config.invocation_directory, + search: self.search, }; use Thunk::*; @@ -127,7 +127,7 @@ impl<'src, 'run> Evaluator<'src, 'run> { cmd.arg(raw); - cmd.current_dir(self.working_directory); + cmd.current_dir(&self.search.working_directory); cmd.export(self.dotenv, &self.scope); @@ -167,15 +167,15 @@ impl<'src, 'run> Evaluator<'src, 'run> { arguments: &[&str], scope: &'run Scope<'src, 'run>, settings: &'run Settings, - working_directory: &'run Path, + search: &'run Search, ) -> RunResult<'src, Scope<'src, 'run>> { let mut evaluator = Evaluator { assignments: None, scope: Scope::child(scope), + search, settings, dotenv, config, - working_directory, }; let mut scope = Scope::child(scope); @@ -211,15 +211,15 @@ impl<'src, 'run> Evaluator<'src, 'run> { dotenv: &'run BTreeMap, scope: &'run Scope<'src, 'run>, settings: &'run Settings, - working_directory: &'run Path, + search: &'run Search, ) -> Evaluator<'src, 'run> { Evaluator { assignments: None, scope: Scope::child(scope), + search, settings, dotenv, config, - working_directory, } } } diff --git a/src/function.rs b/src/function.rs index 5fe186f..7539dc6 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,6 +1,7 @@ use crate::common::*; use target; +use Function::*; pub(crate) enum Function { Nullary(fn(&FunctionContext) -> Result), @@ -10,15 +11,14 @@ pub(crate) enum Function { lazy_static! { pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![ - ("arch", Function::Nullary(arch)), - ("os", Function::Nullary(os)), - ("os_family", Function::Nullary(os_family)), - ("env_var", Function::Unary(env_var)), - ("env_var_or_default", Function::Binary(env_var_or_default)), - ( - "invocation_directory", - Function::Nullary(invocation_directory) - ), + ("arch", Nullary(arch)), + ("os", Nullary(os)), + ("os_family", Nullary(os_family)), + ("justfile_directory", Nullary(justfile_directory)), + ("justfile", Nullary(justfile)), + ("invocation_directory", Nullary(invocation_directory)), + ("env_var", Unary(env_var)), + ("env_var_or_default", Binary(env_var_or_default)), ] .into_iter() .collect(); @@ -26,7 +26,6 @@ lazy_static! { impl Function { pub(crate) fn argc(&self) -> usize { - use self::Function::*; match *self { Nullary(_) => 0, Unary(_) => 1, @@ -48,8 +47,44 @@ fn os_family(_context: &FunctionContext) -> Result { } fn invocation_directory(context: &FunctionContext) -> Result { - Platform::to_shell_path(context.working_directory, context.invocation_directory) - .map_err(|e| format!("Error getting shell path: {}", e)) + Platform::to_shell_path( + &context.search.working_directory, + context.invocation_directory, + ) + .map_err(|e| format!("Error getting shell path: {}", e)) +} + +fn justfile(context: &FunctionContext) -> Result { + context + .search + .justfile + .to_str() + .map(str::to_owned) + .ok_or_else(|| { + format!( + "Justfile path is not valid unicode: {}", + context.search.justfile.to_string_lossy() + ) + }) +} + +fn justfile_directory(context: &FunctionContext) -> Result { + let justfile_directory = context.search.justfile.parent().ok_or_else(|| { + format!( + "Could not resolve justfile directory. Justfile `{}` had no parent.", + context.search.justfile.display() + ) + })?; + + justfile_directory + .to_str() + .map(str::to_owned) + .ok_or_else(|| { + format!( + "Justfile directory is not valid unicode: {}", + justfile_directory.to_string_lossy() + ) + }) } fn env_var(context: &FunctionContext, key: &str) -> Result { diff --git a/src/function_context.rs b/src/function_context.rs index 074b148..3a5a435 100644 --- a/src/function_context.rs +++ b/src/function_context.rs @@ -1,7 +1,7 @@ use crate::common::*; pub(crate) struct FunctionContext<'run> { - pub(crate) invocation_directory: &'run Path, - pub(crate) working_directory: &'run Path, pub(crate) dotenv: &'run BTreeMap, + pub(crate) invocation_directory: &'run Path, + pub(crate) search: &'run Search, } diff --git a/src/justfile.rs b/src/justfile.rs index 86583eb..ee07ccc 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -43,13 +43,13 @@ impl<'src> Justfile<'src> { None } - pub(crate) fn run( - &'src self, - config: &'src Config, - working_directory: &'src Path, - overrides: &'src BTreeMap, - arguments: &'src [String], - ) -> RunResult<'src, ()> { + pub(crate) fn run<'run>( + &'run self, + config: &'run Config, + search: &'run Search, + overrides: &'run BTreeMap, + arguments: &'run [String], + ) -> RunResult<'run, ()> { let argvec: Vec<&str> = if !arguments.is_empty() { arguments.iter().map(|argument| argument.as_str()).collect() } else if let Some(recipe) = self.first() { @@ -105,7 +105,7 @@ impl<'src> Justfile<'src> { &dotenv, scope, &self.settings, - working_directory, + search, )? }; @@ -172,12 +172,12 @@ impl<'src> Justfile<'src> { settings: &self.settings, config, scope, - working_directory, + search, }; let mut ran = BTreeSet::new(); for (recipe, arguments) in grouped { - self.run_recipe(&context, recipe, arguments, &dotenv, &mut ran)? + self.run_recipe(&context, recipe, arguments, &dotenv, &search, &mut ran)? } Ok(()) @@ -203,6 +203,7 @@ impl<'src> Justfile<'src> { recipe: &Recipe<'src>, arguments: &[&'run str], dotenv: &BTreeMap, + search: &'run Search, ran: &mut BTreeSet>, ) -> RunResult<'src, ()> { let scope = Evaluator::evaluate_parameters( @@ -212,16 +213,11 @@ impl<'src> Justfile<'src> { arguments, &context.scope, context.settings, - context.working_directory, + search, )?; - let mut evaluator = Evaluator::recipe_evaluator( - context.config, - dotenv, - &scope, - context.settings, - context.working_directory, - ); + let mut evaluator = + Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search); for Dependency { recipe, arguments } in &recipe.dependencies { let mut invocation = vec![recipe.name().to_owned()]; @@ -236,11 +232,11 @@ impl<'src> Justfile<'src> { .skip(1) .map(String::as_ref) .collect::>(); - self.run_recipe(context, recipe, &arguments, dotenv, ran)?; + self.run_recipe(context, recipe, &arguments, dotenv, search, ran)?; } } - recipe.run(context, dotenv, scope)?; + recipe.run(context, dotenv, scope, search)?; let mut invocation = Vec::new(); invocation.push(recipe.name().to_owned()); diff --git a/src/recipe.rs b/src/recipe.rs index 612e7b8..0abcdf9 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -69,6 +69,7 @@ impl<'src, D> Recipe<'src, D> { context: &RecipeContext<'src, 'run>, dotenv: &BTreeMap, scope: Scope<'src, 'run>, + search: &'run Search, ) -> RunResult<'src, ()> { let config = &context.config; @@ -82,13 +83,8 @@ impl<'src, D> Recipe<'src, D> { ); } - let mut evaluator = Evaluator::recipe_evaluator( - context.config, - dotenv, - &scope, - context.settings, - context.working_directory, - ); + let mut evaluator = + Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search); if self.shebang { let mut evaluated_lines = vec![]; @@ -166,12 +162,16 @@ impl<'src, D> Recipe<'src, D> { })?; // create a command to run the script - let mut command = - Platform::make_shebang_command(&path, context.working_directory, interpreter, argument) - .map_err(|output_error| RuntimeError::Cygpath { - recipe: self.name(), - output_error, - })?; + let mut command = Platform::make_shebang_command( + &path, + &context.search.working_directory, + interpreter, + argument, + ) + .map_err(|output_error| RuntimeError::Cygpath { + recipe: self.name(), + output_error, + })?; command.export(dotenv, &scope); @@ -248,7 +248,7 @@ impl<'src, D> Recipe<'src, D> { let mut cmd = context.settings.shell_command(config); - cmd.current_dir(context.working_directory); + cmd.current_dir(&context.search.working_directory); cmd.arg(command); diff --git a/src/recipe_context.rs b/src/recipe_context.rs index 8e3abbb..0c0dc34 100644 --- a/src/recipe_context.rs +++ b/src/recipe_context.rs @@ -3,6 +3,6 @@ use crate::common::*; pub(crate) struct RecipeContext<'src: 'run, 'run> { pub(crate) config: &'run Config, pub(crate) scope: Scope<'src, 'run>, - pub(crate) working_directory: &'run Path, + pub(crate) search: &'run Search, pub(crate) settings: &'run Settings<'src>, } diff --git a/src/search.rs b/src/search.rs index 34cbb49..1625cab 100644 --- a/src/search.rs +++ b/src/search.rs @@ -2,7 +2,7 @@ use crate::common::*; use std::path::Component; -const FILENAME: &str = "justfile"; +pub(crate) const FILENAME: &str = "justfile"; const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"]; pub(crate) struct Search { diff --git a/src/testing.rs b/src/testing.rs index 42fc6d7..d3ac8f7 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -20,6 +20,16 @@ pub(crate) fn config(args: &[&str]) -> Config { Config::from_matches(&matches).unwrap() } +pub(crate) fn search(config: &Config) -> Search { + let working_directory = config.invocation_directory.clone(); + let justfile = working_directory.join(crate::search::FILENAME); + + Search { + working_directory, + justfile, + } +} + pub(crate) use test_utilities::{tempdir, unindent}; macro_rules! analysis_error { @@ -80,15 +90,15 @@ macro_rules! run_error { } => { #[test] fn $name() { - let config = &$crate::testing::config(&$args); - let current_dir = std::env::current_dir().unwrap(); + let config = $crate::testing::config(&$args); + let search = $crate::testing::search(&config); if let Subcommand::Run{ overrides, arguments } = &config.subcommand { match $crate::compiler::Compiler::compile(&$crate::testing::unindent($src)) .expect("Expected successful compilation") .run( - config, - ¤t_dir, + &config, + &search, &overrides, &arguments, ).expect_err("Expected runtime error") {