use syn::punctuated::Punctuated;
use syn::{token, ExprPath, PathSegment};
use typst::model::Content;
use typst_library::layout::{EnumElem, EnumItem, ListElem, ListItem, ParbreakElem, TableElem};
use typst_library::meta::HeadingElem;
use typst_library::text::{SpaceElem, TextElem};
use crate::literal_string;
#[derive(Debug)]
pub enum SupportedContent<'a> {
Sequence(Vec<SupportedContent<'a>>),
Heading(&'a HeadingElem),
Text(&'a TextElem),
Space(&'a SpaceElem),
Table(&'a TableElem),
UnorderedList(&'a ListElem),
UnorderedListItem(&'a ListItem),
ParagraphBreak(&'a ParbreakElem),
OrderedList(&'a EnumElem),
OrderedListItem(&'a EnumItem),
}
impl<'a> SupportedContent<'a> {
pub fn downcast(value: &'a Content) -> Self {
if let Some(sequence) = value.to_sequence() {
let children = sequence
.map(|child| SupportedContent::downcast(child))
.collect::<Vec<_>>();
Self::Sequence(children)
} else if let Some(heading) = value.to::<HeadingElem>() {
Self::Heading(heading)
} else if let Some(text) = value.to::<TextElem>() {
Self::Text(text)
} else if let Some(space) = value.to::<SpaceElem>() {
Self::Space(space)
} else if let Some(table) = value.to::<TableElem>() {
Self::Table(table)
} else if let Some(list) = value.to::<ListElem>() {
Self::UnorderedList(list)
} else if let Some(list) = value.to::<EnumElem>() {
Self::OrderedList(list)
} else if let Some(item) = value.to::<ListItem>() {
Self::UnorderedListItem(item)
} else if let Some(item) = value.to::<EnumItem>() {
Self::OrderedListItem(item)
} else if let Some(pragraph_break) = value.to::<ParbreakElem>() {
Self::ParagraphBreak(pragraph_break)
} else {
dbg!(value);
todo!()
}
}
pub fn to_xilem(&self) -> Box<dyn Iterator<Item = syn::Expr> + '_> {
match self {
SupportedContent::Sequence(sequence) => {
Box::new(sequence.iter().map(SupportedContent::to_xilem).flatten())
}
SupportedContent::Heading(heading) => {
let body_text = Self::downcast(heading.body());
Box::new(Some(xilem_html_element("h1", body_text.to_xilem())).into_iter())
}
SupportedContent::Text(text) => {
Box::new(Some(syn::Expr::Lit(literal_string(text.text().as_str()))).into_iter())
}
SupportedContent::Space(_space) => {
Box::new(Some(syn::Expr::Lit(literal_string(" "))).into_iter())
}
SupportedContent::Table(table) => Box::new(
Some(xilem_html_element(
"table",
Some(syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
table
.children()
.into_iter()
.map(|content| SupportedContent::downcast(content))
.map(|supported| supported.to_xilem().collect::<Vec<_>>())
.flatten()
.collect::<Vec<_>>()
.chunks(2) .map(|chunk| {
xilem_html_element(
"tr",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
chunk.into_iter().map(|item| item.to_owned()),
),
})],
)
}),
),
})),
))
.into_iter(),
),
SupportedContent::UnorderedList(list) => Box::new(
Some(xilem_html_element(
"ul",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
list.children()
.into_iter()
.map(|content| SupportedContent::downcast(content.body()))
.map(|supported| supported.to_xilem().collect::<Vec<_>>())
.flatten(),
),
})],
))
.into_iter(),
),
SupportedContent::OrderedList(list) => Box::new(
Some(xilem_html_element(
"ol",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
list.children()
.into_iter()
.map(|content| SupportedContent::downcast(content.body()))
.map(|supported| supported.to_xilem().collect::<Vec<_>>())
.flatten(),
),
})],
))
.into_iter(),
),
SupportedContent::UnorderedListItem(item) => Box::new(
Some(xilem_html_element(
"ul",
vec![xilem_html_element(
"li",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
SupportedContent::downcast(item.body()).to_xilem(),
),
})],
)],
))
.into_iter(),
),
SupportedContent::OrderedListItem(item) => Box::new(
Some(xilem_html_element(
"ol",
vec![xilem_html_element(
"li",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::from_iter(
SupportedContent::downcast(item.body()).to_xilem(),
),
})],
)],
))
.into_iter(),
),
SupportedContent::ParagraphBreak(_parahraph_break) => Box::new(
Some(xilem_html_element(
"br",
vec![syn::Expr::Tuple(syn::ExprTuple {
attrs: Vec::new(),
paren_token: token::Paren::default(),
elems: Punctuated::new(),
})],
))
.into_iter(),
),
}
}
}
fn path_segment(segment: &str) -> syn::PathSegment {
syn::PathSegment {
ident: syn::Ident::new(segment, proc_macro2::Span::call_site()),
arguments: syn::PathArguments::None,
}
}
fn expr_path(name: &str) -> syn::Expr {
let mut segments: Punctuated<PathSegment, token::PathSep> = Punctuated::new();
segments.push(path_segment("elements"));
segments.push(path_segment(name));
let expr_path = ExprPath {
attrs: Vec::new(),
qself: None,
path: syn::Path {
leading_colon: None,
segments,
},
};
syn::Expr::Path(expr_path)
}
fn xilem_html_element(name: &str, expressions: impl IntoIterator<Item = syn::Expr>) -> syn::Expr {
syn::Expr::Call(syn::ExprCall {
attrs: Vec::new(),
func: Box::new(expr_path(name)),
paren_token: token::Paren::default(),
args: Punctuated::from_iter(expressions.into_iter()),
})
}