1extern crate log;
2extern crate phf;
3extern crate proc_macro;
4extern crate proc_macro2;
5extern crate quote;
6extern crate simple_logger;
7extern crate syn;
8mod data;
9mod misc;
10mod transform;
11
12use {
13 data::{ast::Document, semantics::Semantics},
14 log::LevelFilter,
15 proc_macro::TokenStream,
16 proc_macro2::TokenStream as TokenStream2,
17 quote::{quote, ToTokens},
18 simple_logger::SimpleLogger,
19 std::{
20 fs::{create_dir_all, read_dir, read_to_string, write},
21 path::Path,
22 },
23 syn::parse_macro_input,
24};
25
26fn pipeline(document: Document) -> (String, TokenStream2) {
27 let mut semantics = document.analyze();
28 semantics.render();
29 (semantics.html().0, semantics.wasm(true))
30}
31
32#[proc_macro]
33pub fn cui(input: TokenStream) -> TokenStream {
34 SimpleLogger::new()
35 .with_level(LevelFilter::Debug)
36 .init()
37 .unwrap();
38 let mut input = input.into();
39
40 let path = "./cui";
42 if Path::new(path).exists() {
43 for entry in read_dir(path).expect(&*format!("reading from {}", path)) {
44 let entry = entry.expect("reading .cui file");
45 let filename = entry.path().display().to_string();
46 if filename.ends_with(".cui") {
47 let contents: TokenStream2 = read_to_string(entry.path()).unwrap().parse().unwrap();
48 contents.to_tokens(&mut input);
49 }
50 }
51 }
52
53 let input = input.into();
54 let (html, runtime) = pipeline(parse_macro_input!(input as Document));
55 let destination = "target/html/index.html";
56 create_dir_all("target/html").expect("unable to create target/html directory");
57 write(destination, html).expect(&*format!("writing output html code to {}", destination));
58 write("target/cui_macro_output.rs", runtime.to_string()).expect("writing output rust code");
59
60 runtime.into()
61}
62
63#[proc_macro]
64pub fn test_setup(input: TokenStream) -> TokenStream {
65 if SimpleLogger::new()
66 .with_level(LevelFilter::Error)
67 .init()
68 .is_ok()
69 {}
70
71 let document = parse_macro_input!(input as Document);
72 let mut semantics = document.analyze();
73 semantics.render();
74
75 let (pages, styles) = semantics.html_parts();
76 let content = pages.get("/").unwrap();
77
78 let wasm = semantics.wasm(false);
79 let wasm = quote! {
80 let window = web_sys::window().expect("getting window");
81 let document = &window.document().expect("getting `window.document`");
82 let head = &document.head().expect("getting `window.document.head`");
83 let body = &document.body().expect("getting `window.document.body`");
84 {
85 let style = document
86 .create_element("style")
87 .unwrap()
88 .dyn_into::<HtmlElement>()
89 .unwrap();
90 style.set_inner_text(#styles);
91 head.append_child(&style).unwrap();
92
93 let root = document.create_element("div").unwrap();
94 body.prepend_with_node_1(&root).unwrap();
95 root.set_outer_html(#content);
96 }
97
98 {
99 #wasm
100 }
101
102 let root = body
103 .first_child()
104 .expect("body should contain the root node")
105 .dyn_into::<HtmlElement>()
106 .expect("the root node should be an element");
107 };
108
109 log::debug!("***************************");
110 log::debug!("{}", wasm);
111 log::debug!("***************************");
112
113 wasm.into()
114}
115
116#[proc_macro]
117pub fn test_header(_input: TokenStream) -> TokenStream {
118 let header = Semantics::runtime();
119 quote! {
120 #header
121 thread_local! {
122 static STATE: RefCell<Vec<Value>> = RefCell::new(vec![]);
123 }
124 }
125 .into()
126}