Skip to content

Commit

Permalink
attempt extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Nertsal committed Nov 19, 2024
1 parent c11bfe6 commit 22c983e
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 68 deletions.
22 changes: 12 additions & 10 deletions crates/stecs-derive/src/get/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ impl StorageGetOpts {
// },
// }

let (fields, constructor) = self.image.prepare_fields_constructor();
let mut get_fields = constructor;

let storage = &self.struct_of;
let id = &self.id;
for (name, is_mut, optic) in fields.into_iter().rev() {
let name = &name.mangled;
let access = if is_mut {
optic.access_mut(quote! { #id }, quote! { #storage })
let generation = self.image.prepare_fields_constructor(storage);
let mut get_fields = generation.constructor;

for field in generation.fields.into_iter().rev() {
let name = &field.name.mangled;
let access = if field.is_mut {
field.optic.access_mut(quote! { #id }, quote! { #storage })
} else {
optic.access(quote! { #id }, quote! { #storage })
field.optic.access(quote! { #id }, quote! { #storage })
};

get_fields = match optic {
get_fields = match field.optic {
#[cfg(feature = "dynamic")]
Optic::Dynamic { .. } => quote! {
Optic::Dynamic { .. } | Optic::Extension { .. } => quote! {
{
let #name = #access;
#get_fields
Expand Down Expand Up @@ -65,6 +65,8 @@ impl StorageGetOpts {
};
}

get_fields.extend(generation.get_extensions);

quote! {{
#[allow(non_snake_case)]
#get_fields
Expand Down
106 changes: 90 additions & 16 deletions crates/stecs-derive/src/get/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

Check warning on line 1 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

unused import: `std::collections::HashMap`

Check warning on line 1 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

unused import: `std::collections::HashMap`

use crate::optic::Optic;

use darling::export::syn::{self, punctuated::Punctuated};
Expand Down Expand Up @@ -50,25 +52,41 @@ pub struct TupleFieldOpts {
pub optic: Optic,
}

pub struct FieldGeneration {
pub name: FieldName,
pub is_mut: bool,
pub optic: Optic,
#[cfg(feature = "dynamic")]
pub extension: Option<syn::Ident>,
}

pub struct ImageGeneration {
pub fields: Vec<FieldGeneration>,
pub get_extensions: Vec<TokenStream>,
pub constructor: TokenStream,
}

impl ImageOpts {
/// Prepare fields for code generation and the constructor for the image.
pub fn prepare_fields_constructor(&self) -> (Vec<(FieldName, bool, Optic)>, TokenStream) {
pub fn prepare_fields_constructor(&self, storage: &syn::Expr) -> ImageGeneration {

Check warning on line 71 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

unused variable: `storage`

Check warning on line 71 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

unused variable: `storage`
let fields: Vec<_> = match &self {
ImageOpts::Struct { fields, .. } => fields
.iter()
.map(|field| {
(
FieldGeneration {
// NOTE: mangled name to avoid conflicts
FieldName {
name: FieldName {
original: field.name.clone(),
mangled: syn::Ident::new(
&format!("__{}", field.name),
proc_macro2::Span::call_site(),
),
},
field.is_mut,
field.optic.clone(),
)
is_mut: field.is_mut,
optic: field.optic.clone(),
#[cfg(feature = "dynamic")]
extension: field.optic.get_extension(),
}
})
.collect(),
ImageOpts::Tuple { fields } => fields
Expand All @@ -78,31 +96,87 @@ impl ImageOpts {
// NOTE: mangled name to avoid conflicts
let name =
syn::Ident::new(&format!("__field{}", i), proc_macro2::Span::call_site());
(
FieldName {
FieldGeneration {
name: FieldName {
original: name.clone(),
mangled: name,
},
field.is_mut,
field.optic.clone(),
)
is_mut: field.is_mut,
optic: field.optic.clone(),
#[cfg(feature = "dynamic")]
extension: field.optic.get_extension(),
}
})
.collect(),
};

#[cfg(not(feature = "dynamic"))]
let get_extensions = vec![];
#[cfg(feature = "dynamic")]
let get_extensions = {
let mut extensions: HashMap<&syn::Type, bool> = Default::default();
for field in &fields {
if let Optic::Extension { ty, .. } = &field.optic {
let value = extensions.entry(ty).or_insert(false);
*value = *value || field.is_mut;
}
}

extensions
.into_iter()
.map(|(ty, is_mut)| {
let name = extension_name(ty);
if is_mut {
quote! {
let mut __EXT_empty = <#ty as ::stecs::archetype::SplitFields<_>>::Split::default();
let #name = #storage.r#dyn.get_ext_mut::<#ty>().unwrap_or(&mut __EXT_empty);
}
} else {
quote! {
let __EXT_empty = <#ty as ::stecs::archetype::SplitFields<_>>::Split::default();
let #name = #storage.r#dyn.get_ext::<#ty>().unwrap_or(&__EXT_empty);
}
}
})
.collect()
};

let constructor = match self {
ImageOpts::Struct { ident, .. } => {
let fields = fields
.iter()
.map(|(FieldName { original, mangled }, _, _)| quote! { #original: #mangled });
let fields = fields.iter().map(|field| {
let original = &field.name.original;
let mangled = &field.name.mangled;
quote! { #original: #mangled }
});
quote! { Some(#ident { #(#fields),* }) }
}
ImageOpts::Tuple { .. } => {
let fields = fields.iter().map(|(name, _, _)| &name.mangled);
let fields = fields.iter().map(|field| &field.name.mangled);
quote! { Some(( #(#fields),* )) }
}
};

(fields, constructor)
ImageGeneration {
fields,
get_extensions,
constructor,
}
}
}

impl Optic {
#[cfg(feature = "dynamic")]
pub fn get_extension(&self) -> Option<syn::Ident> {
match self {
Optic::Extension { ty, .. } => Some(extension_name(ty)),
_ => None,
}
}
}

fn extension_name(ty: &syn::Type) -> syn::Ident {

Check warning on line 177 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

function `extension_name` is never used

Check warning on line 177 in crates/stecs-derive/src/get/types.rs

View workflow job for this annotation

GitHub Actions / Test

function `extension_name` is never used
syn::Ident::new(
&format!("__EXT_{}", quote! { #ty }),
proc_macro2::Span::call_site(),
)
}
110 changes: 110 additions & 0 deletions crates/stecs-derive/src/optic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub enum Optic {
ty: syn::Type,
component: OpticComponent,
},
#[cfg(feature = "dynamic")]
Extension {
ty: syn::Type,
storage: OpticStorage,
component: OpticComponent,
},
GetId,
Access {
storage: OpticStorage,
Expand Down Expand Up @@ -54,6 +60,17 @@ impl Access {
}

impl Optic {
pub fn is_dynamic(&self) -> bool {
match self {
#[cfg(feature = "dynamic")]
Optic::Dynamic { .. } => true,
#[cfg(feature = "dynamic")]
Optic::Extension { .. } => true,
Optic::GetId => false,
Optic::Access { .. } => false,
}
}

/// Access the target component immutably.
pub fn access(&self, id: TokenStream, archetype: TokenStream) -> TokenStream {
self.access_impl(false, id, archetype)
Expand Down Expand Up @@ -85,6 +102,34 @@ impl Optic {
}}
}
}
#[cfg(feature = "dynamic")]
Optic::Extension {
ty: _,
storage,
component,
} => {
let storage = storage.access(archetype);

let getter = if is_mut {
quote! { get_mut }
} else {
quote! { get }
};

if component.is_identity() {
quote! { #storage.#getter(#id) }
} else {
let value_name = quote! { __value };
let access =
component.access_impl(Access::borrow(is_mut), quote! { #value_name });
quote! {
match #storage.#getter(#id) {
None => None,
Some(#value_name) => { Some(#access) }
}
}
}
}
Optic::GetId => id,
Optic::Access { storage, component } => {
let storage = storage.access(quote! { #archetype.inner });
Expand Down Expand Up @@ -129,6 +174,26 @@ impl Optic {
unsafe { #archetype.r#dyn.get_many_mut::<#ty>(#ids) } #access
}
}
#[cfg(feature = "dynamic")]
Optic::Extension {
ty: _,
storage,
component,
} => {
let archetype = storage.access(archetype);

let value_name = quote! { __value };
let access = if component.is_identity() {
quote! {}
} else {
let access = component.access_impl(Access::BorrowMut, quote! { #value_name });
quote! { .map(|#value_name| #access) }
};

quote! {
unsafe { #archetype.get_many_unchecked_mut(#ids) } #access
}
}
Optic::GetId => ids,
Optic::Access { storage, component } => {
let storage = storage.access(quote! { #archetype.inner });
Expand Down Expand Up @@ -223,6 +288,7 @@ enum OpticPart {

impl Parse for Optic {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
// Dynamic
if let Some(_dyn) = input.parse::<Option<syn::Token![dyn]>>()? {
#[cfg(not(feature = "dynamic"))]
{
Expand Down Expand Up @@ -251,6 +317,41 @@ impl Parse for Optic {
}
}

// Extension
let _extension = if input
.fork()
.parse::<Option<syn::Ident>>()?
.filter(|ident| ident == "ext")
.is_some()
{
let _ext = input.parse::<syn::Ident>().expect("parsed ext");

#[cfg(not(feature = "dynamic"))]
{
return Err(syn::Error::new_spanned(
_ext,
"`ext` extensions are not available because the `dynamic` feature is disabled",
));
}

let ty: syn::Type = input.parse()?;

Check warning on line 337 in crates/stecs-derive/src/optic.rs

View workflow job for this annotation

GitHub Actions / Test

unreachable statement

Check warning on line 337 in crates/stecs-derive/src/optic.rs

View workflow job for this annotation

GitHub Actions / Test

unreachable statement
let mut some = false;
if input.parse::<Option<syn::Token![.]>>()?.is_some() {
if let Some(ident) = input.parse::<Option<syn::Ident>>()? {
if ident != "Some" {
return Err(syn::Error::new_spanned(
ident, "unexpected token, you might be missing a `.Some` to filter existing extensions"
));
}
some = true;
input.parse::<Option<syn::Token![.]>>()?;
}
}
Some((ty, some))
} else {
None
};

let parts = Punctuated::<OpticPartToken, syn::Token![.]>::parse_separated_nonempty(input)?;

let parts: Vec<_> = parts.into_iter().collect();
Expand Down Expand Up @@ -320,6 +421,15 @@ impl Parse for Optic {
// Component part
let component = build_component_optic(component_parts)?;

#[cfg(feature = "dynamic")]
if let Some((ty, _)) = _extension {
return Ok(Optic::Extension {
ty,
storage,
component,
});
}

Ok(Optic::Access { storage, component })
}
}
Expand Down
Loading

0 comments on commit 22c983e

Please sign in to comment.