use crate::template::Node;
use serde_json::Value;
use std::collections::BTreeMap;
use std::path::Path;
pub type Binding = BTreeMap<String, String>;
pub fn expand(params: &[String], page: &[Node], base: &Path) -> Vec<Binding> {
let mut bindings = vec![Binding::new()];
for param in params {
let values = match collection_dir(page, param) {
Some(dir) => slugs_in(&base.join(dir)),
None => Vec::new(),
};
bindings = bindings
.iter()
.flat_map(|base_binding| {
values.iter().map(move |value| {
let mut next = base_binding.clone();
next.insert(param.clone(), value.clone());
next
})
})
.collect();
}
bindings
}
fn collection_dir(nodes: &[Node], param: &str) -> Option<String> {
let marker = format!("${}", param);
find_data_from(nodes, &marker).map(|from| {
let prefix = from.split(&marker).next().unwrap_or("");
let dir = prefix.trim_end_matches('/');
if dir.is_empty() { ".".to_string() } else { dir.to_string() }
})
}
fn find_data_from(nodes: &[Node], marker: &str) -> Option<String> {
for node in nodes {
match node {
Node::VoidElement { name, attrs } if name == "data" => {
if let Some(from) = attrs.get("from") {
if from.contains(marker) {
return Some(from.clone());
}
}
}
Node::Element { children, .. } => {
if let Some(found) = find_data_from(children, marker) {
return Some(found);
}
}
_ => {}
}
}
None
}
fn slugs_in(dir: &Path) -> Vec<String> {
match crate::features::load_data_dir(dir) {
Ok(Value::Array(items)) => items
.iter()
.filter_map(|item| item.get("slug")?.as_str().map(String::from))
.collect(),
_ => Vec::new(),
}
}
pub fn route_params(template_path: &str) -> Vec<String> {
let mut params = Vec::new();
let mut rest = template_path;
while let Some(open) = rest.find('[') {
let after = &rest[open + 1..];
let Some(close) = after.find(']') else { break };
let name = after[..close].trim_start_matches("...");
params.push(name.to_string());
rest = &after[close + 1..];
}
params
}
pub fn output_path(template_path: &str, binding: &Binding) -> String {
let mut stem = template_path
.strip_prefix("pages/")
.unwrap_or(template_path)
.trim_end_matches(".hrml")
.trim_end_matches(".trml")
.to_string();
for (param, value) in binding {
stem = stem
.replace(&format!("[...{}]", param), value)
.replace(&format!("[{}]", param), value);
}
format!("{}.html", stem)
}