Skip to content

Commit

Permalink
Use build script to generate elements with inline JS
Browse files Browse the repository at this point in the history
  • Loading branch information
kmicklas committed Jun 21, 2024
1 parent 4409078 commit 8cf4479
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 37 deletions.
83 changes: 83 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
5 changes: 5 additions & 0 deletions ravel-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
65 changes: 65 additions & 0 deletions ravel-web/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::fmt::Write as _;

use serde::Deserialize;

#[derive(Deserialize)]
struct Config {
element: std::collections::HashMap<String, Element>,
}

#[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: Body) -> {t}<Body> {{ {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::<String>() + cs.as_str(),
}
}
27 changes: 27 additions & 0 deletions ravel-web/generate.toml
Original file line number Diff line number Diff line change
@@ -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 = {}
46 changes: 9 additions & 37 deletions ravel-web/src/el.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<Kind: ElKind, Body: Builder<Web>> Builder<Web> for El<Kind, Body> {
type State = ElState<Body::State>;

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) {
Expand Down Expand Up @@ -64,13 +64,15 @@ pub fn el<Kind: ElKind, Body>(_: Kind, body: Body) -> El<Kind, Body> {
}
}

fn create_element(kind: &'static str) -> web_sys::Element {
gloo_utils::document().create_element(kind).unwrap_throw()
}

fn build_el<Body: Builder<Web>>(
cx: BuildCx,
kind: &'static str,
el: web_sys::Element,
body: Body,
) -> ElState<Body::State> {
let el = gloo_utils::document().create_element(kind).unwrap_throw();

let state = body.build(BuildCx {
position: Position {
parent: &el,
Expand All @@ -88,7 +90,7 @@ fn build_el<Body: Builder<Web>>(
}

macro_rules! make_el {
($name:ident, $t:ident) => {
($name:ident, $t:ident, $create:expr) => {
#[doc = concat!("`", stringify!($name), "` element.")]
#[repr(transparent)]
#[derive(Copy, Clone)]
Expand All @@ -98,7 +100,7 @@ macro_rules! make_el {
type State = ElState<Body::State>;

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) {
Expand All @@ -111,37 +113,7 @@ macro_rules! make_el {
)
}
}

#[doc = concat!("`", stringify!($name), "` element.")]
pub fn $name<Body>(body: Body) -> $t<Body> {
$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"));

0 comments on commit 8cf4479

Please sign in to comment.