cascading_ui/
lib.rs

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	// if it exists, import .cui files from the `cui` directory and attach them to the input
41	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}