View plugins

This commit is contained in:
2026-02-01 16:34:27 +01:00
parent d40a032bc1
commit b0eaa76398
3 changed files with 73 additions and 3 deletions
+25 -2
View File
@@ -51,6 +51,20 @@ fn emit_deps_vec(deps: &[Dep]) -> TokenStream {
}
}
/// Emits a Vec of plugins
fn emit_plugins(deps: &[ViewPlugin]) -> TokenStream {
deps.iter()
.map(|d| {
let path = &d.path;
if let Some(args) = &d.args {
quote!( #path(ctx, view #(,#args)*); )
} else {
quote!( #path(ctx, view); )
}
})
.collect()
}
/// Emits code for a single node within a scope
fn emit_node_scope(node: &RsxNode) -> TokenStream {
match node {
@@ -144,6 +158,7 @@ fn emit_node(node: &RsxNode) -> TokenStream {
}
RsxNode::Component {
plugins,
path,
props,
children,
@@ -171,8 +186,16 @@ fn emit_node(node: &RsxNode) -> TokenStream {
}
};
quote! {
scope.child(#component_expr, None);
if plugins.len() == 0 {
quote! {
scope.child(#component_expr, None);
}
} else {
let plugins_emit = emit_plugins(plugins);
quote! {
scope.child(#component_expr, Some(std::sync::Arc::new(|ctx, view| {#plugins_emit})));
}
}
}
+41 -1
View File
@@ -5,13 +5,14 @@
use std::collections::HashSet;
use proc_macro2::Span;
use syn::braced;
use syn::parse::discouraged::Speculative;
use syn::punctuated::Punctuated;
use syn::{
Expr, Ident, LitStr, Pat, Path, Result, Token,
parse::{Parse, ParseStream},
token::Brace,
};
use syn::{braced, parenthesized};
/// Root of an RSX expression
pub struct RsxRoot {
@@ -45,6 +46,8 @@ pub enum RsxNode {
Expr(Expr),
/// Component instantiation: `Component { prop: value, ... }`
Component {
/// View plugins
plugins: Vec<ViewPlugin>,
/// Component path (e.g., `my_module::MyComponent`)
path: Path,
/// Component properties
@@ -82,6 +85,11 @@ pub struct Dep {
pub is_dep: bool,
}
pub struct ViewPlugin {
pub path: Path,
pub args: Option<Vec<Expr>>,
}
fn parse_deps(input: ParseStream) -> Result<Vec<Dep>> {
let mut deps = Vec::new();
@@ -122,6 +130,35 @@ fn parse_deps(input: ParseStream) -> Result<Vec<Dep>> {
Ok(deps)
}
fn parse_plugins(input: ParseStream) -> Result<Vec<ViewPlugin>> {
let mut plugins = Vec::new();
if input.peek(Token![impl]) {
input.parse::<Token![impl]>()?;
loop {
let path: Path = input.parse()?;
let args = if input.peek(syn::token::Paren) {
let content;
parenthesized!(content in input);
let exprs = Punctuated::<Expr, Token![,]>::parse_terminated(&content)?;
Some(exprs.into_iter().collect())
} else {
None
};
plugins.push(ViewPlugin { path, args });
if !input.peek(Token![,]) {
break;
}
input.parse::<Token![,]>()?;
}
}
Ok(plugins)
}
impl Parse for RsxNode {
fn parse(input: ParseStream) -> Result<Self> {
// @{ $expr }
@@ -201,10 +238,12 @@ impl Parse for RsxNode {
/// Parses Path or Path { ... } into RsxNode::Component.
fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> {
let plugins = parse_plugins(input)?;
let path: Path = input.parse()?;
if !input.peek(Brace) {
return Ok(RsxNode::Component {
plugins,
path,
props: Vec::new(),
children: Vec::new(),
@@ -217,6 +256,7 @@ fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> {
let (props, children) = parse_props_and_children(&content)?;
Ok(RsxNode::Component {
plugins,
path,
props,
children,