Experiments in applying Entity-Component-System patterns to durable data storage APIs.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at track-changes 117 lines 3.7 kB view raw
1extern crate proc_macro; 2use proc_macro::TokenStream; 3use quote::quote; 4use syn::{punctuated::Punctuated, Data, Expr, Fields, Lit, Meta, Token}; 5 6#[proc_macro_derive(Component, attributes(component))] 7pub fn derive_component_fn(input: TokenStream) -> TokenStream { 8 let ast = syn::parse(input).unwrap(); 9 impl_derive_component(&ast) 10} 11 12#[proc_macro_derive(Resource, attributes(component))] 13pub fn derive_resource_fn(input: TokenStream) -> TokenStream { 14 let ast = syn::parse(input).unwrap(); 15 impl_derive_resource(&ast) 16} 17 18#[derive(Debug)] 19enum Storage { 20 Json, 21 Blob, 22 Null, 23} 24 25fn impl_derive_component(ast: &syn::DeriveInput) -> TokenStream { 26 let name = ast.ident.clone(); 27 28 let mut storage = Storage::Json; 29 30 if let Data::Struct(ref struc) = ast.data { 31 if matches!(struc.fields, Fields::Unit) { 32 storage = Storage::Null; 33 } 34 } 35 36 if let Some(component_attribute) = ast.attrs.iter().find(|a| a.path().is_ident("component")) { 37 let nested = component_attribute 38 .parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated) 39 .unwrap(); 40 for meta in nested.clone() { 41 match meta { 42 Meta::NameValue(mnv) if mnv.path.is_ident("storage") => { 43 if let Expr::Lit(expr_lit) = &mnv.value { 44 if let Lit::Str(lit) = &expr_lit.lit { 45 match lit.value().as_str() { 46 "json" => storage = Storage::Json, 47 "blob" => storage = Storage::Blob, 48 other => panic!("storage {other} not supported"), 49 } 50 } 51 } 52 } 53 other => panic!( 54 "Unsupported attribute {}", 55 other.path().get_ident().unwrap() 56 ), 57 } 58 } 59 }; 60 61 let gen = match storage { 62 Storage::Json => { 63 quote! { 64 impl ecsdb::component::Component for #name { 65 type Storage =ecsdb::component::JsonStorage; 66 const NAME: &'static str = concat!(std::module_path!(), "::", stringify!(#name)); 67 } 68 } 69 } 70 71 Storage::Null => { 72 quote! { 73 impl ecsdb::component::Component for #name { 74 type Storage = ecsdb::component::NullStorage; 75 const NAME: &'static str = concat!(std::module_path!(), "::", stringify!(#name)); 76 } 77 } 78 } 79 80 Storage::Blob => { 81 quote! { 82 impl ecsdb::component::Component for #name { 83 type Storage = ecsdb::component::BlobStorage; 84 const NAME: &'static str = concat!(std::module_path!(), "::", stringify!(#name)); 85 } 86 87 impl Into<Vec<u8>> for #name { 88 fn into(self) -> Vec<u8> { 89 self.0 90 } 91 } 92 93 impl From<Vec<u8>> for #name { 94 fn from(value: Vec<u8>) -> Self { 95 Self(value) 96 } 97 } 98 } 99 } 100 }; 101 102 gen.into() 103} 104 105fn impl_derive_resource(ast: &syn::DeriveInput) -> TokenStream { 106 let name = ast.ident.clone(); 107 108 let component_derive: proc_macro2::TokenStream = impl_derive_component(ast).into(); 109 110 let gen = quote! { 111 #component_derive 112 113 impl ecsdb::resource::Resource for #name { } 114 }; 115 116 gen.into() 117}