Refactor evaluator (#2138)

This commit is contained in:
Greg Shuflin 2024-06-08 07:42:16 -07:00 committed by GitHub
parent 38873dcb74
commit 4fbd03735a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 143 additions and 173 deletions

View File

@ -2,38 +2,56 @@ use super::*;
pub(crate) struct Evaluator<'src: 'run, 'run> { pub(crate) struct Evaluator<'src: 'run, 'run> {
pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>, pub(crate) assignments: Option<&'run Table<'src, Assignment<'src>>>,
pub(crate) config: &'run Config, pub(crate) context: ExecutionContext<'src, 'run>,
pub(crate) dotenv: &'run BTreeMap<String, String>,
pub(crate) module_source: &'run Path,
pub(crate) scope: Scope<'src, 'run>, pub(crate) scope: Scope<'src, 'run>,
pub(crate) search: &'run Search,
pub(crate) settings: &'run Settings<'run>,
unsets: &'run HashSet<String>,
} }
impl<'src, 'run> Evaluator<'src, 'run> { impl<'src, 'run> Evaluator<'src, 'run> {
pub(crate) fn evaluate_assignments( pub(crate) fn evaluate_assignments(
assignments: &'run Table<'src, Assignment<'src>>,
config: &'run Config, config: &'run Config,
dotenv: &'run BTreeMap<String, String>, dotenv: &'run BTreeMap<String, String>,
module_source: &'run Path, module: &'run Justfile<'src>,
scope: Scope<'src, 'run>, overrides: &BTreeMap<String, String>,
parent: &'run Scope<'src, 'run>,
search: &'run Search, search: &'run Search,
settings: &'run Settings<'run>, ) -> RunResult<'src, Scope<'src, 'run>>
unsets: &'run HashSet<String>, where
) -> RunResult<'src, Scope<'src, 'run>> { 'src: 'run,
let mut evaluator = Self { {
assignments: Some(assignments), let context = ExecutionContext {
config, config,
dotenv, dotenv,
module_source, module_source: &module.source,
scope, scope: parent,
search, search,
settings, settings: &module.settings,
unsets, unexports: &module.unexports,
}; };
for assignment in assignments.values() { let mut scope = context.scope.child();
let mut unknown_overrides = Vec::new();
for (name, value) in overrides {
if let Some(assignment) = module.assignments.get(name) {
scope.bind(assignment.export, assignment.name, value.clone());
} else {
unknown_overrides.push(name.clone());
}
}
if !unknown_overrides.is_empty() {
return Err(Error::UnknownOverrides {
overrides: unknown_overrides,
});
}
let mut evaluator = Self {
context,
assignments: Some(&module.assignments),
scope,
};
for assignment in module.assignments.values() {
evaluator.evaluate_assignment(assignment)?; evaluator.evaluate_assignment(assignment)?;
} }
@ -155,7 +173,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} }
Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.clone()), Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.clone()),
Expression::Backtick { contents, token } => { Expression::Backtick { contents, token } => {
if self.config.dry_run { if self.context.config.dry_run {
Ok(format!("`{contents}`")) Ok(format!("`{contents}`"))
} else { } else {
Ok(self.run_backtick(contents, token)?) Ok(self.run_backtick(contents, token)?)
@ -216,13 +234,18 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} }
pub(crate) fn run_command(&self, command: &str, args: &[&str]) -> Result<String, OutputError> { pub(crate) fn run_command(&self, command: &str, args: &[&str]) -> Result<String, OutputError> {
let mut cmd = self.settings.shell_command(self.config); let mut cmd = self.context.settings.shell_command(self.context.config);
cmd.arg(command); cmd.arg(command);
cmd.args(args); cmd.args(args);
cmd.current_dir(&self.search.working_directory); cmd.current_dir(&self.context.search.working_directory);
cmd.export(self.settings, self.dotenv, &self.scope, self.unsets); cmd.export(
self.context.settings,
self.context.dotenv,
&self.scope,
self.context.unexports,
);
cmd.stdin(Stdio::inherit()); cmd.stdin(Stdio::inherit());
cmd.stderr(if self.config.verbosity.quiet() { cmd.stderr(if self.context.config.verbosity.quiet() {
Stdio::null() Stdio::null()
} else { } else {
Stdio::inherit() Stdio::inherit()
@ -256,28 +279,11 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} }
pub(crate) fn evaluate_parameters( pub(crate) fn evaluate_parameters(
context: &ExecutionContext<'src, 'run>,
arguments: &[String], arguments: &[String],
config: &'run Config,
dotenv: &'run BTreeMap<String, String>,
module_source: &'run Path,
parameters: &[Parameter<'src>], parameters: &[Parameter<'src>],
scope: &'run Scope<'src, 'run>,
search: &'run Search,
settings: &'run Settings,
unsets: &'run HashSet<String>,
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> { ) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
let mut evaluator = Self { let mut evaluator = Self::new(context, context.scope);
assignments: None,
config,
dotenv,
module_source,
scope: scope.child(),
search,
settings,
unsets,
};
let mut scope = scope.child();
let mut positional = Vec::new(); let mut positional = Vec::new();
@ -308,30 +314,22 @@ impl<'src, 'run> Evaluator<'src, 'run> {
rest = &rest[1..]; rest = &rest[1..];
value value
}; };
scope.bind(parameter.export, parameter.name, value); evaluator
.scope
.bind(parameter.export, parameter.name, value);
} }
Ok((scope, positional)) Ok((evaluator.scope, positional))
} }
pub(crate) fn recipe_evaluator( pub(crate) fn new(
config: &'run Config, context: &ExecutionContext<'src, 'run>,
dotenv: &'run BTreeMap<String, String>,
module_source: &'run Path,
scope: &'run Scope<'src, 'run>, scope: &'run Scope<'src, 'run>,
search: &'run Search,
settings: &'run Settings,
unsets: &'run HashSet<String>,
) -> Self { ) -> Self {
Self { Self {
context: *context,
assignments: None, assignments: None,
config, scope: scope.child(),
dotenv,
module_source,
scope: Scope::child(scope),
search,
settings,
unsets,
} }
} }
} }

View File

@ -1,6 +1,7 @@
use super::*; use super::*;
pub(crate) struct RecipeContext<'src: 'run, 'run> { #[derive(Copy, Clone)]
pub(crate) struct ExecutionContext<'src: 'run, 'run> {
pub(crate) config: &'run Config, pub(crate) config: &'run Config,
pub(crate) dotenv: &'run BTreeMap<String, String>, pub(crate) dotenv: &'run BTreeMap<String, String>,
pub(crate) module_source: &'run Path, pub(crate) module_source: &'run Path,

View File

@ -121,6 +121,7 @@ impl Function {
fn absolute_path(context: Context, path: &str) -> Result<String, String> { fn absolute_path(context: Context, path: &str) -> Result<String, String> {
let abs_path_unchecked = context let abs_path_unchecked = context
.evaluator .evaluator
.context
.search .search
.working_directory .working_directory
.join(path) .join(path)
@ -129,7 +130,7 @@ fn absolute_path(context: Context, path: &str) -> Result<String, String> {
Some(absolute_path) => Ok(absolute_path.to_owned()), Some(absolute_path) => Ok(absolute_path.to_owned()),
None => Err(format!( None => Err(format!(
"Working directory is not valid unicode: {}", "Working directory is not valid unicode: {}",
context.evaluator.search.working_directory.display() context.evaluator.context.search.working_directory.display()
)), )),
} }
} }
@ -152,7 +153,12 @@ fn blake3(_context: Context, s: &str) -> Result<String, String> {
} }
fn blake3_file(context: Context, path: &str) -> Result<String, String> { fn blake3_file(context: Context, path: &str) -> Result<String, String> {
let path = context.evaluator.search.working_directory.join(path); let path = context
.evaluator
.context
.search
.working_directory
.join(path);
let mut hasher = blake3::Hasher::new(); let mut hasher = blake3::Hasher::new();
hasher hasher
.update_mmap_rayon(&path) .update_mmap_rayon(&path)
@ -245,7 +251,7 @@ fn encode_uri_component(_context: Context, s: &str) -> Result<String, String> {
fn env_var(context: Context, key: &str) -> Result<String, String> { fn env_var(context: Context, key: &str) -> Result<String, String> {
use std::env::VarError::*; use std::env::VarError::*;
if let Some(value) = context.evaluator.dotenv.get(key) { if let Some(value) = context.evaluator.context.dotenv.get(key) {
return Ok(value.clone()); return Ok(value.clone());
} }
@ -261,7 +267,7 @@ fn env_var(context: Context, key: &str) -> Result<String, String> {
fn env_var_or_default(context: Context, key: &str, default: &str) -> Result<String, String> { fn env_var_or_default(context: Context, key: &str, default: &str) -> Result<String, String> {
use std::env::VarError::*; use std::env::VarError::*;
if let Some(value) = context.evaluator.dotenv.get(key) { if let Some(value) = context.evaluator.context.dotenv.get(key) {
return Ok(value.clone()); return Ok(value.clone());
} }
@ -308,8 +314,8 @@ fn file_stem(_context: Context, path: &str) -> Result<String, String> {
fn invocation_directory(context: Context) -> Result<String, String> { fn invocation_directory(context: Context) -> Result<String, String> {
Platform::convert_native_path( Platform::convert_native_path(
&context.evaluator.search.working_directory, &context.evaluator.context.search.working_directory,
&context.evaluator.config.invocation_directory, &context.evaluator.context.config.invocation_directory,
) )
.map_err(|e| format!("Error getting shell path: {e}")) .map_err(|e| format!("Error getting shell path: {e}"))
} }
@ -317,6 +323,7 @@ fn invocation_directory(context: Context) -> Result<String, String> {
fn invocation_directory_native(context: Context) -> Result<String, String> { fn invocation_directory_native(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.config .config
.invocation_directory .invocation_directory
.to_str() .to_str()
@ -324,7 +331,12 @@ fn invocation_directory_native(context: Context) -> Result<String, String> {
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"Invocation directory is not valid unicode: {}", "Invocation directory is not valid unicode: {}",
context.evaluator.config.invocation_directory.display() context
.evaluator
.context
.config
.invocation_directory
.display()
) )
}) })
} }
@ -365,6 +377,7 @@ fn just_pid(_context: Context) -> Result<String, String> {
fn justfile(context: Context) -> Result<String, String> { fn justfile(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.search .search
.justfile .justfile
.to_str() .to_str()
@ -372,18 +385,24 @@ fn justfile(context: Context) -> Result<String, String> {
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"Justfile path is not valid unicode: {}", "Justfile path is not valid unicode: {}",
context.evaluator.search.justfile.display() context.evaluator.context.search.justfile.display()
) )
}) })
} }
fn justfile_directory(context: Context) -> Result<String, String> { fn justfile_directory(context: Context) -> Result<String, String> {
let justfile_directory = context.evaluator.search.justfile.parent().ok_or_else(|| { let justfile_directory = context
format!( .evaluator
"Could not resolve justfile directory. Justfile `{}` had no parent.", .context
context.evaluator.search.justfile.display() .search
) .justfile
})?; .parent()
.ok_or_else(|| {
format!(
"Could not resolve justfile directory. Justfile `{}` had no parent.",
context.evaluator.context.search.justfile.display()
)
})?;
justfile_directory justfile_directory
.to_str() .to_str()
@ -411,11 +430,12 @@ fn lowercase(_context: Context, s: &str) -> Result<String, String> {
fn module_directory(context: Context) -> Result<String, String> { fn module_directory(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.search .search
.justfile .justfile
.parent() .parent()
.unwrap() .unwrap()
.join(context.evaluator.module_source) .join(context.evaluator.context.module_source)
.parent() .parent()
.unwrap() .unwrap()
.to_str() .to_str()
@ -423,7 +443,13 @@ fn module_directory(context: Context) -> Result<String, String> {
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"Module directory is not valid unicode: {}", "Module directory is not valid unicode: {}",
context.evaluator.module_source.parent().unwrap().display(), context
.evaluator
.context
.module_source
.parent()
.unwrap()
.display(),
) )
}) })
} }
@ -431,17 +457,18 @@ fn module_directory(context: Context) -> Result<String, String> {
fn module_file(context: Context) -> Result<String, String> { fn module_file(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.search .search
.justfile .justfile
.parent() .parent()
.unwrap() .unwrap()
.join(context.evaluator.module_source) .join(context.evaluator.context.module_source)
.to_str() .to_str()
.map(str::to_owned) .map(str::to_owned)
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"Module file path is not valid unicode: {}", "Module file path is not valid unicode: {}",
context.evaluator.module_source.display(), context.evaluator.context.module_source.display(),
) )
}) })
} }
@ -470,6 +497,7 @@ fn path_exists(context: Context, path: &str) -> Result<String, String> {
Ok( Ok(
context context
.evaluator .evaluator
.context
.search .search
.working_directory .working_directory
.join(path) .join(path)
@ -510,7 +538,12 @@ fn sha256(_context: Context, s: &str) -> Result<String, String> {
fn sha256_file(context: Context, path: &str) -> Result<String, String> { fn sha256_file(context: Context, path: &str) -> Result<String, String> {
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
let path = context.evaluator.search.working_directory.join(path); let path = context
.evaluator
.context
.search
.working_directory
.join(path);
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
let mut file = let mut file =
fs::File::open(&path).map_err(|err| format!("Failed to open `{}`: {err}", path.display()))?; fs::File::open(&path).map_err(|err| format!("Failed to open `{}`: {err}", path.display()))?;
@ -546,6 +579,7 @@ fn snakecase(_context: Context, s: &str) -> Result<String, String> {
fn source_directory(context: Context) -> Result<String, String> { fn source_directory(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.search .search
.justfile .justfile
.parent() .parent()
@ -566,6 +600,7 @@ fn source_directory(context: Context) -> Result<String, String> {
fn source_file(context: Context) -> Result<String, String> { fn source_file(context: Context) -> Result<String, String> {
context context
.evaluator .evaluator
.context
.search .search
.justfile .justfile
.parent() .parent()

View File

@ -78,46 +78,6 @@ impl<'src> Justfile<'src> {
.next() .next()
} }
fn scope<'run>(
&'run self,
config: &'run Config,
dotenv: &'run BTreeMap<String, String>,
search: &'run Search,
overrides: &BTreeMap<String, String>,
parent: &'run Scope<'src, 'run>,
) -> RunResult<'src, Scope<'src, 'run>>
where
'src: 'run,
{
let mut scope = parent.child();
let mut unknown_overrides = Vec::new();
for (name, value) in overrides {
if let Some(assignment) = self.assignments.get(name) {
scope.bind(assignment.export, assignment.name, value.clone());
} else {
unknown_overrides.push(name.clone());
}
}
if !unknown_overrides.is_empty() {
return Err(Error::UnknownOverrides {
overrides: unknown_overrides,
});
}
Evaluator::evaluate_assignments(
&self.assignments,
config,
dotenv,
&self.source,
scope,
search,
&self.settings,
&self.unexports,
)
}
pub(crate) fn run( pub(crate) fn run(
&self, &self,
config: &Config, config: &Config,
@ -145,7 +105,7 @@ impl<'src> Justfile<'src> {
let root = Scope::root(); let root = Scope::root();
let scope = self.scope(config, &dotenv, search, overrides, &root)?; let scope = Evaluator::evaluate_assignments(config, &dotenv, self, overrides, &root, search)?;
match &config.subcommand { match &config.subcommand {
Subcommand::Command { Subcommand::Command {
@ -196,11 +156,7 @@ impl<'src> Justfile<'src> {
}); });
} }
} else { } else {
let mut width = 0; let width = scope.names().fold(0, |max, name| name.len().max(max));
for name in scope.names() {
width = cmp::max(name.len(), width);
}
for binding in scope.bindings() { for binding in scope.bindings() {
println!( println!(
@ -281,7 +237,7 @@ impl<'src> Justfile<'src> {
let mut ran = Ran::default(); let mut ran = Ran::default();
for invocation in invocations { for invocation in invocations {
let context = RecipeContext { let context = ExecutionContext {
config, config,
dotenv: &dotenv, dotenv: &dotenv,
module_source: invocation.module_source, module_source: invocation.module_source,
@ -301,7 +257,6 @@ impl<'src> Justfile<'src> {
&context, &context,
&mut ran, &mut ran,
invocation.recipe, invocation.recipe,
search,
)?; )?;
} }
@ -339,7 +294,14 @@ impl<'src> Justfile<'src> {
let scope = if let Some(scope) = scopes.get(path) { let scope = if let Some(scope) = scopes.get(path) {
scope scope
} else { } else {
let scope = module.scope(config, dotenv, search, &BTreeMap::new(), parent)?; let scope = Evaluator::evaluate_assignments(
config,
dotenv,
module,
&BTreeMap::new(),
parent,
search,
)?;
let scope = arena.alloc(scope); let scope = arena.alloc(scope);
scopes.insert(path.clone(), scope); scopes.insert(path.clone(), scope);
scopes.get(path).unwrap() scopes.get(path).unwrap()
@ -420,10 +382,9 @@ impl<'src> Justfile<'src> {
fn run_recipe( fn run_recipe(
arguments: &[String], arguments: &[String],
context: &RecipeContext<'src, '_>, context: &ExecutionContext<'src, '_>,
ran: &mut Ran<'src>, ran: &mut Ran<'src>,
recipe: &Recipe<'src>, recipe: &Recipe<'src>,
search: &Search,
) -> RunResult<'src> { ) -> RunResult<'src> {
if ran.has_run(&recipe.namepath, arguments) { if ran.has_run(&recipe.namepath, arguments) {
return Ok(()); return Ok(());
@ -435,29 +396,12 @@ impl<'src> Justfile<'src> {
}); });
} }
let (outer, positional) = Evaluator::evaluate_parameters( let (outer, positional) =
arguments, Evaluator::evaluate_parameters(context, arguments, &recipe.parameters)?;
context.config,
context.dotenv,
context.module_source,
&recipe.parameters,
context.scope,
search,
context.settings,
context.unexports,
)?;
let scope = outer.child(); let scope = outer.child();
let mut evaluator = Evaluator::recipe_evaluator( let mut evaluator = Evaluator::new(context, &scope);
context.config,
context.dotenv,
context.module_source,
&scope,
search,
context.settings,
context.unexports,
);
if !context.config.no_dependencies { if !context.config.no_dependencies {
for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) { for Dependency { recipe, arguments } in recipe.dependencies.iter().take(recipe.priors) {
@ -466,7 +410,7 @@ impl<'src> Justfile<'src> {
.map(|argument| evaluator.evaluate_expression(argument)) .map(|argument| evaluator.evaluate_expression(argument))
.collect::<RunResult<Vec<String>>>()?; .collect::<RunResult<Vec<String>>>()?;
Self::run_recipe(&arguments, context, ran, recipe, search)?; Self::run_recipe(&arguments, context, ran, recipe)?;
} }
} }
@ -482,7 +426,7 @@ impl<'src> Justfile<'src> {
evaluated.push(evaluator.evaluate_expression(argument)?); evaluated.push(evaluator.evaluate_expression(argument)?);
} }
Self::run_recipe(&evaluated, context, &mut ran, recipe, search)?; Self::run_recipe(&evaluated, context, &mut ran, recipe)?;
} }
} }

View File

@ -22,14 +22,14 @@ pub(crate) use {
condition::Condition, conditional_operator::ConditionalOperator, config::Config, condition::Condition, conditional_operator::ConditionalOperator, config::Config,
config_error::ConfigError, constants::constants, count::Count, delimiter::Delimiter, config_error::ConfigError, constants::constants, count::Count, delimiter::Delimiter,
dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error, dependency::Dependency, dump_format::DumpFormat, enclosure::Enclosure, error::Error,
evaluator::Evaluator, expression::Expression, fragment::Fragment, function::Function, evaluator::Evaluator, execution_context::ExecutionContext, expression::Expression,
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item, fragment::Fragment, function::Function, interrupt_guard::InterruptGuard,
justfile::Justfile, keyed::Keyed, keyword::Keyword, lexer::Lexer, line::Line, list::List, interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, keyed::Keyed,
load_dotenv::load_dotenv, loader::Loader, module_path::ModulePath, name::Name, keyword::Keyword, lexer::Lexer, line::Line, list::List, load_dotenv::load_dotenv,
namepath::Namepath, ordinal::Ordinal, output::output, output_error::OutputError, loader::Loader, module_path::ModulePath, name::Name, namepath::Namepath, ordinal::Ordinal,
parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform, output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind,
platform_interface::PlatformInterface, position::Position, positional::Positional, ran::Ran, parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position,
range_ext::RangeExt, recipe::Recipe, recipe_context::RecipeContext, positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe,
recipe_resolver::RecipeResolver, recipe_signature::RecipeSignature, scope::Scope, recipe_resolver::RecipeResolver, recipe_signature::RecipeSignature, scope::Scope,
search::Search, search_config::SearchConfig, search_error::SearchError, set::Set, search::Search, search_config::SearchConfig, search_error::SearchError, set::Set,
setting::Setting, settings::Settings, shebang::Shebang, shell::Shell, setting::Setting, settings::Settings, shebang::Shebang, shell::Shell,
@ -139,6 +139,7 @@ mod dump_format;
mod enclosure; mod enclosure;
mod error; mod error;
mod evaluator; mod evaluator;
mod execution_context;
mod expression; mod expression;
mod fragment; mod fragment;
mod function; mod function;
@ -169,7 +170,6 @@ mod positional;
mod ran; mod ran;
mod range_ext; mod range_ext;
mod recipe; mod recipe;
mod recipe_context;
mod recipe_resolver; mod recipe_resolver;
mod recipe_signature; mod recipe_signature;
mod run; mod run;

View File

@ -146,7 +146,7 @@ impl<'src, D> Recipe<'src, D> {
pub(crate) fn run<'run>( pub(crate) fn run<'run>(
&self, &self,
context: &RecipeContext<'src, 'run>, context: &ExecutionContext<'src, 'run>,
scope: &Scope<'src, 'run>, scope: &Scope<'src, 'run>,
positional: &[String], positional: &[String],
) -> RunResult<'src, ()> { ) -> RunResult<'src, ()> {
@ -162,15 +162,7 @@ impl<'src, D> Recipe<'src, D> {
); );
} }
let evaluator = Evaluator::recipe_evaluator( let evaluator = Evaluator::new(context, scope);
context.config,
context.dotenv,
context.module_source,
scope,
context.search,
context.settings,
context.unexports,
);
if self.shebang { if self.shebang {
self.run_shebang(context, scope, positional, config, evaluator) self.run_shebang(context, scope, positional, config, evaluator)
@ -181,7 +173,7 @@ impl<'src, D> Recipe<'src, D> {
fn run_linewise<'run>( fn run_linewise<'run>(
&self, &self,
context: &RecipeContext<'src, 'run>, context: &ExecutionContext<'src, 'run>,
scope: &Scope<'src, 'run>, scope: &Scope<'src, 'run>,
positional: &[String], positional: &[String],
config: &Config, config: &Config,
@ -313,7 +305,7 @@ impl<'src, D> Recipe<'src, D> {
pub(crate) fn run_shebang<'run>( pub(crate) fn run_shebang<'run>(
&self, &self,
context: &RecipeContext<'src, 'run>, context: &ExecutionContext<'src, 'run>,
scope: &Scope<'src, 'run>, scope: &Scope<'src, 'run>,
positional: &[String], positional: &[String],
config: &Config, config: &Config,