WIP: add extends to the props macro
This commit is contained in:
1 parent
56b1a0aefc
commit
7b51bb8060
6 files changed
+270
-151
No files matched your search
+13
-56
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::dioxus_elements::ExtendedDivMarker;
|
||||||
use dioxus::{
|
use dioxus::{
|
||||||
core::{exports::bumpalo::Bump, Attribute, HasAttributesBox},
|
core::{exports::bumpalo::Bump, Attribute, HasAttributesBox},
|
||||||
html::{ExtendedGlobalAttributesMarker, GlobalAttributesExtension},
|
html::{ExtendedGlobalAttributesMarker, GlobalAttributesExtension},
|
||||||
@@ -13,65 +14,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
cx.render(::dioxus::core::LazyNodes::new(
|
render! {
|
||||||
move |__cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
|
Component {
|
||||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
|
width: "10px",
|
||||||
name: "src/main.rs:15:15:289",
|
height: "10px",
|
||||||
roots: &[::dioxus::core::TemplateNode::Dynamic { id: 0usize }],
|
left: 1,
|
||||||
node_paths: &[&[0u8]],
|
}
|
||||||
attr_paths: &[],
|
|
||||||
};
|
|
||||||
::dioxus::core::VNode {
|
|
||||||
parent: None,
|
|
||||||
key: None,
|
|
||||||
template: std::cell::Cell::new(TEMPLATE),
|
|
||||||
root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(
|
|
||||||
1usize,
|
|
||||||
__cx.bump(),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
dynamic_nodes: __cx.bump().alloc([__cx.component(
|
|
||||||
Component,
|
|
||||||
Props {
|
|
||||||
bump: __cx.bump(),
|
|
||||||
attributes: Vec::new(),
|
|
||||||
}
|
|
||||||
.width(10)
|
|
||||||
.height("100px"),
|
|
||||||
"Component",
|
|
||||||
)]),
|
|
||||||
dynamic_attrs: __cx.bump().alloc([]),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Props)]
|
|
||||||
struct Props<'a> {
|
|
||||||
bump: &'a Bump,
|
|
||||||
attributes: Vec<Attribute<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HasAttributesBox<'a, Props<'a>> for Props<'a> {
|
|
||||||
fn push_attribute(
|
|
||||||
mut self,
|
|
||||||
name: &'a str,
|
|
||||||
ns: Option<&'static str>,
|
|
||||||
attr: impl IntoAttributeValue<'a>,
|
|
||||||
volatile: bool,
|
|
||||||
) -> Self {
|
|
||||||
self.attributes.push(Attribute {
|
|
||||||
name,
|
|
||||||
namespace: ns,
|
|
||||||
value: attr.into_value(self.bump),
|
|
||||||
volatile,
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtendedGlobalAttributesMarker for Props<'_> {}
|
|
||||||
|
|
||||||
fn Component<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
|
fn Component<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
|
||||||
let attributes = &*cx.props.attributes;
|
let attributes = &*cx.props.attributes;
|
||||||
render! {
|
render! {
|
||||||
@@ -80,3 +31,9 @@ fn Component<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
struct Props<'a> {
|
||||||
|
#[props(extends = GlobalAttributes)]
|
||||||
|
attributes: Vec<Attribute<'a>>,
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ syn = { version = "2.0", features = ["full", "extra-traits"] }
|
|||||||
dioxus-rsx = { workspace = true }
|
dioxus-rsx = { workspace = true }
|
||||||
dioxus-core = { workspace = true }
|
dioxus-core = { workspace = true }
|
||||||
constcat = "0.3.0"
|
constcat = "0.3.0"
|
||||||
|
convert_case = "^0.6.0"
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
|
|||||||
.included_fields()
|
.included_fields()
|
||||||
.map(|f| struct_info.field_impl(f))
|
.map(|f| struct_info.field_impl(f))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let extends = struct_info
|
||||||
|
.extend_fields()
|
||||||
|
.map(|f| struct_info.extends_impl(f))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let fields = quote!(#(#fields)*).into_iter();
|
let fields = quote!(#(#fields)*).into_iter();
|
||||||
let required_fields = struct_info
|
let required_fields = struct_info
|
||||||
.included_fields()
|
.included_fields()
|
||||||
@@ -36,6 +40,7 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
|
|||||||
#builder_creation
|
#builder_creation
|
||||||
#conversion_helper
|
#conversion_helper
|
||||||
#( #fields )*
|
#( #fields )*
|
||||||
|
#( #extends )*
|
||||||
#( #required_fields )*
|
#( #required_fields )*
|
||||||
#build_method
|
#build_method
|
||||||
}
|
}
|
||||||
@@ -167,8 +172,8 @@ mod field_info {
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::Expr;
|
|
||||||
use syn::{parse::Error, punctuated::Punctuated};
|
use syn::{parse::Error, punctuated::Punctuated};
|
||||||
|
use syn::{Expr, Path};
|
||||||
|
|
||||||
use super::util::{
|
use super::util::{
|
||||||
expr_to_single_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix,
|
expr_to_single_string, ident_to_type, path_to_single_string, strip_raw_ident_prefix,
|
||||||
@@ -199,6 +204,13 @@ mod field_info {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extended field is automatically empty
|
||||||
|
if !builder_attr.extends.is_empty() {
|
||||||
|
builder_attr.default = Some(
|
||||||
|
syn::parse(quote!(::core::default::Default::default()).into()).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// auto detect optional
|
// auto detect optional
|
||||||
let strip_option_auto = builder_attr.strip_option
|
let strip_option_auto = builder_attr.strip_option
|
||||||
|| !builder_attr.ignore_option
|
|| !builder_attr.ignore_option
|
||||||
@@ -257,6 +269,7 @@ mod field_info {
|
|||||||
pub auto_into: bool,
|
pub auto_into: bool,
|
||||||
pub strip_option: bool,
|
pub strip_option: bool,
|
||||||
pub ignore_option: bool,
|
pub ignore_option: bool,
|
||||||
|
pub extends: Vec<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldBuilderAttr {
|
impl FieldBuilderAttr {
|
||||||
@@ -309,6 +322,17 @@ mod field_info {
|
|||||||
let name = expr_to_single_string(&assign.left)
|
let name = expr_to_single_string(&assign.left)
|
||||||
.ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
|
.ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
|
"extends" => {
|
||||||
|
if let syn::Expr::Path(path) = *assign.right {
|
||||||
|
self.extends.push(path.path);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new_spanned(
|
||||||
|
assign.right,
|
||||||
|
"Expected simple identifier",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
"default" => {
|
"default" => {
|
||||||
self.default = Some(*assign.right);
|
self.default = Some(*assign.right);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -363,6 +387,11 @@ mod field_info {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"extend" => {
|
||||||
|
self.extends.push(path.path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
macro_rules! handle_fields {
|
macro_rules! handle_fields {
|
||||||
( $( $flag:expr, $field:ident, $already:expr; )* ) => {
|
( $( $flag:expr, $field:ident, $already:expr; )* ) => {
|
||||||
@@ -466,11 +495,14 @@ fn type_from_inside_option(ty: &syn::Type, check_option_name: bool) -> Option<&s
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod struct_info {
|
mod struct_info {
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse::Error;
|
use syn::parse::Error;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::Expr;
|
use syn::spanned::Spanned;
|
||||||
|
use syn::visit::Visit;
|
||||||
|
use syn::{Expr, Ident};
|
||||||
|
|
||||||
use super::field_info::{FieldBuilderAttr, FieldInfo};
|
use super::field_info::{FieldBuilderAttr, FieldInfo};
|
||||||
use super::util::{
|
use super::util::{
|
||||||
@@ -493,7 +525,46 @@ mod struct_info {
|
|||||||
|
|
||||||
impl<'a> StructInfo<'a> {
|
impl<'a> StructInfo<'a> {
|
||||||
pub fn included_fields(&self) -> impl Iterator<Item = &FieldInfo<'a>> {
|
pub fn included_fields(&self) -> impl Iterator<Item = &FieldInfo<'a>> {
|
||||||
self.fields.iter().filter(|f| !f.builder_attr.skip)
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.filter(|f| !f.builder_attr.skip && f.builder_attr.extends.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_fields(&self) -> impl Iterator<Item = &FieldInfo<'a>> {
|
||||||
|
self.fields
|
||||||
|
.iter()
|
||||||
|
.filter(|f| !f.builder_attr.extends.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_lifetime(&self) -> syn::Result<Option<syn::Lifetime>> {
|
||||||
|
let first_extend = self.extend_fields().next();
|
||||||
|
|
||||||
|
match first_extend {
|
||||||
|
Some(f) => {
|
||||||
|
struct VisitFirstLifetime(Option<syn::Lifetime>);
|
||||||
|
|
||||||
|
impl Visit<'_> for VisitFirstLifetime {
|
||||||
|
fn visit_lifetime(&mut self, lifetime: &'_ syn::Lifetime) {
|
||||||
|
if self.0.is_none() {
|
||||||
|
self.0 = Some(lifetime.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = f.name;
|
||||||
|
let mut visitor = VisitFirstLifetime(None);
|
||||||
|
|
||||||
|
visitor.visit_type(&f.ty);
|
||||||
|
|
||||||
|
visitor.0.ok_or_else(|| {
|
||||||
|
syn::Error::new_spanned(
|
||||||
|
name,
|
||||||
|
"Unable to find lifetime for extended field. Please specify it manually",
|
||||||
|
)
|
||||||
|
}).map(Some)
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@@ -542,29 +613,12 @@ mod struct_info {
|
|||||||
|
|
||||||
let generics = self.generics.clone();
|
let generics = self.generics.clone();
|
||||||
let (_, ty_generics, where_clause) = generics.split_for_impl();
|
let (_, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
let impl_generics = self.modify_generics(|g| {
|
let impl_generics = self.generics.clone();
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
g.params.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericParam::Lifetime(syn::LifetimeParam::new(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
let (impl_generics, b_initial_generics, _) = impl_generics.split_for_impl();
|
let (impl_generics, b_initial_generics, _) = impl_generics.split_for_impl();
|
||||||
let all_fields_param = syn::GenericParam::Type(
|
let all_fields_param = syn::GenericParam::Type(
|
||||||
syn::Ident::new("TypedBuilderFields", proc_macro2::Span::call_site()).into(),
|
syn::Ident::new("TypedBuilderFields", proc_macro2::Span::call_site()).into(),
|
||||||
);
|
);
|
||||||
let b_generics = self.modify_generics(|g| {
|
let b_generics = self.modify_generics(|g| {
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
g.params.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericParam::Lifetime(syn::LifetimeParam::new(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
g.params.insert(0, all_fields_param.clone());
|
g.params.insert(0, all_fields_param.clone());
|
||||||
});
|
});
|
||||||
let empties_tuple = type_tuple(self.included_fields().map(|_| empty_type()));
|
let empties_tuple = type_tuple(self.included_fields().map(|_| empty_type()));
|
||||||
@@ -629,8 +683,7 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
quote!(#[doc(hidden)])
|
quote!(#[doc(hidden)])
|
||||||
};
|
};
|
||||||
|
|
||||||
let (b_generics_impl, b_generics_ty, b_generics_where_extras_predicates) =
|
let (_, _, b_generics_where_extras_predicates) = b_generics.split_for_impl();
|
||||||
b_generics.split_for_impl();
|
|
||||||
let mut b_generics_where: syn::WhereClause = syn::parse2(quote! {
|
let mut b_generics_where: syn::WhereClause = syn::parse2(quote! {
|
||||||
where TypedBuilderFields: Clone
|
where TypedBuilderFields: Clone
|
||||||
})?;
|
})?;
|
||||||
@@ -650,12 +703,26 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
false => quote! { true },
|
false => quote! { true },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let extend_fields = self.extend_fields().map(|f| {
|
||||||
|
let name = f.name;
|
||||||
|
let ty = f.ty;
|
||||||
|
quote!(#name: #ty)
|
||||||
|
});
|
||||||
|
let extend_fields_value = self.extend_fields().map(|f| {
|
||||||
|
let name = f.name;
|
||||||
|
quote!(#name: Vec::new())
|
||||||
|
});
|
||||||
|
let extend_lifetime = self
|
||||||
|
.extend_lifetime()?
|
||||||
|
.unwrap_or(syn::Lifetime::new("'_", proc_macro2::Span::call_site()));
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl #impl_generics #name #ty_generics #where_clause {
|
impl #impl_generics #name #ty_generics #where_clause {
|
||||||
#[doc = #builder_method_doc]
|
#[doc = #builder_method_doc]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#vis fn builder(_cx: &'__bump ::dioxus::prelude::ScopeState) -> #builder_name #generics_with_empty {
|
#vis fn builder(_cx: & #extend_lifetime ::dioxus::prelude::ScopeState) -> #builder_name #generics_with_empty {
|
||||||
#builder_name {
|
#builder_name {
|
||||||
|
#(#extend_fields_value,)*
|
||||||
bump: _cx.bump(),
|
bump: _cx.bump(),
|
||||||
fields: #empties_tuple,
|
fields: #empties_tuple,
|
||||||
_phantom: ::core::default::Default::default(),
|
_phantom: ::core::default::Default::default(),
|
||||||
@@ -667,27 +734,18 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
#builder_type_doc
|
#builder_type_doc
|
||||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||||
#vis struct #builder_name #b_generics {
|
#vis struct #builder_name #b_generics {
|
||||||
bump: &'__bump ::dioxus::core::exports::bumpalo::Bump,
|
#(#extend_fields,)*
|
||||||
|
bump: & #extend_lifetime ::dioxus::core::exports::bumpalo::Bump,
|
||||||
fields: #all_fields_param,
|
fields: #all_fields_param,
|
||||||
_phantom: (#( #phantom_generics ),*),
|
_phantom: (#( #phantom_generics ),*),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #b_generics_impl Clone for #builder_name #b_generics_ty #b_generics_where {
|
impl #impl_generics ::dioxus::prelude::Properties<#extend_lifetime> for #name #ty_generics
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
bump: self.bump,
|
|
||||||
fields: self.fields.clone(),
|
|
||||||
_phantom: ::core::default::Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #impl_generics ::dioxus::prelude::Properties<'__bump> for #name #ty_generics
|
|
||||||
#b_generics_where_extras_predicates
|
#b_generics_where_extras_predicates
|
||||||
{
|
{
|
||||||
type Builder = #builder_name #generics_with_empty;
|
type Builder = #builder_name #generics_with_empty;
|
||||||
const IS_STATIC: bool = #is_static;
|
const IS_STATIC: bool = #is_static;
|
||||||
fn builder(_cx: &'__bump ::dioxus::prelude::ScopeState) -> Self::Builder {
|
fn builder(_cx: &#extend_lifetime ::dioxus::prelude::ScopeState) -> Self::Builder {
|
||||||
#name::builder(_cx)
|
#name::builder(_cx)
|
||||||
}
|
}
|
||||||
unsafe fn memoize(&self, other: &Self) -> bool {
|
unsafe fn memoize(&self, other: &Self) -> bool {
|
||||||
@@ -723,6 +781,137 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extends_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
|
||||||
|
let StructInfo {
|
||||||
|
ref builder_name, ..
|
||||||
|
} = *self;
|
||||||
|
|
||||||
|
let field_name = field.name;
|
||||||
|
|
||||||
|
let descructuring = self.included_fields().map(|f| {
|
||||||
|
if f.ordinal == field.ordinal {
|
||||||
|
quote!(_)
|
||||||
|
} else {
|
||||||
|
let name = f.name;
|
||||||
|
quote!(#name)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let reconstructing = self.included_fields().map(|f| f.name);
|
||||||
|
|
||||||
|
// Add the bump lifetime to the generics
|
||||||
|
let mut ty_generics: Vec<syn::GenericArgument> = self
|
||||||
|
.generics
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|generic_param| match generic_param {
|
||||||
|
syn::GenericParam::Type(type_param) => {
|
||||||
|
let ident = type_param.ident.clone();
|
||||||
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
|
}
|
||||||
|
syn::GenericParam::Lifetime(lifetime_def) => {
|
||||||
|
syn::GenericArgument::Lifetime(lifetime_def.lifetime.clone())
|
||||||
|
}
|
||||||
|
syn::GenericParam::Const(const_param) => {
|
||||||
|
let ident = const_param.ident.clone();
|
||||||
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut target_generics_tuple = empty_type_tuple();
|
||||||
|
let mut ty_generics_tuple = empty_type_tuple();
|
||||||
|
let generics = self.modify_generics(|g| {
|
||||||
|
let index_after_lifetime_in_generics = g
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
|
||||||
|
.count();
|
||||||
|
for f in self.included_fields() {
|
||||||
|
if f.ordinal == field.ordinal {
|
||||||
|
ty_generics_tuple.elems.push_value(empty_type());
|
||||||
|
target_generics_tuple
|
||||||
|
.elems
|
||||||
|
.push_value(f.tuplized_type_ty_param());
|
||||||
|
} else {
|
||||||
|
g.params
|
||||||
|
.insert(index_after_lifetime_in_generics, f.generic_ty_param());
|
||||||
|
let generic_argument: syn::Type = f.type_ident();
|
||||||
|
ty_generics_tuple.elems.push_value(generic_argument.clone());
|
||||||
|
target_generics_tuple.elems.push_value(generic_argument);
|
||||||
|
}
|
||||||
|
ty_generics_tuple.elems.push_punct(Default::default());
|
||||||
|
target_generics_tuple.elems.push_punct(Default::default());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut target_generics = ty_generics.clone();
|
||||||
|
let index_after_lifetime_in_generics = target_generics
|
||||||
|
.iter()
|
||||||
|
.filter(|arg| matches!(arg, syn::GenericArgument::Lifetime(_)))
|
||||||
|
.count();
|
||||||
|
target_generics.insert(
|
||||||
|
index_after_lifetime_in_generics,
|
||||||
|
syn::GenericArgument::Type(target_generics_tuple.into()),
|
||||||
|
);
|
||||||
|
ty_generics.insert(
|
||||||
|
index_after_lifetime_in_generics,
|
||||||
|
syn::GenericArgument::Type(ty_generics_tuple.into()),
|
||||||
|
);
|
||||||
|
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let forward_extended_fields = self.extend_fields().map(|f| {
|
||||||
|
let name = f.name;
|
||||||
|
quote!(#name: self.#name)
|
||||||
|
});
|
||||||
|
|
||||||
|
let extends_impl = field.builder_attr.extends.iter().map(|path| {
|
||||||
|
let name_str = path_to_single_string(path).unwrap();
|
||||||
|
let camel_name = name_str.to_case(Case::UpperCamel);
|
||||||
|
let marker_name = Ident::new(
|
||||||
|
format!("Extended{}Marker", &camel_name).as_str(),
|
||||||
|
path.span(),
|
||||||
|
);
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics #marker_name for #builder_name < #( #ty_generics ),* > #where_clause {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let extend_lifetime = self.extend_lifetime()?.ok_or(Error::new_spanned(
|
||||||
|
field_name,
|
||||||
|
"Unable to find lifetime for extended field. Please specify it manually",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl #impl_generics ::dioxus::prelude::HasAttributesBox<#extend_lifetime> for #builder_name < #( #ty_generics ),* > #where_clause {
|
||||||
|
fn push_attribute(
|
||||||
|
mut self,
|
||||||
|
name: &#extend_lifetime str,
|
||||||
|
ns: Option<&'static str>,
|
||||||
|
attr: impl ::dioxus::prelude::IntoAttributeValue<#extend_lifetime>,
|
||||||
|
volatile: bool
|
||||||
|
) -> Self {
|
||||||
|
let ( #(#descructuring,)* ) = self.fields;
|
||||||
|
self.#field_name.push(
|
||||||
|
::dioxus::core::Attribute::new(
|
||||||
|
name,
|
||||||
|
{
|
||||||
|
use ::dioxus::prelude::IntoAttributeValue;
|
||||||
|
attr.into_value(self.bump)
|
||||||
|
},
|
||||||
|
ns,
|
||||||
|
volatile,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
#builder_name {
|
||||||
|
#(#forward_extended_fields,)*
|
||||||
|
bump: self.bump,
|
||||||
|
fields: ( #(#reconstructing,)* ),
|
||||||
|
_phantom: self._phantom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#(#extends_impl)*
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn field_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
|
pub fn field_impl(&self, field: &FieldInfo) -> Result<TokenStream, Error> {
|
||||||
let StructInfo {
|
let StructInfo {
|
||||||
ref builder_name, ..
|
ref builder_name, ..
|
||||||
@@ -744,12 +933,11 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
..
|
..
|
||||||
} = field;
|
} = field;
|
||||||
// Add the bump lifetime to the generics
|
// Add the bump lifetime to the generics
|
||||||
let mut ty_generics: Vec<syn::GenericArgument> = vec![syn::GenericArgument::Lifetime(
|
let mut ty_generics: Vec<syn::GenericArgument> = self
|
||||||
syn::Lifetime::new("'__bump", proc_macro2::Span::call_site()),
|
.generics
|
||||||
)];
|
.params
|
||||||
|
.iter()
|
||||||
ty_generics.extend(self.generics.params.iter().map(
|
.map(|generic_param| match generic_param {
|
||||||
|generic_param| match generic_param {
|
|
||||||
syn::GenericParam::Type(type_param) => {
|
syn::GenericParam::Type(type_param) => {
|
||||||
let ident = type_param.ident.clone();
|
let ident = type_param.ident.clone();
|
||||||
syn::parse(quote!(#ident).into()).unwrap()
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
@@ -761,19 +949,11 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
let ident = const_param.ident.clone();
|
let ident = const_param.ident.clone();
|
||||||
syn::parse(quote!(#ident).into()).unwrap()
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
));
|
.collect();
|
||||||
let mut target_generics_tuple = empty_type_tuple();
|
let mut target_generics_tuple = empty_type_tuple();
|
||||||
let mut ty_generics_tuple = empty_type_tuple();
|
let mut ty_generics_tuple = empty_type_tuple();
|
||||||
let generics = self.modify_generics(|g| {
|
let generics = self.modify_generics(|g| {
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
g.params.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericParam::Lifetime(syn::LifetimeParam::new(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
let index_after_lifetime_in_generics = g
|
let index_after_lifetime_in_generics = g
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
@@ -851,6 +1031,11 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
);
|
);
|
||||||
let repeated_fields_error_message = format!("Repeated field {field_name}");
|
let repeated_fields_error_message = format!("Repeated field {field_name}");
|
||||||
|
|
||||||
|
let forward_extended_fields = self.extend_fields().map(|f| {
|
||||||
|
let name = f.name;
|
||||||
|
quote!(#name: self.#name)
|
||||||
|
});
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||||
impl #impl_generics #builder_name < #( #ty_generics ),* > #where_clause {
|
impl #impl_generics #builder_name < #( #ty_generics ),* > #where_clause {
|
||||||
@@ -859,6 +1044,7 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
let #field_name = (#arg_expr,);
|
let #field_name = (#arg_expr,);
|
||||||
let ( #(#descructuring,)* ) = self.fields;
|
let ( #(#descructuring,)* ) = self.fields;
|
||||||
#builder_name {
|
#builder_name {
|
||||||
|
#(#forward_extended_fields,)*
|
||||||
bump: self.bump,
|
bump: self.bump,
|
||||||
fields: ( #(#reconstructing,)* ),
|
fields: ( #(#reconstructing,)* ),
|
||||||
_phantom: self._phantom,
|
_phantom: self._phantom,
|
||||||
@@ -893,13 +1079,11 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
..
|
..
|
||||||
} = field;
|
} = field;
|
||||||
// Add a bump lifetime to the generics
|
// Add a bump lifetime to the generics
|
||||||
let mut builder_generics: Vec<syn::GenericArgument> =
|
let mut builder_generics: Vec<syn::GenericArgument> = self
|
||||||
vec![syn::GenericArgument::Lifetime(syn::Lifetime::new(
|
.generics
|
||||||
"'__bump",
|
.params
|
||||||
proc_macro2::Span::call_site(),
|
.iter()
|
||||||
))];
|
.map(|generic_param| match generic_param {
|
||||||
builder_generics.extend(self.generics.params.iter().map(|generic_param| {
|
|
||||||
match generic_param {
|
|
||||||
syn::GenericParam::Type(type_param) => {
|
syn::GenericParam::Type(type_param) => {
|
||||||
let ident = &type_param.ident;
|
let ident = &type_param.ident;
|
||||||
syn::parse(quote!(#ident).into()).unwrap()
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
@@ -911,18 +1095,10 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
let ident = &const_param.ident;
|
let ident = &const_param.ident;
|
||||||
syn::parse(quote!(#ident).into()).unwrap()
|
syn::parse(quote!(#ident).into()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}));
|
.collect();
|
||||||
let mut builder_generics_tuple = empty_type_tuple();
|
let mut builder_generics_tuple = empty_type_tuple();
|
||||||
let generics = self.modify_generics(|g| {
|
let generics = self.modify_generics(|g| {
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
g.params.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericParam::Lifetime(syn::LifetimeParam::new(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
let index_after_lifetime_in_generics = g
|
let index_after_lifetime_in_generics = g
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1007,15 +1183,6 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
} = *self;
|
} = *self;
|
||||||
|
|
||||||
let generics = self.modify_generics(|g| {
|
let generics = self.modify_generics(|g| {
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
g.params.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericParam::Lifetime(syn::LifetimeParam::new(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
|
|
||||||
let index_after_lifetime_in_generics = g
|
let index_after_lifetime_in_generics = g
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1054,15 +1221,6 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
let (_, ty_generics, where_clause) = self.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
|
||||||
let modified_ty_generics = modify_types_generics_hack(&ty_generics, |args| {
|
let modified_ty_generics = modify_types_generics_hack(&ty_generics, |args| {
|
||||||
// Add a bump lifetime to the generics
|
|
||||||
args.insert(
|
|
||||||
0,
|
|
||||||
syn::GenericArgument::Lifetime(syn::Lifetime::new(
|
|
||||||
"'__bump",
|
|
||||||
proc_macro2::Span::call_site(),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
args.insert(
|
args.insert(
|
||||||
0,
|
0,
|
||||||
syn::GenericArgument::Type(
|
syn::GenericArgument::Type(
|
||||||
@@ -1088,7 +1246,9 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||||||
// reordering based on that, but for now this much simpler thing is a reasonable approach.
|
// reordering based on that, but for now this much simpler thing is a reasonable approach.
|
||||||
let assignments = self.fields.iter().map(|field| {
|
let assignments = self.fields.iter().map(|field| {
|
||||||
let name = &field.name;
|
let name = &field.name;
|
||||||
if let Some(ref default) = field.builder_attr.default {
|
if !field.builder_attr.extends.is_empty() {
|
||||||
|
quote!(let #name = self.#name;)
|
||||||
|
} else if let Some(ref default) = field.builder_attr.default {
|
||||||
if field.builder_attr.skip {
|
if field.builder_attr.skip {
|
||||||
quote!(let #name = #default;)
|
quote!(let #name = #default;)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -89,9 +89,10 @@ pub mod prelude {
|
|||||||
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, has_context,
|
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, has_context,
|
||||||
provide_context, provide_context_to_scope, provide_root_context, push_future,
|
provide_context, provide_context_to_scope, provide_root_context, push_future,
|
||||||
remove_future, schedule_update_any, spawn, spawn_forever, suspend, throw, AnyValue,
|
remove_future, schedule_update_any, spawn, spawn_forever, suspend, throw, AnyValue,
|
||||||
AttributeType, Component, Element, Event, EventHandler, Fragment, IntoAttributeValue,
|
AttributeType, Component, Element, Event, EventHandler, Fragment, HasAttributesBox,
|
||||||
LazyNodes, MountedAttribute, Properties, Runtime, RuntimeGuard, Scope, ScopeId, ScopeState,
|
IntoAttributeValue, LazyNodes, MountedAttribute, Properties, Runtime, RuntimeGuard, Scope,
|
||||||
Scoped, TaskId, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
|
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, Throw,
|
||||||
|
VNode, VirtualDom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -899,12 +899,12 @@ impl<'a, T: IntoAttributeValue<'a>> IntoAttributeValue<'a> for Option<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasAttributesBox<'a, T> {
|
pub trait HasAttributesBox<'a> {
|
||||||
fn push_attribute(
|
fn push_attribute(
|
||||||
self,
|
self,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
ns: Option<&'static str>,
|
ns: Option<&'static str>,
|
||||||
attr: impl IntoAttributeValue<'a>,
|
attr: impl IntoAttributeValue<'a>,
|
||||||
volatile: bool,
|
volatile: bool,
|
||||||
) -> T;
|
) -> Self;
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ impl ToTokens for ImplExtensionAttributes {
|
|||||||
pub trait #extension_name<'a> {
|
pub trait #extension_name<'a> {
|
||||||
#(#defs)*
|
#(#defs)*
|
||||||
}
|
}
|
||||||
impl<'a, T> #extension_name<'a> for T where T: HasAttributesBox<'a, T> + #marker_name {
|
impl<'a, T> #extension_name<'a> for T where T: HasAttributesBox<'a> + #marker_name {
|
||||||
#(#impls)*
|
#(#impls)*
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user