From 7359deeb996e3be416282f09dffbac0c844f4fa1 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Fri, 17 May 2024 17:21:47 -0700 Subject: [PATCH] Replace FunctionContext with Evaluator (#2048) --- src/evaluator.rs | 78 +++++++++----------- src/function.rs | 160 +++++++++++++++++++--------------------- src/function_context.rs | 7 -- src/lib.rs | 12 ++- src/thunk.rs | 12 +-- 5 files changed, 121 insertions(+), 148 deletions(-) delete mode 100644 src/function_context.rs diff --git a/src/evaluator.rs b/src/evaluator.rs index e6e14726..0ac58198 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,12 +1,12 @@ use super::*; pub(crate) struct Evaluator<'src: 'run, 'run> { - assignments: Option<&'run Table<'src, Assignment<'src>>>, - config: &'run Config, - dotenv: &'run BTreeMap, - scope: Scope<'src, 'run>, - settings: &'run Settings<'run>, - search: &'run Search, + pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>, + pub(crate) config: &'run Config, + pub(crate) dotenv: &'run BTreeMap, + pub(crate) scope: Scope<'src, 'run>, + pub(crate) settings: &'run Settings<'run>, + pub(crate) search: &'run Search, } impl<'src, 'run> Evaluator<'src, 'run> { @@ -68,30 +68,23 @@ impl<'src, 'run> Evaluator<'src, 'run> { Expression::Call { thunk } => { use Thunk::*; - let context = FunctionContext { - dotenv: self.dotenv, - invocation_directory: &self.config.invocation_directory, - search: self.search, - }; - match thunk { - Nullary { name, function, .. } => { - function(&context).map_err(|message| Error::FunctionCall { - function: *name, - message, - }) - } + Nullary { name, function, .. } => function(self).map_err(|message| Error::FunctionCall { + function: *name, + message, + }), Unary { name, function, arg, .. - } => function(&context, &self.evaluate_expression(arg)?).map_err(|message| { - Error::FunctionCall { + } => { + let arg = self.evaluate_expression(arg)?; + function(self, &arg).map_err(|message| Error::FunctionCall { function: *name, message, - } - }), + }) + } UnaryOpt { name, function, @@ -104,7 +97,7 @@ impl<'src, 'run> Evaluator<'src, 'run> { None => None, }; - function(&context, &a, b.as_deref()).map_err(|message| Error::FunctionCall { + function(self, &a, b.as_deref()).map_err(|message| Error::FunctionCall { function: *name, message, }) @@ -114,15 +107,14 @@ impl<'src, 'run> Evaluator<'src, 'run> { function, args: [a, b], .. - } => function( - &context, - &self.evaluate_expression(a)?, - &self.evaluate_expression(b)?, - ) - .map_err(|message| Error::FunctionCall { - function: *name, - message, - }), + } => { + let a = self.evaluate_expression(a)?; + let b = self.evaluate_expression(b)?; + function(self, &a, &b).map_err(|message| Error::FunctionCall { + function: *name, + message, + }) + } BinaryPlus { name, function, @@ -131,13 +123,12 @@ impl<'src, 'run> Evaluator<'src, 'run> { } => { let a = self.evaluate_expression(a)?; let b = self.evaluate_expression(b)?; - let mut rest_evaluated = Vec::new(); for arg in rest { rest_evaluated.push(self.evaluate_expression(arg)?); } - function(&context, &a, &b, &rest_evaluated).map_err(|message| Error::FunctionCall { + function(self, &a, &b, &rest_evaluated).map_err(|message| Error::FunctionCall { function: *name, message, }) @@ -147,16 +138,15 @@ impl<'src, 'run> Evaluator<'src, 'run> { function, args: [a, b, c], .. - } => function( - &context, - &self.evaluate_expression(a)?, - &self.evaluate_expression(b)?, - &self.evaluate_expression(c)?, - ) - .map_err(|message| Error::FunctionCall { - function: *name, - message, - }), + } => { + let a = self.evaluate_expression(a)?; + let b = self.evaluate_expression(b)?; + let c = self.evaluate_expression(c)?; + function(self, &a, &b, &c).map_err(|message| Error::FunctionCall { + function: *name, + message, + }) + } } } Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.clone()), diff --git a/src/function.rs b/src/function.rs index 64f607e4..2c7dc0c2 100644 --- a/src/function.rs +++ b/src/function.rs @@ -9,12 +9,12 @@ use { }; pub(crate) enum Function { - Nullary(fn(&FunctionContext) -> Result), - Unary(fn(&FunctionContext, &str) -> Result), - UnaryOpt(fn(&FunctionContext, &str, Option<&str>) -> Result), - Binary(fn(&FunctionContext, &str, &str) -> Result), - BinaryPlus(fn(&FunctionContext, &str, &str, &[String]) -> Result), - Ternary(fn(&FunctionContext, &str, &str, &str) -> Result), + Nullary(fn(&Evaluator) -> Result), + Unary(fn(&Evaluator, &str) -> Result), + UnaryOpt(fn(&Evaluator, &str, Option<&str>) -> Result), + Binary(fn(&Evaluator, &str, &str) -> Result), + BinaryPlus(fn(&Evaluator, &str, &str, &[String]) -> Result), + Ternary(fn(&Evaluator, &str, &str, &str) -> Result), } pub(crate) fn get(name: &str) -> Option { @@ -96,18 +96,18 @@ impl Function { } } -fn absolute_path(context: &FunctionContext, path: &str) -> Result { - let abs_path_unchecked = context.search.working_directory.join(path).lexiclean(); +fn absolute_path(evaluator: &Evaluator, path: &str) -> Result { + let abs_path_unchecked = evaluator.search.working_directory.join(path).lexiclean(); match abs_path_unchecked.to_str() { Some(absolute_path) => Ok(absolute_path.to_owned()), None => Err(format!( "Working directory is not valid unicode: {}", - context.search.working_directory.display() + evaluator.search.working_directory.display() )), } } -fn append(_context: &FunctionContext, suffix: &str, s: &str) -> Result { +fn append(_evaluator: &Evaluator, suffix: &str, s: &str) -> Result { Ok( s.split_whitespace() .map(|s| format!("{s}{suffix}")) @@ -116,16 +116,16 @@ fn append(_context: &FunctionContext, suffix: &str, s: &str) -> Result Result { +fn arch(_evaluator: &Evaluator) -> Result { Ok(target::arch().to_owned()) } -fn blake3(_context: &FunctionContext, s: &str) -> Result { +fn blake3(_evaluator: &Evaluator, s: &str) -> Result { Ok(blake3::hash(s.as_bytes()).to_string()) } -fn blake3_file(context: &FunctionContext, path: &str) -> Result { - let path = context.search.working_directory.join(path); +fn blake3_file(evaluator: &Evaluator, path: &str) -> Result { + let path = evaluator.search.working_directory.join(path); let mut hasher = blake3::Hasher::new(); hasher .update_mmap_rayon(&path) @@ -133,7 +133,7 @@ fn blake3_file(context: &FunctionContext, path: &str) -> Result Ok(hasher.finalize().to_string()) } -fn canonicalize(_context: &FunctionContext, path: &str) -> Result { +fn canonicalize(_evaluator: &Evaluator, path: &str) -> Result { let canonical = std::fs::canonicalize(path).map_err(|err| format!("I/O error canonicalizing path: {err}"))?; @@ -145,7 +145,7 @@ fn canonicalize(_context: &FunctionContext, path: &str) -> Result Result { +fn capitalize(_evaluator: &Evaluator, s: &str) -> Result { let mut capitalized = String::new(); for (i, c) in s.chars().enumerate() { if i == 0 { @@ -157,7 +157,7 @@ fn capitalize(_context: &FunctionContext, s: &str) -> Result { Ok(capitalized) } -fn clean(_context: &FunctionContext, path: &str) -> Result { +fn clean(_evaluator: &Evaluator, path: &str) -> Result { Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned()) } @@ -177,10 +177,10 @@ fn dir(name: &'static str, f: fn() -> Option) -> Result } } -fn env_var(context: &FunctionContext, key: &str) -> Result { +fn env_var(evaluator: &Evaluator, key: &str) -> Result { use std::env::VarError::*; - if let Some(value) = context.dotenv.get(key) { + if let Some(value) = evaluator.dotenv.get(key) { return Ok(value.clone()); } @@ -193,14 +193,10 @@ fn env_var(context: &FunctionContext, key: &str) -> Result { } } -fn env_var_or_default( - context: &FunctionContext, - key: &str, - default: &str, -) -> Result { +fn env_var_or_default(evaluator: &Evaluator, key: &str, default: &str) -> Result { use std::env::VarError::*; - if let Some(value) = context.dotenv.get(key) { + if let Some(value) = evaluator.dotenv.get(key) { return Ok(value.clone()); } @@ -213,60 +209,61 @@ fn env_var_or_default( } } -fn env(context: &FunctionContext, key: &str, default: Option<&str>) -> Result { +fn env(evaluator: &Evaluator, key: &str, default: Option<&str>) -> Result { match default { - Some(val) => env_var_or_default(context, key, val), - None => env_var(context, key), + Some(val) => env_var_or_default(evaluator, key, val), + None => env_var(evaluator, key), } } -fn error(_context: &FunctionContext, message: &str) -> Result { +fn error(_evaluator: &Evaluator, message: &str) -> Result { Err(message.to_owned()) } -fn extension(_context: &FunctionContext, path: &str) -> Result { +fn extension(_evaluator: &Evaluator, path: &str) -> Result { Utf8Path::new(path) .extension() .map(str::to_owned) .ok_or_else(|| format!("Could not extract extension from `{path}`")) } -fn file_name(_context: &FunctionContext, path: &str) -> Result { +fn file_name(_evaluator: &Evaluator, path: &str) -> Result { Utf8Path::new(path) .file_name() .map(str::to_owned) .ok_or_else(|| format!("Could not extract file name from `{path}`")) } -fn file_stem(_context: &FunctionContext, path: &str) -> Result { +fn file_stem(_evaluator: &Evaluator, path: &str) -> Result { Utf8Path::new(path) .file_stem() .map(str::to_owned) .ok_or_else(|| format!("Could not extract file stem from `{path}`")) } -fn invocation_directory(context: &FunctionContext) -> Result { +fn invocation_directory(evaluator: &Evaluator) -> Result { Platform::convert_native_path( - &context.search.working_directory, - context.invocation_directory, + &evaluator.search.working_directory, + &evaluator.config.invocation_directory, ) .map_err(|e| format!("Error getting shell path: {e}")) } -fn invocation_directory_native(context: &FunctionContext) -> Result { - context +fn invocation_directory_native(evaluator: &Evaluator) -> Result { + evaluator + .config .invocation_directory .to_str() .map(str::to_owned) .ok_or_else(|| { format!( "Invocation directory is not valid unicode: {}", - context.invocation_directory.display() + evaluator.config.invocation_directory.display() ) }) } -fn prepend(_context: &FunctionContext, prefix: &str, s: &str) -> Result { +fn prepend(_evaluator: &Evaluator, prefix: &str, s: &str) -> Result { Ok( s.split_whitespace() .map(|s| format!("{prefix}{s}")) @@ -275,12 +272,7 @@ fn prepend(_context: &FunctionContext, prefix: &str, s: &str) -> Result Result { +fn join(_evaluator: &Evaluator, base: &str, with: &str, and: &[String]) -> Result { let mut result = Utf8Path::new(base).join(with); for arg in and { result.push(arg); @@ -288,7 +280,7 @@ fn join( Ok(result.to_string()) } -fn just_executable(_context: &FunctionContext) -> Result { +fn just_executable(_evaluator: &Evaluator) -> Result { let exe_path = env::current_exe().map_err(|e| format!("Error getting current executable: {e}"))?; @@ -300,12 +292,12 @@ fn just_executable(_context: &FunctionContext) -> Result { }) } -fn just_pid(_context: &FunctionContext) -> Result { +fn just_pid(_evaluator: &Evaluator) -> Result { Ok(std::process::id().to_string()) } -fn justfile(context: &FunctionContext) -> Result { - context +fn justfile(evaluator: &Evaluator) -> Result { + evaluator .search .justfile .to_str() @@ -313,16 +305,16 @@ fn justfile(context: &FunctionContext) -> Result { .ok_or_else(|| { format!( "Justfile path is not valid unicode: {}", - context.search.justfile.display() + evaluator.search.justfile.display() ) }) } -fn justfile_directory(context: &FunctionContext) -> Result { - let justfile_directory = context.search.justfile.parent().ok_or_else(|| { +fn justfile_directory(evaluator: &Evaluator) -> Result { + let justfile_directory = evaluator.search.justfile.parent().ok_or_else(|| { format!( "Could not resolve justfile directory. Justfile `{}` had no parent.", - context.search.justfile.display() + evaluator.search.justfile.display() ) })?; @@ -337,41 +329,41 @@ fn justfile_directory(context: &FunctionContext) -> Result { }) } -fn kebabcase(_context: &FunctionContext, s: &str) -> Result { +fn kebabcase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_kebab_case()) } -fn lowercamelcase(_context: &FunctionContext, s: &str) -> Result { +fn lowercamelcase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_lower_camel_case()) } -fn lowercase(_context: &FunctionContext, s: &str) -> Result { +fn lowercase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_lowercase()) } -fn num_cpus(_context: &FunctionContext) -> Result { +fn num_cpus(_evaluator: &Evaluator) -> Result { let num = num_cpus::get(); Ok(num.to_string()) } -fn os(_context: &FunctionContext) -> Result { +fn os(_evaluator: &Evaluator) -> Result { Ok(target::os().to_owned()) } -fn os_family(_context: &FunctionContext) -> Result { +fn os_family(_evaluator: &Evaluator) -> Result { Ok(target::family().to_owned()) } -fn parent_directory(_context: &FunctionContext, path: &str) -> Result { +fn parent_directory(_evaluator: &Evaluator, path: &str) -> Result { Utf8Path::new(path) .parent() .map(Utf8Path::to_string) .ok_or_else(|| format!("Could not extract parent directory from `{path}`")) } -fn path_exists(context: &FunctionContext, path: &str) -> Result { +fn path_exists(evaluator: &Evaluator, path: &str) -> Result { Ok( - context + evaluator .search .working_directory .join(path) @@ -380,16 +372,16 @@ fn path_exists(context: &FunctionContext, path: &str) -> Result ) } -fn quote(_context: &FunctionContext, s: &str) -> Result { +fn quote(_evaluator: &Evaluator, s: &str) -> Result { Ok(format!("'{}'", s.replace('\'', "'\\''"))) } -fn replace(_context: &FunctionContext, s: &str, from: &str, to: &str) -> Result { +fn replace(_evaluator: &Evaluator, s: &str, from: &str, to: &str) -> Result { Ok(s.replace(from, to)) } fn replace_regex( - _context: &FunctionContext, + _evaluator: &Evaluator, s: &str, regex: &str, replacement: &str, @@ -402,7 +394,7 @@ fn replace_regex( ) } -fn sha256(_context: &FunctionContext, s: &str) -> Result { +fn sha256(_evaluator: &Evaluator, s: &str) -> Result { use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); hasher.update(s); @@ -410,9 +402,9 @@ fn sha256(_context: &FunctionContext, s: &str) -> Result { Ok(format!("{hash:x}")) } -fn sha256_file(context: &FunctionContext, path: &str) -> Result { +fn sha256_file(evaluator: &Evaluator, path: &str) -> Result { use sha2::{Digest, Sha256}; - let path = context.search.working_directory.join(path); + let path = evaluator.search.working_directory.join(path); let mut hasher = Sha256::new(); let mut file = fs::File::open(&path).map_err(|err| format!("Failed to open `{}`: {err}", path.display()))?; @@ -422,63 +414,63 @@ fn sha256_file(context: &FunctionContext, path: &str) -> Result Ok(format!("{hash:x}")) } -fn shoutykebabcase(_context: &FunctionContext, s: &str) -> Result { +fn shoutykebabcase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_shouty_kebab_case()) } -fn shoutysnakecase(_context: &FunctionContext, s: &str) -> Result { +fn shoutysnakecase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_shouty_snake_case()) } -fn snakecase(_context: &FunctionContext, s: &str) -> Result { +fn snakecase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_snake_case()) } -fn titlecase(_context: &FunctionContext, s: &str) -> Result { +fn titlecase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_title_case()) } -fn trim(_context: &FunctionContext, s: &str) -> Result { +fn trim(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.trim().to_owned()) } -fn trim_end(_context: &FunctionContext, s: &str) -> Result { +fn trim_end(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.trim_end().to_owned()) } -fn trim_end_match(_context: &FunctionContext, s: &str, pat: &str) -> Result { +fn trim_end_match(_evaluator: &Evaluator, s: &str, pat: &str) -> Result { Ok(s.strip_suffix(pat).unwrap_or(s).to_owned()) } -fn trim_end_matches(_context: &FunctionContext, s: &str, pat: &str) -> Result { +fn trim_end_matches(_evaluator: &Evaluator, s: &str, pat: &str) -> Result { Ok(s.trim_end_matches(pat).to_owned()) } -fn trim_start(_context: &FunctionContext, s: &str) -> Result { +fn trim_start(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.trim_start().to_owned()) } -fn trim_start_match(_context: &FunctionContext, s: &str, pat: &str) -> Result { +fn trim_start_match(_evaluator: &Evaluator, s: &str, pat: &str) -> Result { Ok(s.strip_prefix(pat).unwrap_or(s).to_owned()) } -fn trim_start_matches(_context: &FunctionContext, s: &str, pat: &str) -> Result { +fn trim_start_matches(_evaluator: &Evaluator, s: &str, pat: &str) -> Result { Ok(s.trim_start_matches(pat).to_owned()) } -fn uppercamelcase(_context: &FunctionContext, s: &str) -> Result { +fn uppercamelcase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_upper_camel_case()) } -fn uppercase(_context: &FunctionContext, s: &str) -> Result { +fn uppercase(_evaluator: &Evaluator, s: &str) -> Result { Ok(s.to_uppercase()) } -fn uuid(_context: &FunctionContext) -> Result { +fn uuid(_evaluator: &Evaluator) -> Result { Ok(uuid::Uuid::new_v4().to_string()) } -fn without_extension(_context: &FunctionContext, path: &str) -> Result { +fn without_extension(_evaluator: &Evaluator, path: &str) -> Result { let parent = Utf8Path::new(path) .parent() .ok_or_else(|| format!("Could not extract parent from `{path}`"))?; @@ -493,7 +485,7 @@ fn without_extension(_context: &FunctionContext, path: &str) -> Result=0.1.0") fn semver_matches( - _context: &FunctionContext, + _evaluator: &Evaluator, version: &str, requirement: &str, ) -> Result { diff --git a/src/function_context.rs b/src/function_context.rs deleted file mode 100644 index 986d3fea..00000000 --- a/src/function_context.rs +++ /dev/null @@ -1,7 +0,0 @@ -use super::*; - -pub(crate) struct FunctionContext<'run> { - pub(crate) dotenv: &'run BTreeMap, - pub(crate) invocation_directory: &'run Path, - pub(crate) search: &'run Search, -} diff --git a/src/lib.rs b/src/lib.rs index 13d3af8e..f3f0d0f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,12 +23,11 @@ pub(crate) use { config_error::ConfigError, count::Count, delimiter::Delimiter, dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error, evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function, - function_context::FunctionContext, interrupt_guard::InterruptGuard, - interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyed::Keyed, - keyword::Keyword, lexer::Lexer, line::Line, list::List, load_dotenv::load_dotenv, - loader::Loader, name::Name, namepath::Namepath, ordinal::Ordinal, output::output, - output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, - platform::Platform, platform_interface::PlatformInterface, position::Position, + interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item, + justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line, list::List, + load_dotenv::load_dotenv, loader::Loader, name::Name, namepath::Namepath, ordinal::Ordinal, + output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind, + parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position, positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe, recipe_context::RecipeContext, recipe_resolver::RecipeResolver, scope::Scope, search::Search, search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting, @@ -139,7 +138,6 @@ mod evaluator; mod expression; mod fragment; mod function; -mod function_context; mod interrupt_guard; mod interrupt_handler; mod item; diff --git a/src/thunk.rs b/src/thunk.rs index c6e5684b..da25d00b 100644 --- a/src/thunk.rs +++ b/src/thunk.rs @@ -6,36 +6,36 @@ pub(crate) enum Thunk<'src> { Nullary { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext) -> Result, + function: fn(&Evaluator) -> Result, }, Unary { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext, &str) -> Result, + function: fn(&Evaluator, &str) -> Result, arg: Box>, }, UnaryOpt { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext, &str, Option<&str>) -> Result, + function: fn(&Evaluator, &str, Option<&str>) -> Result, args: (Box>, Box>>), }, Binary { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext, &str, &str) -> Result, + function: fn(&Evaluator, &str, &str) -> Result, args: [Box>; 2], }, BinaryPlus { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext, &str, &str, &[String]) -> Result, + function: fn(&Evaluator, &str, &str, &[String]) -> Result, args: ([Box>; 2], Vec>), }, Ternary { name: Name<'src>, #[derivative(Debug = "ignore", PartialEq = "ignore")] - function: fn(&FunctionContext, &str, &str, &str) -> Result, + function: fn(&Evaluator, &str, &str, &str) -> Result, args: [Box>; 3], }, }