Port example 04
This commit is contained in:
@@ -4,8 +4,8 @@ use quote::{format_ident, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{
|
||||
Error, Expr, FnArg, Ident, ItemFn, Pat, PatIdent, Path, Result, ReturnType, Token, Type,
|
||||
braced, parenthesized, parse_macro_input,
|
||||
Error, Expr, FnArg, Ident, ItemFn, ItemStruct, Pat, PatIdent, Path, Result, ReturnType, Token,
|
||||
Type, braced, parenthesized, parse_macro_input,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
@@ -29,6 +29,16 @@ pub fn view(input: TokenStream) -> TokenStream {
|
||||
expand_node(&root.root).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn context_provider(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let value_ty = parse_macro_input!(attr as Type);
|
||||
let item_struct = parse_macro_input!(item as ItemStruct);
|
||||
match expand_context_provider(item_struct, value_ty) {
|
||||
Ok(tokens) => tokens.into(),
|
||||
Err(error) => error.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_component(mut function: ItemFn) -> Result<proc_macro2::TokenStream> {
|
||||
validate_component_function(&function)?;
|
||||
|
||||
@@ -256,6 +266,34 @@ fn expand_component(mut function: ItemFn) -> Result<proc_macro2::TokenStream> {
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_context_provider(
|
||||
item_struct: ItemStruct,
|
||||
value_ty: Type,
|
||||
) -> Result<proc_macro2::TokenStream> {
|
||||
if !item_struct.generics.params.is_empty() || item_struct.generics.where_clause.is_some() {
|
||||
return Err(Error::new_spanned(
|
||||
&item_struct.generics,
|
||||
"context providers cannot be generic",
|
||||
));
|
||||
}
|
||||
if !matches!(item_struct.fields, syn::Fields::Unit) {
|
||||
return Err(Error::new_spanned(
|
||||
&item_struct.fields,
|
||||
"context providers must be unit structs",
|
||||
));
|
||||
}
|
||||
|
||||
let ident = &item_struct.ident;
|
||||
|
||||
Ok(quote! {
|
||||
#item_struct
|
||||
|
||||
impl ::ruin_app::ContextKey for #ident {
|
||||
type Value = #value_ty;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_component_function(function: &ItemFn) -> Result<()> {
|
||||
let signature = &function.sig;
|
||||
|
||||
@@ -443,6 +481,9 @@ fn looks_like_node(input: ParseStream<'_>) -> Result<bool> {
|
||||
|
||||
fn expand_node(node: &Node) -> proc_macro2::TokenStream {
|
||||
let path = &node.path;
|
||||
if is_provider_path(path) {
|
||||
return expand_provider_node(node);
|
||||
}
|
||||
let prop_calls = node.props.iter().map(|property| {
|
||||
let name = &property.name;
|
||||
let value = &property.value;
|
||||
@@ -465,6 +506,34 @@ fn expand_node(node: &Node) -> proc_macro2::TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_provider_node(node: &Node) -> proc_macro2::TokenStream {
|
||||
let path = &node.path;
|
||||
let value = match node.props.as_slice() {
|
||||
[Property { name, value }] if name == "value" => value,
|
||||
_ => {
|
||||
return Error::new_spanned(
|
||||
path,
|
||||
"provide::<Context>(...) expects exactly one `value = ...` property",
|
||||
)
|
||||
.to_compile_error();
|
||||
}
|
||||
};
|
||||
let child = match node.children.as_slice() {
|
||||
[child] => expand_child(child),
|
||||
_ => {
|
||||
return Error::new_spanned(path, "provide::<Context>(...) requires exactly one child")
|
||||
.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
::ruin_app::#path(
|
||||
#value,
|
||||
|| ::ruin_app::IntoView::into_view(#child),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_children(children: &[Child]) -> proc_macro2::TokenStream {
|
||||
match children {
|
||||
[] => quote! { () },
|
||||
@@ -491,3 +560,12 @@ fn is_component_path(path: &Path) -> bool {
|
||||
.map(|ch| ch.is_ascii_uppercase())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_provider_path(path: &Path) -> bool {
|
||||
!is_component_path(path)
|
||||
&& path
|
||||
.segments
|
||||
.last()
|
||||
.map(|segment| segment.ident == "provide")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user