diff --git a/Cargo.lock b/Cargo.lock index e233e89..070e1e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "futures-micro" version = "0.5.0" @@ -72,6 +78,22 @@ dependencies = [ "web-sys", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.11" @@ -93,6 +115,12 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + [[package]] name = "once_cell" version = "1.19.0" @@ -145,6 +173,9 @@ dependencies = [ "gloo-events", "gloo-utils", "ravel", + "serde", + "toml", + "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] @@ -186,6 +217,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + [[package]] name = "syn" version = "2.0.66" @@ -208,6 +248,40 @@ dependencies = [ "web-sys", ] +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tutorial" version = "0.1.0" @@ -301,3 +375,12 @@ dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 4a3e5e6..c649ee7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,6 @@ log = "0.4.21" paste = "1.0.15" ravel = { version = "0.2.0", path = "./ravel" } ravel-web = { version = "0.3.0", path = "./ravel-web" } +wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" web-sys = "0.3.69" diff --git a/ravel-web/Cargo.toml b/ravel-web/Cargo.toml index 4110b50..90129fe 100644 --- a/ravel-web/Cargo.toml +++ b/ravel-web/Cargo.toml @@ -12,5 +12,10 @@ futures-micro.workspace = true gloo-events.workspace = true gloo-utils.workspace = true ravel.workspace = true +wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true web-sys = { workspace = true, features = ["Node", "Element", "Text", "Comment"] } + +[build-dependencies] +serde = { version = "1.0.203", features = ["derive"] } +toml = "0.8.14" diff --git a/ravel-web/build.rs b/ravel-web/build.rs new file mode 100644 index 0000000..e8e2128 --- /dev/null +++ b/ravel-web/build.rs @@ -0,0 +1,65 @@ +use std::fmt::Write as _; + +use serde::Deserialize; + +#[derive(Deserialize)] +struct Config { + element: std::collections::HashMap, +} + +#[derive(Deserialize)] +struct Element { + // TODO: JS element type +} + +fn main() { + let config = std::fs::read_to_string("generate.toml").unwrap(); + let config: Config = toml::from_str(&config).unwrap(); + + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + let out_dir = std::path::PathBuf::from(out_dir); + + let mut src = String::new(); + + src.push_str("#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#\"\n"); + + for name in config.element.keys() { + writeln!(&mut src, "export function create_{name}() {{return document.createElement(\"{name}\")}}").unwrap(); + } + + src.push_str("\"#)]\n"); + src.push_str("extern \"C\" {\n"); + + for (name, Element {}) in &config.element { + writeln!(&mut src, "fn create_{name}() -> web_sys::Element;").unwrap(); + } + + src.push_str("}\n"); + + for name in config.element.keys() { + let t = title_case(name); + writeln!(&mut src, "make_el!({name}, {t}, create_{name}());").unwrap(); + + // Ideally this would be part of `make_el`, but rust-analyzer can't + // seem to handle doc attributes generated by a macro generated by a + // build script. + writeln!(&mut src, "/// `{name}` element.").unwrap(); + writeln!( + &mut src, + "pub fn {name}(body: Body) -> {t} {{ {t}(body) }}" + ) + .unwrap(); + } + + std::fs::write(out_dir.join("el_gen.rs"), src).unwrap(); + + println!("cargo::rerun-if-changed=generate.toml"); +} + +fn title_case(s: &str) -> String { + let mut cs = s.chars(); + match cs.next() { + None => String::new(), + Some(c) => c.to_uppercase().collect::() + cs.as_str(), + } +} diff --git a/ravel-web/generate.toml b/ravel-web/generate.toml new file mode 100644 index 0000000..ee03c67 --- /dev/null +++ b/ravel-web/generate.toml @@ -0,0 +1,27 @@ +[element] +a = {} +b = {} +button = {} +div = {} +footer = {} +form = {} +h1 = {} +h2 = {} +h3 = {} +h4 = {} +h5 = {} +h6 = {} +header = {} +input = {} +label = {} +li = {} +p = {} +section = {} +span = {} +strong = {} +table = {} +tbody = {} +td = {} +thead = {} +tr = {} +ul = {} diff --git a/ravel-web/src/el.rs b/ravel-web/src/el.rs index b83acc9..66edcdb 100644 --- a/ravel-web/src/el.rs +++ b/ravel-web/src/el.rs @@ -25,7 +25,7 @@ impl> Builder for El { type State = ElState; fn build(self, cx: BuildCx) -> Self::State { - build_el(cx, Kind::NAME, self.body) + build_el(cx, create_element(Kind::NAME), self.body) } fn rebuild(self, cx: RebuildCx, state: &mut Self::State) { @@ -64,13 +64,15 @@ pub fn el(_: Kind, body: Body) -> El { } } +fn create_element(kind: &'static str) -> web_sys::Element { + gloo_utils::document().create_element(kind).unwrap_throw() +} + fn build_el>( cx: BuildCx, - kind: &'static str, + el: web_sys::Element, body: Body, ) -> ElState { - let el = gloo_utils::document().create_element(kind).unwrap_throw(); - let state = body.build(BuildCx { position: Position { parent: &el, @@ -88,7 +90,7 @@ fn build_el>( } macro_rules! make_el { - ($name:ident, $t:ident) => { + ($name:ident, $t:ident, $create:expr) => { #[doc = concat!("`", stringify!($name), "` element.")] #[repr(transparent)] #[derive(Copy, Clone)] @@ -98,7 +100,7 @@ macro_rules! make_el { type State = ElState; fn build(self, cx: BuildCx) -> Self::State { - build_el(cx, stringify!($name), self.0) + build_el(cx, $create, self.0) } fn rebuild(self, cx: RebuildCx, state: &mut Self::State) { @@ -111,37 +113,7 @@ macro_rules! make_el { ) } } - - #[doc = concat!("`", stringify!($name), "` element.")] - pub fn $name(body: Body) -> $t { - $t(body) - } }; } -make_el!(a, A); -make_el!(b, B); -make_el!(button, Button); -make_el!(div, Div); -make_el!(footer, Footer); -make_el!(form, Form); -make_el!(h1, H1); -make_el!(h2, H2); -make_el!(h3, H3); -make_el!(h4, H4); -make_el!(h5, H5); -make_el!(h6, H6); -make_el!(header, Header); -make_el!(input, Input); -make_el!(label, Label); -make_el!(li, Li); -make_el!(p, P); -make_el!(section, Section); -make_el!(span, Span); -make_el!(strong, Strong); -make_el!(table, Table); -make_el!(tbody, TBody); -make_el!(thead, THead); -make_el!(td, Td); -make_el!(tr, Tr); -make_el!(ul, Ul); +include!(concat!(env!("OUT_DIR"), "/el_gen.rs"));