use std::collections::HashMap;
use crate::ar;
use crate::sdf::{self, Path, Value};
use super::diagnostics::Diagnostics;
use super::layer_graph::LayerGraph;
use super::{CompositionDiagnostic, ExpressionContext, LayerId, LayerStackId};
#[derive(Debug)]
pub(crate) struct AssetSite {
anchor: Option<ar::ResolvedPath>,
source_layer: String,
stack: LayerStackId,
query_path: Path,
}
impl AssetSite {
pub(super) fn in_graph(graph: &LayerGraph, stack: LayerStackId, layer: LayerId, query_path: &Path) -> Self {
Self {
anchor: graph.anchor_location(Some(layer)),
source_layer: graph.identifier(layer).to_string(),
stack,
query_path: query_path.clone(),
}
}
pub(super) fn in_clip(layer: &sdf::Layer, stack: LayerStackId, query_path: &Path) -> Self {
Self {
anchor: layer.anchor_location(),
source_layer: layer.identifier().to_string(),
stack,
query_path: query_path.clone(),
}
}
fn anchor(&self) -> Option<&ar::ResolvedPath> {
self.anchor.as_ref()
}
fn variables<'graph>(&self, graph: &'graph LayerGraph) -> &'graph HashMap<String, Value> {
graph.stack_expression_variables(self.stack)
}
}
pub(super) fn resolve_values(
graph: &LayerGraph,
value: Value,
site: Option<&AssetSite>,
errors: &mut Diagnostics,
) -> Value {
let mut failures = Vec::new();
let anchor = site.and_then(AssetSite::anchor);
let value = sdf::resolve_asset_paths(
graph.layer_registry(),
anchor,
site.map(|site| site.variables(graph)),
value,
&mut failures,
);
record_failures(site, failures, errors);
value
}
pub(super) fn evaluate_values(
graph: &LayerGraph,
value: Value,
site: Option<&AssetSite>,
errors: &mut Diagnostics,
) -> (Value, sdf::AssetOutcome) {
let mut failures = Vec::new();
let variables = site.map(|site| site.variables(graph));
let (value, outcome) = sdf::evaluate_asset_paths(variables, value, &mut failures);
record_failures(site, failures, errors);
(value, outcome)
}
fn record_failures(site: Option<&AssetSite>, failures: Vec<sdf::AssetExpressionFailure>, errors: &mut Diagnostics) {
let Some(site) = site else {
return;
};
for failure in failures {
errors.report(CompositionDiagnostic::InvalidExpression {
expression: failure.expression,
context: ExpressionContext::AssetValue,
source_layer: site.source_layer.clone(),
site_path: site.query_path.clone(),
message: failure.message,
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expr_asset_without_site() {
let graph = LayerGraph::from_layers(Vec::new(), 0, sdf::LayerRegistry::default());
let mut errors = Diagnostics::default();
let value = Value::AssetPath(sdf::AssetPath::new("`${A}`"));
let resolved = resolve_values(&graph, value, None, &mut errors)
.try_as_asset_path()
.expect("an asset value stays one");
assert_eq!(resolved.as_str(), "`${A}`", "the authored expression is kept");
assert!(resolved.evaluated_path().is_none(), "no evaluated path is derived");
assert!(errors.is_empty(), "no site means no diagnostic");
}
}