1#[proc_macro_derive(Asset, attributes(asset, serde))]
2pub fn asset(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
3 match parse(item).and_then(asset_impl) {
4 Ok(tokens) => tokens,
5 Err(error) => error.into_compile_error(),
6 }
7 .into()
8}
9
10#[proc_macro_derive(AssetField, attributes(asset, serde))]
11pub fn asset_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 match parse(item).and_then(asset_field_impl) {
13 Ok(tokens) => tokens,
14 Err(error) => error.into_compile_error(),
15 }
16 .into()
17}
18
19struct Parsed {
20 complex: bool,
21 derive_input: syn::DeriveInput,
22 info: syn::Ident,
23 futures: syn::Ident,
24 decoded: syn::Ident,
25 decode_error: syn::Ident,
26 decode_field_errors: proc_macro2::TokenStream,
27 build_error: syn::Ident,
28 build_field_errors: proc_macro2::TokenStream,
29 builder_bounds: proc_macro2::TokenStream,
30 info_fields: proc_macro2::TokenStream,
31 info_to_futures_fields: proc_macro2::TokenStream,
32 futures_fields: proc_macro2::TokenStream,
33 futures_to_decoded_fields: proc_macro2::TokenStream,
34 decoded_fields: proc_macro2::TokenStream,
35 decoded_to_asset_fields: proc_macro2::TokenStream,
36 serde_attributes: Vec<syn::Attribute>,
37 name: Option<syn::LitStr>,
38}
39
40fn parse(item: proc_macro::TokenStream) -> syn::Result<Parsed> {
41 use syn::spanned::Spanned;
42
43 let derive_input = syn::parse::<syn::DeriveInput>(item)?;
44
45 let asset_attributes = derive_input
46 .attrs
47 .iter()
48 .enumerate()
49 .filter_map(|(index, attr)| {
50 if attr.path.is_ident("asset") {
51 Some(index)
52 } else {
53 None
54 }
55 })
56 .collect::<Vec<_>>();
57
58 let mut name_arg = None;
59
60 for idx in &asset_attributes {
61 let attr = &derive_input.attrs[*idx];
62
63 attr.parse_args_with(|stream: syn::parse::ParseStream| {
64 match stream.parse::<syn::Ident>()? {
65 i if i == "name" => {
66 let _eq = stream.parse::<syn::Token![=]>()?;
67
68 let name = stream.parse::<syn::LitStr>()?;
69 name_arg = Some(name);
70
71 if !stream.is_empty() {
72 return Err(syn::Error::new(stream.span(), "Expected end of arguments"));
73 }
74
75 Ok(())
76 }
77 i => Err(syn::Error::new_spanned(
78 i,
79 "Unexpected ident. Expected: 'name'",
80 )),
81 }
82 })?;
83 }
84
85 let serde_attributes = derive_input
86 .attrs
87 .iter()
88 .filter(|attr| attr.path.is_ident("serde"))
89 .cloned()
90 .collect();
91
92 let mut decode_field_errors = proc_macro2::TokenStream::new();
93 let mut build_field_errors = proc_macro2::TokenStream::new();
94 let mut builder_bounds = proc_macro2::TokenStream::new();
95
96 let info = quote::format_ident!("{}Info", derive_input.ident);
97 let mut info_fields = proc_macro2::TokenStream::new();
98 let mut info_to_futures_fields = proc_macro2::TokenStream::new();
99
100 let futures = quote::format_ident!("{}Futures", derive_input.ident);
101 let mut futures_fields = proc_macro2::TokenStream::new();
102 let mut futures_to_decoded_fields = proc_macro2::TokenStream::new();
103
104 let decoded = quote::format_ident!("{}Decoded", derive_input.ident);
105 let mut decoded_fields = proc_macro2::TokenStream::new();
106 let mut decoded_to_asset_fields = proc_macro2::TokenStream::new();
107
108 let decode_error = quote::format_ident!("{}DecodeError", derive_input.ident);
109 let build_error = quote::format_ident!("{}BuildError", derive_input.ident);
110
111 let mut complex: bool = false;
112
113 let data_struct = match &derive_input.data {
114 syn::Data::Struct(data) => data,
115 syn::Data::Enum(data) => {
116 return Err(syn::Error::new_spanned(
117 data.enum_token,
118 "Only structs are currently supported by derive(Asset) macro",
119 ))
120 }
121 syn::Data::Union(data) => {
122 return Err(syn::Error::new_spanned(
123 data.union_token,
124 "Only structs are currently supported by derive(Asset) macro",
125 ))
126 }
127 };
128
129 for (index, field) in data_struct.fields.iter().enumerate() {
130 let asset_attributes = field
131 .attrs
132 .iter()
133 .enumerate()
134 .filter_map(|(index, attr)| {
135 if attr.path.is_ident("asset") {
136 Some(index)
137 } else {
138 None
139 }
140 })
141 .collect::<Vec<_>>();
142
143 let serde_attributes = field
144 .attrs
145 .iter()
146 .filter(|attr| attr.path.is_ident("serde"));
147
148 let ty = &field.ty;
149
150 match asset_attributes.len() {
151 0 => match &field.ident {
152 Some(ident) => {
153 info_fields.extend(quote::quote!(
154 #(#serde_attributes)*
155 #ident: #ty,
156 ));
157 futures_fields.extend(quote::quote!(#ident: #ty,));
158 decoded_fields.extend(quote::quote!(#ident: #ty,));
159 info_to_futures_fields.extend(quote::quote!(#ident: info.#ident,));
160 futures_to_decoded_fields.extend(quote::quote!(#ident: futures.#ident,));
161 decoded_to_asset_fields.extend(quote::quote!(#ident: decoded.#ident,));
162 }
163 None => {
164 info_fields.extend(quote::quote!(
165 #(#serde_attributes)*
166 #ty,
167 ));
168 futures_fields.extend(quote::quote!(#ty,));
169 decoded_fields.extend(quote::quote!(#ty,));
170 info_to_futures_fields.extend(quote::quote!(info.#index,));
171 futures_to_decoded_fields.extend(quote::quote!(futures.#index,));
172 decoded_to_asset_fields.extend(quote::quote!(decoded.#index,));
173 }
174 },
175 1 => {
176 complex = true;
177
178 let mut is_external = false;
179 let mut is_container = false;
180 let mut as_type_arg = None;
181
182 for idx in &asset_attributes {
183 let attribute = &field.attrs[*idx];
184
185 attribute.parse_args_with(|stream: syn::parse::ParseStream| {
186 match stream.parse::<syn::Ident>()? {
187 i if i == "external" => {
188 if is_container {
189 return Err(syn::Error::new_spanned(i, "Attributes 'container' and 'external' are mutually exclusive"));
190 }
191 if is_external {
192 return Err(syn::Error::new_spanned(i, "Attributes 'external' is already specified"));
193 }
194 is_external = true;
195
196 if !stream.is_empty() {
197 let args;
198 syn::parenthesized!(args in stream);
199 let _as = args.parse::<syn::Token![as]>()?;
200 let as_type = args.parse::<syn::Type>()?;
201 as_type_arg = Some(as_type);
202
203 if !stream.is_empty() {
204 return Err(syn::Error::new(stream.span(), "Expected end of arguments"));
205 }
206 }
207
208 Ok(())
209 },
210 i if i == "container" => {
211 if is_external {
212 return Err(syn::Error::new_spanned(i, "Attributes 'external' and 'container' are mutually exclusive"));
213 }
214 if is_container {
215 return Err(syn::Error::new_spanned(i, "Attributes 'container' is already specified"));
216 }
217 is_container = true;
218
219 if !stream.is_empty() {
220 let args;
221 syn::parenthesized!(args in stream);
222 let _as = args.parse::<syn::Token![as]>()?;
223 let as_type = args.parse::<syn::Type>()?;
224 as_type_arg = Some(as_type);
225
226 if !stream.is_empty() {
227 return Err(syn::Error::new(stream.span(), "Expected end of arguments"));
228 }
229 }
230
231 Ok(())
232 }
233 i => {
234 Err(syn::Error::new_spanned(i, "Unexpected ident. Expected: 'external' or 'container'"))
235 }
236 }
237 })?;
238 }
239
240 let as_type = as_type_arg.as_ref().unwrap_or(ty);
241
242 let kind = match (is_container, is_external) {
243 (false, true) => quote::quote!(::goods::External),
244 (true, false) => quote::quote!(::goods::Container),
245 _ => unreachable!(),
246 };
247
248 match &field.ident {
249 Some(ident) => {
250 let error_variant = quote::format_ident!("{}Error", snake_to_pascal(ident));
251 let decode_error_text = syn::LitStr::new(
252 &format!("Failed to decode asset field '{}'", ident),
253 ident.span(),
254 );
255 let build_error_text = syn::LitStr::new(
256 &format!("Failed to build asset field '{}'", ident),
257 ident.span(),
258 );
259
260 decode_field_errors.extend(quote::quote!(
261 #[error(#decode_error_text)]
262 #error_variant { source: <#as_type as ::goods::AssetField<#kind>>::DecodeError },
263 ));
264 build_field_errors.extend(quote::quote!(
265 #[error(#build_error_text)]
266 #error_variant { source: <#as_type as ::goods::AssetField<#kind>>::BuildError },
267 ));
268
269 builder_bounds.extend(
270 quote::quote!(#as_type: ::goods::AssetFieldBuild<#kind, BuilderGenericParameter>,),
271 );
272 info_fields.extend(
273 quote::quote!(#ident: <#as_type as ::goods::AssetField<#kind>>::Info,),
274 );
275 futures_fields.extend(
276 quote::quote!(#ident: <#as_type as ::goods::AssetField<#kind>>::Fut,),
277 );
278 decoded_fields
279 .extend(quote::quote!(#ident: <#as_type as ::goods::AssetField<#kind>>::Decoded,));
280 info_to_futures_fields
281 .extend(quote::quote!(#ident: <#as_type as ::goods::AssetField<#kind>>::decode(info.#ident, loader),));
282 futures_to_decoded_fields
283 .extend(quote::quote!(#ident: futures.#ident.await.map_err(|err| #decode_error::#error_variant { source: err })?,));
284 decoded_to_asset_fields
285 .extend(quote::quote!(#ident: <#ty as ::std::convert::From<#as_type>>::from(<#as_type as ::goods::AssetFieldBuild<#kind, BuilderGenericParameter>>::build(decoded.#ident, builder).map_err(|err| #build_error::#error_variant { source: err })?),));
286 }
287 None => {
288 let error_variant =
289 syn::Ident::new(&format!("Field{}Error", index), field.span());
290 let decode_error_text = syn::LitStr::new(
291 &format!("Failed to decode asset field '{}'", index),
292 field.span(),
293 );
294 let build_error_text = syn::LitStr::new(
295 &format!("Failed to load asset field '{}'", index),
296 field.span(),
297 );
298
299 decode_field_errors.extend(quote::quote!(
300 #[error(#decode_error_text)]
301 #error_variant { source: <#as_type as ::goods::AssetField<#kind>>::DecodeError },
302 ));
303 build_field_errors.extend(quote::quote!(
304 #[error(#build_error_text)]
305 #error_variant { source: <#as_type as ::goods::AssetField<#kind>>::BuildError },
306 ));
307
308 builder_bounds.extend(
309 quote::quote!(#as_type: ::goods::AssetFieldBuild<#kind, BuilderGenericParameter>,),
310 );
311 info_fields
312 .extend(quote::quote!(<#as_type as ::goods::AssetField<#kind>>::Info,));
313 futures_fields
314 .extend(quote::quote!(<#as_type as ::goods::AssetField<#kind>>::Fut,));
315 decoded_fields.extend(
316 quote::quote!(<#as_type as ::goods::AssetField<#kind>>::Decoded,),
317 );
318 info_to_futures_fields
319 .extend(quote::quote!(<#as_type as ::goods::AssetField<#kind>>::decode(info.#index, loader),));
320 futures_to_decoded_fields.extend(quote::quote!(futures.#index.await.map_err(|err| #decode_error::#error_variant { source: err })?,));
321 decoded_to_asset_fields
322 .extend(quote::quote!(<#ty as ::std::convert::From<#as_type>>::from(<#as_type as ::goods::AssetFieldBuild<#kind, BuilderGenericParameter>>::build(decoded.#index, builder).map_err(|err| #build_error::#error_variant { source: err })?),));
323 }
324 }
325 }
326 _ => {
327 return Err(syn::Error::new_spanned(
328 &field.attrs[asset_attributes[1]],
329 "Only one of two attributes 'external' or 'container' can be specified",
330 ));
331 }
332 }
333 }
334
335 Ok(Parsed {
336 complex,
337 derive_input,
338 info,
339 futures,
340 decoded,
341 decode_error,
342 decode_field_errors,
343 build_error,
344 build_field_errors,
345 builder_bounds,
346 info_fields,
347 info_to_futures_fields,
348 futures_fields,
349 futures_to_decoded_fields,
350 decoded_fields,
351 decoded_to_asset_fields,
352 serde_attributes,
353 name: name_arg,
354 })
355}
356
357fn asset_impl(parsed: Parsed) -> syn::Result<proc_macro2::TokenStream> {
358 let Parsed {
359 complex,
360 derive_input,
361 info,
362 futures,
363 decoded,
364 decode_error,
365 build_error,
366 decode_field_errors,
367 build_field_errors,
368 builder_bounds,
369 info_fields,
370 info_to_futures_fields,
371 futures_fields,
372 futures_to_decoded_fields,
373 decoded_fields,
374 decoded_to_asset_fields,
375 serde_attributes,
376 name,
377 } = parsed;
378
379 let name = match name {
380 None => {
381 return Err(syn::Error::new_spanned(
382 derive_input,
383 "`derive(Asset)` requires `asset(name = \"<name>\")` attribute",
384 ));
385 }
386 Some(name) => name,
387 };
388
389 let data_struct = match &derive_input.data {
390 syn::Data::Struct(data) => data,
391 _ => unreachable!(),
392 };
393
394 let ty = &derive_input.ident;
395
396 let tokens = match data_struct.fields {
397 syn::Fields::Unit => quote::quote! {
398 impl ::goods::TrivialAsset for #ty {
399 type Error = ::std::convert::Infallible;
400
401 fn name() -> &'static str {
402 #name
403 }
404
405 fn decode(bytes: ::std::boxed::Box<[u8]>) -> Result<Self, ::std::convert::Infallible> {
406 ::std::result::Result::Ok(#ty)
407 }
408 }
409 },
410 syn::Fields::Unnamed(_) => todo!("Not yet implemented"),
411 syn::Fields::Named(_) if complex => quote::quote! {
412 #[derive(::goods::serde::Deserialize)]
413 #(#serde_attributes)*
414 struct #info { #info_fields }
415
416 struct #futures { #futures_fields }
417
418 pub struct #decoded { #decoded_fields }
419
420 #[derive(::std::fmt::Debug, ::goods::thiserror::Error)]
421 pub enum #decode_error {
422 #[error("Failed to deserialize asset info. {source:#}")]
423 Info { #[source] source: ::goods::DecodeError },
424
425 #decode_field_errors
426 }
427
428 #[derive(::std::fmt::Debug, ::goods::thiserror::Error)]
429 pub enum #build_error {
430 #build_field_errors
431 }
432
433 impl ::goods::Asset for #ty {
434 type BuildError = #build_error;
435 type DecodeError = #decode_error;
436 type Decoded = #decoded;
437 type Fut = ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<#decoded, #decode_error>> + Send>>;
438
439 fn name() -> &'static str {
440 #name
441 }
442
443 fn decode(bytes: ::std::boxed::Box<[u8]>, loader: &::goods::Loader) -> Self::Fut {
444 use {::std::{boxed::Box, result::Result::{self, Ok, Err}}, ::goods::serde_json::error::Category};
445
446 let result: Result<#info, #decode_error> = if bytes.is_empty() {
448 match ::goods::bincode::deserialize(&*bytes) {
449 Ok(value) => Ok(value),
450 Err(err) => Err(#decode_error:: Info { source: ::goods::DecodeError::Bincode(err) }),
451 }
452 } else {
453 match ::goods::serde_json::from_slice(&*bytes) {
454 Ok(value) => Ok(value),
455 Err(err) => match err.classify() {
456 Category::Syntax => {
457 match ::goods::bincode::deserialize(&*bytes) {
459 Ok(value) => Ok(value),
460 Err(err) => Err(#decode_error:: Info { source: ::goods::DecodeError::Bincode(err) }),
461 }
462 }
463 _ => Err(#decode_error::Info { source: ::goods::DecodeError::Json(err) }),
464 }
465 }
466 };
467
468 match result {
469 Ok(info) => {
470 let futures = #futures {
471 #info_to_futures_fields
472 };
473 Box::pin(async move {Ok(#decoded {
474 #futures_to_decoded_fields
475 })})
476 },
477 Err(err) => Box::pin(async move { Err(err) }),
478 }
479 }
480 }
481
482 impl<BuilderGenericParameter> ::goods::AssetBuild<BuilderGenericParameter> for #ty
483 where
484 #builder_bounds
485 {
486 fn build(decoded: #decoded, builder: &mut BuilderGenericParameter) -> Result<Self, #build_error> {
487 ::std::result::Result::Ok(#ty {
488 #decoded_to_asset_fields
489 })
490 }
491 }
492 },
493 syn::Fields::Named(_) => quote::quote! {
494 impl ::goods::TrivialAsset for #ty {
495 type Error = ::goods::DecodeError;
496
497 fn name() -> &'static str {
498 #name
499 }
500
501 fn decode(bytes: ::std::boxed::Box<[u8]>) -> Result<Self, ::goods::DecodeError> {
502 use {::std::result::Result::{Ok, Err}, ::goods::serde_json::error::Category};
503
504 #[derive(::goods::serde::Deserialize)]
505 #(#serde_attributes)*
506 struct #info { #info_fields }
507
508 let decoded: #info = if bytes.is_empty() {
510 match ::goods::bincode::deserialize(&*bytes) {
511 Ok(value) => value,
512 Err(err) => return Err(::goods::DecodeError::Bincode(err)),
513 }
514 } else {
515 match ::goods::serde_json::from_slice(&*bytes) {
516 Ok(value) => value,
517 Err(err) => match err.classify() {
518 Category::Syntax => {
519 match ::goods::bincode::deserialize(&*bytes) {
521 Ok(value) => value,
522 Err(err) => return Err(::goods::DecodeError::Bincode(err)),
523 }
524 }
525 _ => return Err(::goods::DecodeError::Json(err)),
526 }
527 }
528 };
529
530 Ok(#ty {
531 #decoded_to_asset_fields
532 })
533 }
534 }
535 },
536 };
537
538 Ok(tokens)
539}
540
541fn asset_field_impl(parsed: Parsed) -> syn::Result<proc_macro2::TokenStream> {
542 let Parsed {
543 complex,
544 derive_input,
545 info,
546 futures,
547 decoded,
548 decode_error,
549 build_error,
550 decode_field_errors,
551 build_field_errors,
552 builder_bounds,
553 info_fields,
554 info_to_futures_fields,
555 futures_fields,
556 futures_to_decoded_fields,
557 decoded_fields,
558 decoded_to_asset_fields,
559 serde_attributes,
560 name,
561 } = parsed;
562
563 if let Some(name) = name {
564 return Err(syn::Error::new_spanned(
565 name,
566 "`derive(AssetField)` does not accept `asset(name = \"<name>\")` attribute",
567 ));
568 };
569
570 let ty = &derive_input.ident;
571
572 let data_struct = match &derive_input.data {
573 syn::Data::Struct(data) => data,
574 _ => unreachable!(),
575 };
576
577 let tokens = match data_struct.fields {
578 syn::Fields::Unit => quote::quote! {
579 #[derive(::goods::serde::Deserialize)]
580 #(#serde_attributes)*
581 pub struct #info;
582
583 impl ::goods::AssetField<::goods::Container> for #ty {
584 type BuildError = ::std::convert::Infallible;
585 type DecodeError = ::std::convert::Infallible;
586 type Info = #info;
587 type Decoded = Self;
588 type Fut = ::std::future::Ready<Result<Self, ::std::convert::Infallible>>;
589
590 fn decode(info: #info, _: &::goods::Loader) -> Self::Fut {
591 use ::std::{future::ready, result::Result::Ok};
592
593 ready(Ok(#ty))
594 }
595 }
596
597 impl<BuilderGenericParameter> ::goods::AssetFieldBuild<::goods::Container, BuilderGenericParameter> for #ty {
598 fn build(decoded: Self, builder: &mut BuilderGenericParameter) -> Result<Self, ::std::convert::Infallible> {
599 ::std::result::Result::Ok(decoded)
600 }
601 }
602 },
603
604 syn::Fields::Unnamed(_) => todo!("Not yet implemented"),
605 syn::Fields::Named(_) if complex => quote::quote! {
606 #[derive(::goods::serde::Deserialize)]
607 #(#serde_attributes)*
608 pub struct #info { #info_fields }
609
610 pub struct #decoded { #decoded_fields }
611
612 #[derive(::std::fmt::Debug, ::goods::thiserror::Error)]
613 pub enum #decode_error {
614 #decode_field_errors
615 }
616
617 #[derive(::std::fmt::Debug, ::goods::thiserror::Error)]
618 pub enum #build_error {
619 #build_field_errors
620 }
621
622 impl ::goods::AssetField<::goods::Container> for #ty {
623 type BuildError = #build_error;
624 type DecodeError = #decode_error;
625 type Info = #info;
626 type Decoded = #decoded;
627 type Fut = ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = Result<#decoded, #decode_error>> + Send>>;
628
629 fn decode(info: #info, loader: &::goods::Loader) -> Self::Fut {
630 use ::std::{boxed::Box, result::Result::Ok};
631
632 struct #futures { #futures_fields }
633
634 let futures = #futures {
635 #info_to_futures_fields
636 };
637
638 Box::pin(async move {Ok(#decoded {
639 #futures_to_decoded_fields
640 })})
641 }
642 }
643
644 impl<BuilderGenericParameter> ::goods::AssetFieldBuild<::goods::Container, BuilderGenericParameter> for #ty
645 where
646 #builder_bounds
647 {
648 fn build(decoded: #decoded, builder: &mut BuilderGenericParameter) -> Result<Self, #build_error> {
649 ::std::result::Result::Ok(#ty {
650 #decoded_to_asset_fields
651 })
652 }
653 }
654 },
655 syn::Fields::Named(_) => quote::quote! {
656 #[derive(::goods::serde::Deserialize)]
657 #(#serde_attributes)*
658 pub struct #info { #info_fields }
659
660 impl ::goods::AssetField<::goods::Container> for #ty {
661 type BuildError = ::std::convert::Infallible;
662 type DecodeError = ::std::convert::Infallible;
663 type Info = #info;
664 type Decoded = Self;
665 type Fut = ::std::future::Ready<Result<Self, ::std::convert::Infallible>>;
666
667 fn decode(info: #info, _: &::goods::Loader) -> Self::Fut {
668 use ::std::{future::ready, result::Result::Ok};
669
670 let decoded = info;
671
672 ready(Ok(#ty {
673 #decoded_to_asset_fields
674 }))
675 }
676 }
677
678 impl<BuilderGenericParameter> ::goods::AssetFieldBuild<::goods::Container, BuilderGenericParameter> for #ty {
679 fn build(decoded: Self, builder: &mut BuilderGenericParameter) -> Result<Self, ::std::convert::Infallible> {
680 ::std::result::Result::Ok(decoded)
681 }
682 }
683 },
684 };
685
686 Ok(tokens)
687}
688
689fn snake_to_pascal(input: &syn::Ident) -> syn::Ident {
690 let mut result = String::new();
691 let mut upper = true;
692 for char in input.to_string().chars() {
693 if char.is_ascii_alphabetic() {
694 if upper {
695 upper = false;
696 result.extend(char.to_uppercase());
697 } else {
698 result.push(char);
699 }
700 } else if char.is_ascii_digit() {
701 upper = true;
702 result.push(char);
703 } else if char == '_' {
704 upper = true;
705 } else {
706 return input.clone();
707 }
708 }
709 syn::Ident::new(&result, input.span())
710}