1use proc_macro::TokenStream;
34use quote::quote;
35use syn::{parse_macro_input, Attribute, DeriveInput, Generics, Ident};
36
37struct ArkheArgs {
39 type_code: u32,
40 schema_version: u32,
41}
42
43fn parse_arkhe_args(attrs: &[Attribute], target: &Ident) -> Result<ArkheArgs, TokenStream> {
47 let mut type_code: Option<u32> = None;
48 let mut schema_version: u32 = 1;
49 let mut parse_error: Option<syn::Error> = None;
50
51 for attr in attrs {
52 if !attr.path().is_ident("arkhe") {
53 continue;
54 }
55 let result = attr.parse_nested_meta(|meta| {
56 if meta.path.is_ident("type_code") {
57 let value = meta.value()?;
58 let lit: syn::LitInt = value.parse()?;
59 type_code = Some(lit.base10_parse()?);
60 return Ok(());
61 }
62 if meta.path.is_ident("schema_version") {
63 let value = meta.value()?;
64 let lit: syn::LitInt = value.parse()?;
65 schema_version = lit.base10_parse()?;
66 return Ok(());
67 }
68 Err(meta
69 .error("unknown #[arkhe(...)] argument; expected `type_code` or `schema_version`"))
70 });
71 if let Err(e) = result {
72 parse_error = Some(e);
73 break;
74 }
75 }
76
77 if let Some(err) = parse_error {
78 return Err(err.to_compile_error().into());
79 }
80
81 let type_code = match type_code {
82 Some(v) => v,
83 None => {
84 return Err(syn::Error::new_spanned(
85 target,
86 "missing #[arkhe(type_code = N)] attribute",
87 )
88 .to_compile_error()
89 .into());
90 }
91 };
92
93 Ok(ArkheArgs {
94 type_code,
95 schema_version,
96 })
97}
98
99fn sealed_impl(name: &Ident, generics: &Generics) -> proc_macro2::TokenStream {
101 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
102 quote! {
103 impl #impl_generics ::arkhe_kernel::state::traits::_sealed::Sealed
104 for #name #ty_generics #where_clause {}
105 }
106}
107
108#[proc_macro_derive(ArkheAction, attributes(arkhe))]
111pub fn derive_arkhe_action(input: TokenStream) -> TokenStream {
112 let input = parse_macro_input!(input as DeriveInput);
113 let name = input.ident.clone();
114 let args = match parse_arkhe_args(&input.attrs, &name) {
115 Ok(a) => a,
116 Err(ts) => return ts,
117 };
118 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
119 let sealed = sealed_impl(&name, &input.generics);
120 let type_code = args.type_code;
121 let schema_version = args.schema_version;
122
123 let expanded = quote! {
124 #sealed
125
126 impl #impl_generics ::arkhe_kernel::state::traits::ActionDeriv
127 for #name #ty_generics #where_clause
128 {
129 const TYPE_CODE: ::arkhe_kernel::abi::TypeCode =
130 ::arkhe_kernel::abi::TypeCode(#type_code);
131 const SCHEMA_VERSION: u32 = #schema_version;
132 }
133 };
134
135 TokenStream::from(expanded)
136}
137
138#[proc_macro_derive(ArkheComponent, attributes(arkhe))]
142pub fn derive_arkhe_component(input: TokenStream) -> TokenStream {
143 let input = parse_macro_input!(input as DeriveInput);
144 let name = input.ident.clone();
145 let args = match parse_arkhe_args(&input.attrs, &name) {
146 Ok(a) => a,
147 Err(ts) => return ts,
148 };
149 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
150 let sealed = sealed_impl(&name, &input.generics);
151 let type_code = args.type_code;
152 let schema_version = args.schema_version;
153
154 let expanded = quote! {
155 #sealed
156
157 impl #impl_generics ::arkhe_kernel::state::traits::Component
158 for #name #ty_generics #where_clause
159 {
160 const TYPE_CODE: ::arkhe_kernel::abi::TypeCode =
161 ::arkhe_kernel::abi::TypeCode(#type_code);
162 const SCHEMA_VERSION: u32 = #schema_version;
163 }
164 };
165
166 TokenStream::from(expanded)
167}
168
169#[proc_macro_derive(ArkheEvent, attributes(arkhe))]
172pub fn derive_arkhe_event(input: TokenStream) -> TokenStream {
173 let input = parse_macro_input!(input as DeriveInput);
174 let name = input.ident.clone();
175 let args = match parse_arkhe_args(&input.attrs, &name) {
176 Ok(a) => a,
177 Err(ts) => return ts,
178 };
179 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
180 let sealed = sealed_impl(&name, &input.generics);
181 let type_code = args.type_code;
182 let schema_version = args.schema_version;
183
184 let expanded = quote! {
185 #sealed
186
187 impl #impl_generics ::arkhe_kernel::state::traits::Event
188 for #name #ty_generics #where_clause
189 {
190 const TYPE_CODE: ::arkhe_kernel::abi::TypeCode =
191 ::arkhe_kernel::abi::TypeCode(#type_code);
192 const SCHEMA_VERSION: u32 = #schema_version;
193 }
194 };
195
196 TokenStream::from(expanded)
197}