use std::collections::{BTreeMap, BTreeSet};
use crate::ast::document::Document;
use crate::ast::token::TokenType;
use crate::diagnostics::Diagnostic;
pub(in crate::validate::check) fn check_recipes(
doc: &Document,
page_ids: &BTreeSet<&str>,
all_node_ids: &BTreeSet<String>,
token_type_map: &BTreeMap<&str, &TokenType>,
diagnostics: &mut Vec<Diagnostic>,
) {
let mut seen_recipe_ids: BTreeSet<&str> = BTreeSet::new();
for recipe in &doc.recipes {
if !seen_recipe_ids.insert(recipe.id.as_str()) {
diagnostics.push(Diagnostic::error(
"recipe.duplicate_id",
format!(
"recipe '{}': id is declared more than once; \
recipe ids must be unique within the recipes block",
recipe.id
),
recipe.source_span,
Some(recipe.id.clone()),
));
}
for token_id in &recipe.palette {
let msg = match token_type_map.get(token_id.as_str()) {
None => Some(format!(
"recipe '{}': palette token '{}' is not declared in the tokens block",
recipe.id, token_id
)),
Some(TokenType::Color) => None,
Some(TokenType::Dimension) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'dimension', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Number) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'number', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::FontFamily) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'fontFamily', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::FontWeight) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'fontWeight', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Gradient) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'gradient', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Shadow) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'shadow', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Filter) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'filter', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Mask) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type 'mask', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id
)),
Some(TokenType::Unknown(type_name)) => Some(format!(
"recipe '{}': palette token '{}' is declared but has type '{}', \
not 'color'; palette entries must be color tokens",
recipe.id, token_id, type_name
)),
};
if let Some(msg) = msg {
diagnostics.push(Diagnostic::error(
"recipe.unknown_palette_token",
msg,
recipe.source_span,
Some(recipe.id.clone()),
));
}
}
for node_id in &recipe.expanded {
if !all_node_ids.contains(node_id.as_str()) {
diagnostics.push(Diagnostic::error(
"recipe.unknown_expanded_node",
format!(
"recipe '{}': expanded node '{}' does not exist anywhere in this document",
recipe.id, node_id
),
recipe.source_span,
Some(recipe.id.clone()),
));
}
}
if let Some(bounds_id) = &recipe.bounds {
let known_as_page = page_ids.contains(bounds_id.as_str());
let known_as_node = all_node_ids.contains(bounds_id.as_str());
if !known_as_page && !known_as_node {
diagnostics.push(Diagnostic::error(
"recipe.unknown_bounds",
format!(
"recipe '{}': bounds '{}' does not reference a declared page \
or node id in this document",
recipe.id, bounds_id
),
recipe.source_span,
Some(recipe.id.clone()),
));
}
}
}
}