use super::ir::{ResolvedAttr, ResolvedValue, ShapeKind, VarTable};
use super::styles::StyleTable;
use crate::ast::{BodyItem, ShapeDef, TypeDefaults};
use crate::error::Error;
use crate::span::Span;
use std::collections::HashMap;
const MAX_INHERITANCE_DEPTH: usize = 16;
pub(super) const TEMPLATES: &[(&str, &str)] = &[
("group", "rect"),
("badge", "rect"),
("button", "rect"),
("card", "rect"),
("note", "rect"),
("table", "group"),
("cell", "rect"),
];
pub fn is_template(name: &str) -> bool {
TEMPLATES.iter().any(|(n, _)| *n == name)
}
#[derive(Clone)]
pub struct ResolvedShape {
pub kind: ShapeKind,
pub attrs: Vec<ResolvedAttr>,
pub body_items: Vec<BodyItem>,
pub type_chain: Vec<String>,
}
pub struct ShapesTable {
user: HashMap<String, ResolvedShapeDef>,
type_defaults: HashMap<String, Vec<ResolvedAttr>>,
}
struct ResolvedShapeDef {
base: String,
attrs: Vec<ResolvedAttr>,
body_items: Vec<BodyItem>,
span: Span,
}
impl ShapesTable {
pub fn build(
defs: &[&ShapeDef],
type_defaults: &[&TypeDefaults],
styles: &StyleTable,
vars: &VarTable,
) -> Result<Self, Error> {
let user = build_user_shapes(defs, styles, vars)?;
let type_defaults = build_type_defaults(type_defaults, &user, styles, vars)?;
let table = Self {
user,
type_defaults,
};
for def in defs {
let mut visiting: Vec<String> = Vec::new();
table.walk_chain(&def.name, def.span, &mut visiting, 0)?;
}
Ok(table)
}
pub fn resolve(&self, name: &str, use_span: Span) -> Result<ResolvedShape, Error> {
let mut visiting: Vec<String> = Vec::new();
self.walk_chain(name, use_span, &mut visiting, 0)
}
fn walk_chain(
&self,
name: &str,
use_span: Span,
visiting: &mut Vec<String>,
depth: usize,
) -> Result<ResolvedShape, Error> {
if depth > MAX_INHERITANCE_DEPTH {
return Err(Error::at(
use_span,
format!("'{}' exceeds max inheritance depth (16)", name),
));
}
if visiting.iter().any(|n| n == name) {
let chain = format!("{} -> {}", visiting.join(" -> "), name);
return Err(Error::at(use_span, format!("cycle in '{}'", chain)));
}
if let Some(kind) = ShapeKind::parse(name) {
return Ok(ResolvedShape {
kind,
attrs: self.defaults_for(name),
body_items: Vec::new(),
type_chain: Vec::new(),
});
}
if let Some((_, base)) = TEMPLATES.iter().find(|(n, _)| *n == name) {
visiting.push(name.to_string());
let mut resolved = self.walk_chain(base, use_span, visiting, depth + 1)?;
visiting.pop();
resolved.attrs.extend(template_attrs(name));
resolved.attrs.extend(self.defaults_for(name));
resolved.type_chain.insert(0, name.to_string());
return Ok(resolved);
}
let def = self
.user
.get(name)
.ok_or_else(|| Error::at(use_span, format!("unknown type '|{}|'", name)))?;
visiting.push(name.to_string());
let base = self.walk_chain(&def.base, def.span, visiting, depth + 1)?;
visiting.pop();
let mut attrs = base.attrs;
attrs.extend(self.defaults_for(name));
attrs.extend(def.attrs.iter().cloned());
let mut body_items = base.body_items;
body_items.extend(def.body_items.iter().cloned());
let mut type_chain = base.type_chain;
type_chain.insert(0, name.to_string());
Ok(ResolvedShape {
kind: base.kind,
attrs,
body_items,
type_chain,
})
}
fn defaults_for(&self, name: &str) -> Vec<ResolvedAttr> {
self.type_defaults.get(name).cloned().unwrap_or_default()
}
pub fn type_default_attrs(&self, name: &str) -> Option<&[ResolvedAttr]> {
self.type_defaults.get(name).map(Vec::as_slice)
}
pub fn own_attrs(&self, name: &str) -> Option<&[ResolvedAttr]> {
self.user.get(name).map(|d| d.attrs.as_slice())
}
}
pub(super) fn template_attrs(name: &str) -> Vec<ResolvedAttr> {
if name != "group" {
return Vec::new();
}
let live = |var: &str| ResolvedValue::LiveVar {
name: var.into(),
raw: false,
baked: None,
};
let attr = |name: &str, value: ResolvedValue| ResolvedAttr {
name: name.into(),
value,
span: Span::empty(),
};
vec![
attr("stroke", live("group-stroke")),
attr("fill", live("group-fill")),
attr("radius", ResolvedValue::Number(6.0)),
attr("padding", ResolvedValue::Number(10.0)),
]
}
fn build_user_shapes(
defs: &[&ShapeDef],
styles: &StyleTable,
vars: &VarTable,
) -> Result<HashMap<String, ResolvedShapeDef>, Error> {
let mut user: HashMap<String, ResolvedShapeDef> = HashMap::new();
for def in defs {
if super::is_reserved(&def.name) {
return Err(super::reserved_error(def.span, &def.name));
}
if ShapeKind::parse(&def.name).is_some() {
return Err(Error::at(
def.span,
format!("'{}' shadows a built-in primitive", def.name),
));
}
if is_template(&def.name) {
return Err(Error::at(
def.span,
format!("'{}' shadows a built-in template", def.name),
));
}
if user.contains_key(&def.name) {
return Err(Error::at(
def.span,
format!("duplicate shape '{}'", def.name),
));
}
let attrs = super::resolve_attrs(&def.items, styles, vars)?;
let body_items = def.body.clone().unwrap_or_default();
user.insert(
def.name.clone(),
ResolvedShapeDef {
base: def.base.name.clone(),
attrs,
body_items,
span: def.span,
},
);
}
Ok(user)
}
fn build_type_defaults(
entries: &[&TypeDefaults],
user: &HashMap<String, ResolvedShapeDef>,
styles: &StyleTable,
vars: &VarTable,
) -> Result<HashMap<String, Vec<ResolvedAttr>>, Error> {
let mut out: HashMap<String, Vec<ResolvedAttr>> = HashMap::new();
for entry in entries {
if !is_known_type(&entry.name, user) {
return Err(Error::at(
entry.span,
format!(
"unknown type '|{}|' in defs (no such primitive, template, or shape)",
entry.name
),
));
}
if out.contains_key(&entry.name) {
return Err(Error::at(
entry.span,
format!("duplicate type-defaults entry '|{}|'", entry.name),
));
}
let attrs = super::resolve_attrs(&entry.items, styles, vars)?;
out.insert(entry.name.clone(), attrs);
}
Ok(out)
}
fn is_known_type(name: &str, user: &HashMap<String, ResolvedShapeDef>) -> bool {
ShapeKind::parse(name).is_some() || is_template(name) || user.contains_key(name)
}