use sim_kernel::{Expr, Symbol};
pub const ARTICLE_CLASS: &str = "doc/Article";
pub const BLOCK_KEY: &str = "block";
fn key(name: &str) -> Expr {
Expr::Symbol(Symbol::new(name))
}
fn map(entries: Vec<(&str, Expr)>) -> Expr {
Expr::Map(entries.into_iter().map(|(k, v)| (key(k), v)).collect())
}
pub fn field<'a>(value: &'a Expr, name: &str) -> Option<&'a Expr> {
let Expr::Map(entries) = value else {
return None;
};
entries.iter().find_map(|(entry_key, entry_value)| {
matches!(entry_key, Expr::Symbol(symbol) if &*symbol.name == name && symbol.namespace.is_none())
.then_some(entry_value)
})
}
pub fn article(title: &str, blocks: Vec<Expr>) -> Expr {
map(vec![
("class", Expr::Symbol(Symbol::qualified("doc", "Article"))),
("title", Expr::String(title.to_owned())),
("blocks", Expr::List(blocks)),
])
}
pub fn title(doc: &Expr) -> Option<String> {
match field(doc, "title") {
Some(Expr::String(text)) => Some(text.clone()),
_ => None,
}
}
pub fn blocks(doc: &Expr) -> Vec<Expr> {
match field(doc, "blocks") {
Some(Expr::List(items)) => items.clone(),
_ => Vec::new(),
}
}
pub fn block_kind(block: &Expr) -> Option<Symbol> {
match field(block, BLOCK_KEY) {
Some(Expr::Symbol(symbol)) => Some(symbol.clone()),
_ => None,
}
}
fn block(kind: &str, mut entries: Vec<(&str, Expr)>) -> Expr {
let mut pairs = vec![(BLOCK_KEY, Expr::Symbol(Symbol::new(kind)))];
pairs.append(&mut entries);
map(pairs)
}
pub fn section(heading: &str) -> Expr {
block("section", vec![("title", Expr::String(heading.to_owned()))])
}
pub fn prose(text: &str) -> Expr {
block("prose", vec![("text", Expr::String(text.to_owned()))])
}
pub fn equation(source: &str) -> Expr {
block("equation", vec![("tex", Expr::String(source.to_owned()))])
}
pub fn figure(caption: &str, src: &str) -> Expr {
block(
"figure",
vec![
("caption", Expr::String(caption.to_owned())),
("src", Expr::String(src.to_owned())),
],
)
}
pub fn table(rows: Vec<Vec<Expr>>) -> Expr {
let rows = rows.into_iter().map(Expr::List).collect();
block("table", vec![("rows", Expr::List(rows))])
}
pub fn citation(cite_key: &str, text: &str) -> Expr {
block(
"citation",
vec![
("key", Expr::Symbol(Symbol::new(cite_key))),
("text", Expr::String(text.to_owned())),
],
)
}
pub fn embed_block(value: Expr, lens: &str) -> Expr {
block(
"embed",
vec![("value", value), ("lens", Expr::Symbol(Symbol::new(lens)))],
)
}