1use markup5ever::{local_name, namespace_url, ns};
2use quote::quote;
3use syn::parse2;
4
5use yarte_hir::{Each as HEach, IfElse as HIfElse, Struct, HIR};
6use yarte_html::{
7 interface::{QualName, YName},
8 serializer::SerializerOpt,
9 utils::MARK,
10 y_name,
11};
12
13use crate::{
14 serialize::serialize,
15 sink::{parse_document, parse_fragment, ParseAttribute, ParseElement, ParseResult, Sink},
16};
17
18pub struct DOMFmt(pub Vec<HIR>);
19
20impl From<Vec<HIR>> for DOMFmt {
22 fn from(ir: Vec<HIR>) -> Self {
23 DOMFmt(to_domfmt_init(ir).expect("correct html"))
24 }
25}
26
27const HASH: &str = "0x00000000";
28
29fn get_html(ir: &[HIR]) -> String {
30 let mut html = String::new();
31 for x in ir {
32 match x {
33 HIR::Lit(x) => html.push_str(x),
34 _ => {
35 html.push_str(MARK);
36 html.push_str(HASH);
37 }
38 }
39 }
40
41 html
42}
43
44pub fn to_wasmfmt(mut ir: Vec<HIR>, s: &Struct) -> ParseResult<Vec<HIR>> {
45 let html = get_html(&ir);
46 let sink = match parse_document(&html) {
47 Ok(mut sink) => {
48 add_scripts(s, &mut sink, &mut ir);
49 sink
50 }
51 Err(_) => parse_fragment(&html)?,
52 };
53
54 serialize_domfmt(sink, ir, SerializerOpt { wasm: true })
55}
56
57fn add_scripts(s: &Struct, sink: &mut Sink, ir: &mut Vec<HIR>) {
58 let mut body: Option<usize> = None;
59 use ParseElement::*;
60 match sink.nodes.values().next() {
61 Some(Document(children)) => {
62 if let Some(Node { name, children, .. }) = sink.nodes.get(&children[0]) {
63 if let y_name!("html") = name.local {
64 for i in children {
65 if let Some(Node { name, .. }) = sink.nodes.get(i) {
66 if let y_name!("body") = name.local {
67 body = Some(*i);
68 }
69 }
70 }
71 }
72 }
73 }
74 _ => panic!("Need <!doctype html>"),
75 }
76
77 let mut last = *sink.nodes.keys().last().unwrap() + 1;
78 let get_state = format!(
79 "function get_state(){{return JSON.stringify({}{});}}",
80 MARK, HASH
81 );
82
83 ir.push(HIR::Safe(Box::new(
84 parse2(quote!(yarte::Json(&self))).unwrap(),
85 )));
86
87 let state = Node {
88 name: QualName {
89 prefix: None,
90 ns: ns!(html),
91 local: y_name!("script"),
92 },
93 attrs: vec![],
94 children: vec![last],
95 parent: None,
96 };
97 sink.nodes.insert(last, Text(get_state));
98 last += 1;
99 sink.nodes.insert(last, state);
100 let state = last;
101 last += 1;
102
103 let init_s = format!(
104 "import init from '{}';async function run(){{await init()}}run()",
105 s.script.as_ref().expect("Need `script` attribute")
106 );
107 let init = Node {
108 name: QualName {
109 prefix: None,
110 ns: ns!(html),
111 local: y_name!("script"),
112 },
113 attrs: vec![ParseAttribute {
114 name: QualName {
115 prefix: None,
116 ns: ns!(),
117 local: y_name!("type"),
118 },
119 value: "module".to_string(),
120 }],
121 children: vec![last],
122 parent: None,
123 };
124 sink.nodes.insert(last, Text(init_s));
125 last += 1;
126 sink.nodes.insert(last, init);
127 match sink.nodes.get_mut(&body.expect("body defined")).unwrap() {
128 Node { children, .. } => {
129 children.push(state);
130 children.push(last);
131 }
132 _ => unreachable!(),
133 }
134}
135
136fn to_domfmt_init(ir: Vec<HIR>) -> ParseResult<Vec<HIR>> {
137 let html = get_html(&ir);
138 let sink = match parse_document(&html) {
139 Ok(a) => a,
140 Err(_) => parse_fragment(&html)?,
141 };
142
143 serialize_domfmt(sink, ir, Default::default())
144}
145
146fn to_domfmt(ir: Vec<HIR>, opts: SerializerOpt) -> ParseResult<Vec<HIR>> {
147 let html = get_html(&ir);
148 serialize_domfmt(parse_fragment(&html)?, ir, opts)
149}
150
151fn serialize_domfmt(sink: Sink, mut ir: Vec<HIR>, opts: SerializerOpt) -> ParseResult<Vec<HIR>> {
152 let mut writer = Vec::new();
153 serialize(&mut writer, sink.into(), opts).expect("some serialize node");
154
155 let html = String::from_utf8(writer).expect("");
156 let mut chunks = html.split(MARK).peekable();
157
158 if let Some(first) = chunks.peek() {
159 if first.is_empty() {
160 chunks.next();
161 }
162 }
163 let mut ir = ir.drain(..).filter(|x| !matches!(x, HIR::Lit(_)));
164
165 let mut buff = vec![];
166 for chunk in chunks {
167 if chunk.is_empty() {
168 panic!("chunk empty")
169 } else if let Some(cut) = chunk.strip_prefix(HASH) {
170 resolve_node(ir.next().expect("Some HIR expression"), &mut buff, opts)?;
171 if !cut.is_empty() {
172 buff.push(HIR::Lit(cut.into()));
173 }
174 } else {
175 buff.push(HIR::Lit(chunk.into()));
176 }
177 }
178
179 assert!(ir.next().is_none());
181
182 Ok(buff)
183}
184
185fn resolve_node(ir: HIR, buff: &mut Vec<HIR>, opts: SerializerOpt) -> ParseResult<()> {
186 match ir {
187 HIR::Each(each) => {
188 let HEach { args, body, expr } = *each;
189 buff.push(HIR::Each(Box::new(HEach {
190 args,
191 expr,
192 body: to_domfmt(body, opts)?,
193 })))
194 }
195 HIR::IfElse(if_else) => {
196 let HIfElse { ifs, if_else, els } = *if_else;
197 let mut buf_if_else = vec![];
198 for (expr, body) in if_else {
199 buf_if_else.push((expr, to_domfmt(body, opts)?));
200 }
201 let els = if let Some(els) = els {
202 Some(to_domfmt(els, opts)?)
203 } else {
204 None
205 };
206 buff.push(HIR::IfElse(Box::new(HIfElse {
207 ifs: (ifs.0, to_domfmt(ifs.1, opts)?),
208 if_else: buf_if_else,
209 els,
210 })));
211 }
212 HIR::Lit(_) => panic!("Need some node"),
213 ir => buff.push(ir),
214 }
215 Ok(())
216}