use super::*;
pub(crate) type UnresolvedRecipe<'src> = Recipe<'src, UnresolvedDependency<'src>>;
impl<'src> UnresolvedRecipe<'src> {
pub(crate) fn resolve(
mut self,
evaluator: &mut Evaluator<'src, '_>,
modulepath: &Modulepath,
resolved: Vec<Arc<Recipe<'src>>>,
settings: &Settings,
variable_resolver: &VariableResolver<'src, '_>,
) -> CompileResult<'src, Recipe<'src>> {
assert_eq!(
self.dependencies.len(),
resolved.len(),
"UnresolvedRecipe::resolve: dependency count not equal to resolved count: {} != {}",
self.dependencies.len(),
resolved.len()
);
let mut variable_references = HashSet::new();
for i in 0..self.parameters.len() {
let (resolved, rest) = self.parameters.split_at_mut(i);
let parameters = (*resolved).into();
let parameter = &mut rest[0];
if let Some(expression) = &mut parameter.default {
variable_resolver.resolve_expression(expression, ¶meters, &mut variable_references)?;
}
if let Some(expression) = &mut parameter.value {
variable_resolver.resolve_expression(expression, ¶meters, &mut variable_references)?;
}
}
let parameters = self.parameters.as_slice().into();
let empty = ExpressionContext::new();
for dependency in &mut self.dependencies {
if dependency.starred() && !settings.lists {
return Err(
dependency
.recipe
.last()
.error(CompileErrorKind::MappedDependencyWithoutListsSetting),
);
}
for argument in &mut dependency.arguments {
variable_resolver.resolve_expression(
&mut argument.expression,
¶meters,
&mut variable_references,
)?;
}
}
let script = self.is_script(settings);
let attributes = self
.attributes
.into_items()
.map(|(mut attribute, name)| {
match &mut attribute {
Attribute::Arg {
help_property,
pattern_property,
..
} => {
if let Some((_key, expression)) = help_property {
variable_resolver.resolve_expression(expression, &empty, &mut variable_references)?;
}
if let Some((_key, expression)) = pattern_property {
variable_resolver.resolve_expression(expression, &empty, &mut variable_references)?;
}
}
Attribute::Cache {
extra,
inputs,
outputs,
} => {
if let Some(extra) = extra {
variable_resolver.resolve_expression(extra, ¶meters, &mut variable_references)?;
}
if let Some(inputs) = inputs {
variable_resolver.resolve_expression(
inputs,
¶meters,
&mut variable_references,
)?;
}
if let Some(outputs) = outputs {
variable_resolver.resolve_expression(
outputs,
¶meters,
&mut variable_references,
)?;
}
}
Attribute::Confirm(Some(expression))
| Attribute::Timestamp(Some(expression))
| Attribute::WorkingDirectory(expression) => {
variable_resolver.resolve_expression(
expression,
¶meters,
&mut variable_references,
)?;
}
Attribute::Doc(Some(expression)) => {
variable_resolver.resolve_expression(expression, &empty, &mut variable_references)?;
}
Attribute::Env(key, value) => {
variable_resolver.resolve_expression(key, &empty, &mut variable_references)?;
variable_resolver.resolve_expression(value, &empty, &mut variable_references)?;
}
Attribute::Android
| Attribute::Confirm(None)
| Attribute::Continue(_)
| Attribute::Default
| Attribute::Doc(None)
| Attribute::Dragonfly
| Attribute::ExitMessage
| Attribute::Extension(_)
| Attribute::Freebsd
| Attribute::Group(_)
| Attribute::Linux
| Attribute::Macos
| Attribute::Metadata(_)
| Attribute::Netbsd
| Attribute::NoCd
| Attribute::NoExitMessage
| Attribute::NoQuiet
| Attribute::Openbsd
| Attribute::Parallel
| Attribute::PositionalArguments
| Attribute::Private
| Attribute::Script(_)
| Attribute::Shell
| Attribute::Timestamp(None)
| Attribute::Unix
| Attribute::Windows => {}
}
if let Attribute::Doc(Some(expression)) = &attribute {
let value = evaluator
.evaluate_value_const(expression)?
.join()
.trim()
.to_owned();
self.doc = if value.is_empty() { None } else { Some(value) };
}
if let Attribute::Arg {
help,
help_property: Some((_key, expression)),
name: arg,
..
} = &mut attribute
{
let value = evaluator.evaluate_value_const(expression)?;
if !value.is_empty() {
let value = value.join();
self
.parameters
.iter_mut()
.find(|parameter| parameter.name.lexeme() == arg.cooked)
.unwrap()
.help = Some(value.clone());
*help = Some(value);
}
}
if let Attribute::Arg {
name: arg,
pattern,
pattern_property: Some((key, expression)),
..
} = &mut attribute
{
let value = evaluator.evaluate_value_const(expression)?;
if !value.is_empty() {
let compiled = Pattern::new(&value, *key)?;
self
.parameters
.iter_mut()
.find(|parameter| parameter.name.lexeme() == arg.cooked)
.unwrap()
.pattern = Some(compiled.clone());
*pattern = Some(compiled);
}
}
Ok((attribute, name))
})
.collect::<CompileResult<AttributeSet>>()?;
let mut continued = false;
for line in &mut self.body {
if !script && !continued && line.is_comment() && settings.ignore_comments {
continue;
}
for fragment in &mut line.fragments {
if let Fragment::Interpolation { expression, .. } = fragment {
variable_resolver.resolve_expression(
expression,
¶meters,
&mut variable_references,
)?;
}
}
continued = line.is_continuation();
}
for (unresolved, resolved) in self.dependencies.iter().zip(&resolved) {
assert_eq!(unresolved.recipe.last().lexeme(), resolved.name.lexeme());
if !resolved
.argument_range(settings)
.contains(&unresolved.arguments.len())
{
return Err(unresolved.recipe.last().error(
CompileErrorKind::DependencyArgumentCountMismatch {
dependency: unresolved.recipe.clone(),
found: unresolved.arguments.len(),
min: resolved.min_arguments(),
max: resolved.max_arguments(settings),
},
));
}
}
let dependencies = self
.dependencies
.into_iter()
.zip(resolved)
.map(|(unresolved, resolved)| Dependency {
arguments: resolved.group_arguments(&unresolved.arguments, settings),
path: unresolved.recipe,
recipe: resolved,
})
.collect();
let mut recipe_path = modulepath.clone();
recipe_path.components.push(self.name.lexeme().into());
Ok(Recipe {
attributes,
body: self.body,
dependencies,
doc: self.doc,
file_depth: self.file_depth,
import_offsets: self.import_offsets,
module_path: Some(modulepath.clone()),
name: self.name,
number: self.number,
parameters: self.parameters,
priors: self.priors,
private: self.private,
quiet: self.quiet,
recipe_path: Some(recipe_path),
shebang: self.shebang,
variable_references,
})
}
}