goods_proc/
lib.rs

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                    // Zero-length is definitely bincode.
447                    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                                    // That's not json. Bincode then.
458                                    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                    /// Zero-length is definitely bincode.
509                    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                                    // That's not json. Bincode then.
520                                    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}