component generator

This commit is contained in:
2026-01-30 19:29:52 +01:00
parent 1f64b4f6e2
commit e85257a10e
6 changed files with 125 additions and 32 deletions
+8 -10
View File
@@ -17,7 +17,7 @@ pub fn main() {
let handle = thread::spawn(move || {
let res = engine
.run(BenchmarkApp(items))
.run(BenchmarkApp { items })
.expect("Failed to run engine");
tx.send((items, res)).expect("Failed to send result");
@@ -41,16 +41,14 @@ pub fn main() {
}
}
struct BenchmarkApp(pub usize);
#[component]
fn BenchmarkApp(cx: &Arc<Context>, items: &usize) -> View {
let items = items.clone();
impl ComponentImpl for BenchmarkApp {
fn call(&self, cx: &Arc<Context>) -> View {
let items = self.0.clone();
rsx! {
for _ in (0..items) {
"Hello, world!"
}
rsx! {
for _ in (0..items) {
"Hello, world!"
}
.view(cx.clone())
}
.view(cx.clone())
}
+6 -9
View File
@@ -4,16 +4,13 @@ use osui::prelude::*;
pub fn main() {
let engine = Console::new();
engine.run(App).expect("Failed to run engine");
engine.run(App {}).expect("Failed to run engine");
}
struct App;
impl ComponentImpl for App {
fn call(&self, cx: &Arc<Context>) -> View {
rsx! {
"Hello, world!"
}
.view(cx.clone())
#[component]
fn App(cx: &Arc<Context>) -> View {
rsx! {
"Hello World"
}
.view(cx.clone())
}
+6 -9
View File
@@ -4,7 +4,7 @@ use osui::prelude::*;
pub fn main() {
let engine = Benchmark::new(Console::new());
let benchmark_result = engine.run(App).expect("Failed to run engine");
let benchmark_result = engine.run(App {}).expect("Failed to run engine");
println!("Avg: {} μs", benchmark_result.average);
println!("Min: {} μs", benchmark_result.min);
println!("Max: {} μs", benchmark_result.max);
@@ -12,13 +12,10 @@ pub fn main() {
println!("Tot Render: {} μs", benchmark_result.total_render);
}
struct App;
impl ComponentImpl for App {
fn call(&self, cx: &Arc<Context>) -> View {
rsx! {
"Hello, world!"
}
.view(cx.clone())
#[component]
fn App(cx: &Arc<Context>) -> View {
rsx! {
"Hello, world!"
}
.view(cx.clone())
}
+2 -2
View File
@@ -51,14 +51,14 @@ fn emit_node_scope(node: &RsxNode) -> TokenStream {
RsxNode::Text(_) => {
let emit = emit_node(node);
quote! {
r.static_scope(|scope| {#emit});
r.static_scope(move |scope| {#emit});
}
}
RsxNode::Component(_) => {
let emit = emit_node(node);
quote! {
r.static_scope(|scope| {#emit});
r.static_scope(move |scope| {#emit});
}
}
+102 -1
View File
@@ -1,10 +1,111 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{FnArg, ItemFn, Pat, ReturnType, Type, parse_macro_input};
mod emit;
mod parse;
#[proc_macro]
pub fn rsx(input: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(input as parse::RsxRoot);
let ast = parse_macro_input!(input as parse::RsxRoot);
emit::emit_rsx(ast).into()
}
#[proc_macro_attribute]
pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let name = &input.sig.ident;
let vis = &input.vis;
let body = &input.block;
let return_ty = match &input.sig.output {
ReturnType::Type(_, ty) => ty,
_ => {
return syn::Error::new_spanned(&input.sig, "component must return View")
.to_compile_error()
.into();
}
};
let mut inputs = input.sig.inputs.iter();
// ---- First param must be cx ----
let cx = match inputs.next() {
Some(FnArg::Typed(pat)) => pat,
_ => {
return syn::Error::new_spanned(&input.sig, "first argument must be cx: &Arc<Context>")
.to_compile_error()
.into();
}
};
let cx_ident = match &*cx.pat {
Pat::Ident(id) => &id.ident,
_ => unreachable!(),
};
let cx_ty = &cx.ty;
// ---- Remaining params are props ----
let mut struct_fields = Vec::new();
let mut render_params = Vec::new();
let mut call_args = Vec::new();
for arg in inputs {
let FnArg::Typed(pat) = arg else { continue };
let ident = match &*pat.pat {
Pat::Ident(id) => &id.ident,
_ => {
return syn::Error::new_spanned(pat, "unsupported prop pattern")
.to_compile_error()
.into();
}
};
// Strip leading &
let owned_ty = match &*pat.ty {
Type::Reference(r) => &r.elem,
ty => ty,
};
struct_fields.push(quote! {
pub #ident: #owned_ty
});
render_params.push(quote! {
#ident: &#owned_ty
});
call_args.push(quote! {
&self.#ident
});
}
let expanded = quote! {
#vis struct #name {
#(#struct_fields,)*
}
impl #name {
pub fn component(
#cx_ident: #cx_ty,
#(#render_params,)*
) -> #return_ty {
#body
}
}
impl ComponentImpl for #name {
fn call(&self, cx: &std::sync::Arc<Context>) -> #return_ty {
Self::component(
cx,
#(#call_args,)*
)
}
}
};
expanded.into()
}
+1 -1
View File
@@ -14,7 +14,7 @@ pub mod prelude {
pub use crate::render::*;
pub use crate::state::*;
pub use crate::View;
pub use osui_macros::rsx;
pub use osui_macros::{component, rsx};
}
pub type View = Arc<dyn Fn(&mut DrawContext) + Send + Sync>;