object_rainbow_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{
4    Attribute, Data, DeriveInput, Error, Generics, Ident, LitStr, Type, parse::Parse,
5    parse_macro_input, parse_quote, parse_quote_spanned, spanned::Spanned, token::Comma,
6};
7
8#[proc_macro_derive(ToOutput)]
9pub fn derive_to_output(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let name = input.ident;
12    let generics = match bounds_to_output(input.generics, &input.data) {
13        Ok(g) => g,
14        Err(e) => return e.into_compile_error().into(),
15    };
16    let to_output = gen_to_output(&input.data);
17    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
18    let output = quote! {
19        impl #impl_generics ::object_rainbow::ToOutput for #name #ty_generics #where_clause {
20            fn to_output(&self, output: &mut dyn ::object_rainbow::Output) {
21                #to_output
22            }
23        }
24    };
25    TokenStream::from(output)
26}
27
28fn bounds_to_output(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
29    match data {
30        Data::Struct(data) => {
31            for f in data.fields.iter() {
32                let ty = &f.ty;
33                generics
34                    .make_where_clause()
35                    .predicates
36                    .push(parse_quote_spanned! { ty.span() =>
37                        #ty: ::object_rainbow::ToOutput
38                    });
39            }
40        }
41        Data::Enum(data) => {
42            for v in data.variants.iter() {
43                for f in v.fields.iter() {
44                    let ty = &f.ty;
45                    generics.make_where_clause().predicates.push(
46                        parse_quote_spanned! { ty.span() =>
47                            #ty: ::object_rainbow::ToOutput
48                        },
49                    );
50                }
51            }
52        }
53        Data::Union(data) => {
54            return Err(Error::new_spanned(
55                data.union_token,
56                "`union`s are not supported",
57            ));
58        }
59    }
60    Ok(generics)
61}
62
63fn fields_to_output(fields: &syn::Fields) -> proc_macro2::TokenStream {
64    match fields {
65        syn::Fields::Named(fields) => {
66            let let_self = fields.named.iter().map(|f| f.ident.as_ref().unwrap());
67            let to_output = let_self.clone().zip(fields.named.iter()).map(|(i, f)| {
68                quote_spanned! { f.ty.span() =>
69                    #i.to_output(output)
70                }
71            });
72            quote! {
73                { #(#let_self),* } => {
74                    #(#to_output);*
75                }
76            }
77        }
78        syn::Fields::Unnamed(fields) => {
79            let let_self = fields
80                .unnamed
81                .iter()
82                .enumerate()
83                .map(|(i, f)| Ident::new(&format!("field{i}"), f.ty.span()));
84            let to_output = let_self.clone().zip(fields.unnamed.iter()).map(|(i, f)| {
85                quote_spanned! { f.ty.span() =>
86                    #i.to_output(output)
87                }
88            });
89            quote! {
90                (#(#let_self),*) => {
91                    #(#to_output);*
92                }
93            }
94        }
95        syn::Fields::Unit => quote! {
96            => {}
97        },
98    }
99}
100
101fn gen_to_output(data: &Data) -> proc_macro2::TokenStream {
102    match data {
103        Data::Struct(data) => {
104            let arm = fields_to_output(&data.fields);
105            quote! {
106                match self {
107                    Self #arm
108                }
109            }
110        }
111        Data::Enum(data) => {
112            let to_output = data.variants.iter().map(|v| {
113                let ident = &v.ident;
114                let arm = fields_to_output(&v.fields);
115                quote! { Self::#ident #arm }
116            });
117            quote! {
118                let kind = ::object_rainbow::Enum::kind(self);
119                let tag = ::object_rainbow::enumkind::EnumKind::to_tag(kind);
120                tag.to_output(output);
121                match self {
122                    #(#to_output)*
123                }
124            }
125        }
126        Data::Union(data) => {
127            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
128        }
129    }
130}
131
132#[proc_macro_derive(Topological)]
133pub fn derive_topological(input: TokenStream) -> TokenStream {
134    let input = parse_macro_input!(input as DeriveInput);
135    let name = input.ident;
136    let generics = match bounds_topological(input.generics, &input.data) {
137        Ok(g) => g,
138        Err(e) => return e.into_compile_error().into(),
139    };
140    let accept_points = gen_accept_points(&input.data);
141    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
142    let output = quote! {
143        impl #impl_generics ::object_rainbow::Topological for #name #ty_generics #where_clause {
144            fn accept_points(&self, visitor: &mut impl ::object_rainbow::PointVisitor) {
145                #accept_points
146            }
147        }
148    };
149    TokenStream::from(output)
150}
151
152fn bounds_topological(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
153    match data {
154        Data::Struct(data) => {
155            for f in data.fields.iter() {
156                let ty = &f.ty;
157                generics
158                    .make_where_clause()
159                    .predicates
160                    .push(parse_quote_spanned! { ty.span() =>
161                        #ty: ::object_rainbow::Topological
162                    });
163            }
164        }
165        Data::Enum(data) => {
166            for v in data.variants.iter() {
167                for f in v.fields.iter() {
168                    let ty = &f.ty;
169                    generics.make_where_clause().predicates.push(
170                        parse_quote_spanned! { ty.span() =>
171                            #ty: ::object_rainbow::Topological
172                        },
173                    );
174                }
175            }
176        }
177        Data::Union(data) => {
178            return Err(Error::new_spanned(
179                data.union_token,
180                "`union`s are not supported",
181            ));
182        }
183    }
184    Ok(generics)
185}
186
187fn fields_accept_points(fields: &syn::Fields) -> proc_macro2::TokenStream {
188    match fields {
189        syn::Fields::Named(fields) => {
190            let let_self = fields.named.iter().map(|f| f.ident.as_ref().unwrap());
191            let accept_points = let_self.clone().zip(fields.named.iter()).map(|(i, f)| {
192                quote_spanned! { f.ty.span() =>
193                    #i.accept_points(visitor)
194                }
195            });
196            quote! {
197                { #(#let_self),* } => {
198                    #(#accept_points);*
199                }
200            }
201        }
202        syn::Fields::Unnamed(fields) => {
203            let let_self = fields
204                .unnamed
205                .iter()
206                .enumerate()
207                .map(|(i, f)| Ident::new(&format!("field{i}"), f.ty.span()));
208            let accept_points = let_self.clone().zip(fields.unnamed.iter()).map(|(i, f)| {
209                quote_spanned! { f.ty.span() =>
210                    #i.accept_points(visitor)
211                }
212            });
213            quote! {
214                (#(#let_self),*) => {
215                    #(#accept_points);*
216                }
217            }
218        }
219        syn::Fields::Unit => quote! {
220            => {}
221        },
222    }
223}
224
225fn gen_accept_points(data: &Data) -> proc_macro2::TokenStream {
226    match data {
227        Data::Struct(data) => {
228            let arm = fields_accept_points(&data.fields);
229            quote! {
230                match self {
231                    Self #arm
232                }
233            }
234        }
235        Data::Enum(data) => {
236            let to_output = data.variants.iter().map(|v| {
237                let ident = &v.ident;
238                let arm = fields_accept_points(&v.fields);
239                quote! { Self::#ident #arm }
240            });
241            quote! {
242                let kind = ::object_rainbow::Enum::kind(self);
243                let tag = ::object_rainbow::enumkind::EnumKind::to_tag(kind);
244                tag.accept_points(visitor);
245                match self {
246                    #(#to_output)*
247                }
248            }
249        }
250        Data::Union(data) => {
251            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
252        }
253    }
254}
255
256#[proc_macro_derive(Tagged, attributes(tags))]
257pub fn derive_tagged(input: TokenStream) -> TokenStream {
258    let input = parse_macro_input!(input as DeriveInput);
259    let name = input.ident;
260    let mut errors = Vec::new();
261    let generics = match bounds_tagged(input.generics, &input.data, &mut errors) {
262        Ok(g) => g,
263        Err(e) => return e.into_compile_error().into(),
264    };
265    let tags = gen_tags(&input.data, &input.attrs, &mut errors);
266    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
267    let errors = errors.into_iter().map(|e| e.into_compile_error());
268    let output = quote! {
269        #(#errors)*
270
271        impl #impl_generics ::object_rainbow::Tagged for #name #ty_generics #where_clause {
272            const TAGS: ::object_rainbow::Tags = #tags;
273        }
274    };
275    TokenStream::from(output)
276}
277
278struct FieldTagArgs {
279    skip: bool,
280}
281
282impl Parse for FieldTagArgs {
283    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
284        let mut skip = false;
285        while !input.is_empty() {
286            let ident = input.parse::<Ident>()?;
287            if ident.to_string().as_str() != "skip" {
288                return Err(Error::new(ident.span(), "expected: skip"));
289            }
290            skip = true;
291            if !input.is_empty() {
292                input.parse::<Comma>()?;
293            }
294        }
295        Ok(Self { skip })
296    }
297}
298
299fn bounds_tagged(
300    mut generics: Generics,
301    data: &Data,
302    errors: &mut Vec<Error>,
303) -> syn::Result<Generics> {
304    match data {
305        Data::Struct(data) => {
306            for f in data.fields.iter() {
307                let mut skip = false;
308                for attr in &f.attrs {
309                    if attr_str(attr).as_deref() == Some("tags") {
310                        match attr.parse_args::<FieldTagArgs>() {
311                            Ok(args) => skip |= args.skip,
312                            Err(e) => errors.push(e),
313                        }
314                    }
315                }
316                if !skip {
317                    let ty = &f.ty;
318                    generics.make_where_clause().predicates.push(
319                        parse_quote_spanned! { ty.span() =>
320                            #ty: ::object_rainbow::Tagged
321                        },
322                    );
323                }
324            }
325        }
326        Data::Enum(data) => {
327            for v in data.variants.iter() {
328                for f in v.fields.iter() {
329                    let mut skip = false;
330                    for attr in &f.attrs {
331                        if attr_str(attr).as_deref() == Some("tags") {
332                            match attr.parse_args::<FieldTagArgs>() {
333                                Ok(args) => skip |= args.skip,
334                                Err(e) => errors.push(e),
335                            }
336                        }
337                    }
338                    if !skip {
339                        let ty = &f.ty;
340                        generics.make_where_clause().predicates.push(
341                            parse_quote_spanned! { ty.span() =>
342                                #ty: ::object_rainbow::Tagged
343                            },
344                        );
345                    }
346                }
347            }
348        }
349        Data::Union(data) => {
350            return Err(Error::new_spanned(
351                data.union_token,
352                "`union`s are not supported",
353            ));
354        }
355    }
356    Ok(generics)
357}
358
359struct StructTagArgs {
360    tags: Vec<LitStr>,
361}
362
363impl Parse for StructTagArgs {
364    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
365        let mut tags = Vec::new();
366        while !input.is_empty() {
367            let tag = input.parse::<LitStr>()?;
368            tags.push(tag);
369            if !input.is_empty() {
370                input.parse::<Comma>()?;
371            }
372        }
373        Ok(Self { tags })
374    }
375}
376
377fn fields_tags(fields: &syn::Fields) -> Vec<proc_macro2::TokenStream> {
378    fields
379        .iter()
380        .filter_map(|f| {
381            let mut skip = false;
382            for attr in &f.attrs {
383                if attr_str(attr).as_deref() == Some("tags") {
384                    skip |= attr.parse_args::<FieldTagArgs>().ok()?.skip;
385                }
386            }
387            let ty = &f.ty;
388            (!skip).then_some(quote! { <#ty as ::object_rainbow::Tagged>::TAGS })
389        })
390        .collect()
391}
392
393fn gen_tags(data: &Data, attrs: &[Attribute], errors: &mut Vec<Error>) -> proc_macro2::TokenStream {
394    match data {
395        Data::Struct(data) => {
396            let mut tags = Vec::new();
397            for attr in attrs {
398                if attr_str(attr).as_deref() == Some("tags") {
399                    match attr.parse_args::<StructTagArgs>() {
400                        Ok(mut args) => tags.append(&mut args.tags),
401                        Err(e) => errors.push(e),
402                    }
403                }
404            }
405            let nested = fields_tags(&data.fields);
406            if nested.len() == 1 && tags.is_empty() {
407                let nested = nested.into_iter().next().unwrap();
408                quote! {
409                    #nested
410                }
411            } else {
412                quote! {
413                    ::object_rainbow::Tags(&[#(#tags),*], &[#(&#nested),*])
414                }
415            }
416        }
417        Data::Enum(data) => {
418            let mut tags = Vec::new();
419            for attr in attrs {
420                if attr_str(attr).as_deref() == Some("tags") {
421                    match attr.parse_args::<StructTagArgs>() {
422                        Ok(mut args) => tags.append(&mut args.tags),
423                        Err(e) => errors.push(e),
424                    }
425                }
426            }
427            let mut nested: Vec<_> = data
428                .variants
429                .iter()
430                .flat_map(|v| fields_tags(&v.fields))
431                .collect();
432            let kind_tags = quote! {
433                <
434                    <
435                        <
436                            Self
437                            as
438                            ::object_rainbow::Enum
439                        >::Kind
440                        as
441                        ::object_rainbow::enumkind::EnumKind
442                    >::Tag
443                    as  ::object_rainbow::Tagged
444                >::TAGS
445            };
446            nested.insert(0, kind_tags);
447            if nested.len() == 1 && tags.is_empty() {
448                let nested = nested.into_iter().next().unwrap();
449                quote! {
450                    #nested
451                }
452            } else {
453                quote! {
454                    ::object_rainbow::Tags(&[#(#tags),*], &[#(&#nested),*])
455                }
456            }
457        }
458        Data::Union(data) => {
459            Error::new_spanned(data.union_token, "`union`s are not supported").into_compile_error()
460        }
461    }
462}
463
464#[proc_macro_derive(Object)]
465pub fn derive_object(input: TokenStream) -> TokenStream {
466    let input = parse_macro_input!(input as DeriveInput);
467    let name = input.ident;
468    let generics = match bounds_object(input.generics, &input.data) {
469        Ok(g) => g,
470        Err(e) => return e.into_compile_error().into(),
471    };
472    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
473    let output = quote! {
474        impl #impl_generics ::object_rainbow::Object for #name #ty_generics #where_clause {}
475    };
476    TokenStream::from(output)
477}
478
479fn bounds_object(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
480    match data {
481        Data::Struct(data) => {
482            let last_at = data.fields.len().checked_sub(1).unwrap_or_default();
483            for (i, f) in data.fields.iter().enumerate() {
484                let last = i == last_at;
485                let ty = &f.ty;
486                let tr = if last {
487                    quote!(::object_rainbow::Object)
488                } else {
489                    quote!(::object_rainbow::Inline)
490                };
491                generics
492                    .make_where_clause()
493                    .predicates
494                    .push(parse_quote_spanned! { ty.span() =>
495                        #ty: #tr
496                    });
497            }
498        }
499        Data::Enum(data) => {
500            for v in data.variants.iter() {
501                let last_at = v.fields.len().checked_sub(1).unwrap_or_default();
502                for (i, f) in v.fields.iter().enumerate() {
503                    let last = i == last_at;
504                    let ty = &f.ty;
505                    let tr = if last {
506                        quote!(::object_rainbow::Object)
507                    } else {
508                        quote!(::object_rainbow::Inline)
509                    };
510                    generics.make_where_clause().predicates.push(
511                        parse_quote_spanned! { ty.span() =>
512                            #ty: #tr
513                        },
514                    );
515                }
516            }
517        }
518        Data::Union(data) => {
519            return Err(Error::new_spanned(
520                data.union_token,
521                "`union`s are not supported",
522            ));
523        }
524    }
525    Ok(generics)
526}
527
528#[proc_macro_derive(Inline)]
529pub fn derive_inline(input: TokenStream) -> TokenStream {
530    let input = parse_macro_input!(input as DeriveInput);
531    let name = input.ident;
532    let generics = match bounds_inline(input.generics, &input.data) {
533        Ok(g) => g,
534        Err(e) => return e.into_compile_error().into(),
535    };
536    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
537    let output = quote! {
538        impl #impl_generics ::object_rainbow::Inline for #name #ty_generics #where_clause {}
539    };
540    TokenStream::from(output)
541}
542
543fn bounds_inline(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
544    match data {
545        Data::Struct(data) => {
546            for f in data.fields.iter() {
547                let ty = &f.ty;
548                generics
549                    .make_where_clause()
550                    .predicates
551                    .push(parse_quote_spanned! { ty.span() =>
552                        #ty: ::object_rainbow::Inline
553                    });
554            }
555        }
556        Data::Enum(data) => {
557            for v in data.variants.iter() {
558                for f in v.fields.iter() {
559                    let ty = &f.ty;
560                    generics.make_where_clause().predicates.push(
561                        parse_quote_spanned! { ty.span() =>
562                            #ty: ::object_rainbow::Inline
563                        },
564                    );
565                }
566            }
567        }
568        Data::Union(data) => {
569            return Err(Error::new_spanned(
570                data.union_token,
571                "`union`s are not supported",
572            ));
573        }
574    }
575    Ok(generics)
576}
577
578#[proc_macro_derive(ReflessObject)]
579pub fn derive_refless_object(input: TokenStream) -> TokenStream {
580    let input = parse_macro_input!(input as DeriveInput);
581    let name = input.ident;
582    let generics = match bounds_refless_object(input.generics, &input.data) {
583        Ok(g) => g,
584        Err(e) => return e.into_compile_error().into(),
585    };
586    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
587    let output = quote! {
588        impl #impl_generics ::object_rainbow::ReflessObject for #name #ty_generics #where_clause {}
589    };
590    TokenStream::from(output)
591}
592
593fn bounds_refless_object(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
594    match data {
595        Data::Struct(data) => {
596            let last_at = data.fields.len().checked_sub(1).unwrap_or_default();
597            for (i, f) in data.fields.iter().enumerate() {
598                let last = i == last_at;
599                let ty = &f.ty;
600                let tr = if last {
601                    quote!(::object_rainbow::ReflessObject)
602                } else {
603                    quote!(::object_rainbow::ReflessInline)
604                };
605                generics
606                    .make_where_clause()
607                    .predicates
608                    .push(parse_quote_spanned! { ty.span() =>
609                        #ty: #tr
610                    });
611            }
612        }
613        Data::Enum(data) => {
614            for v in data.variants.iter() {
615                let last_at = v.fields.len().checked_sub(1).unwrap_or_default();
616                for (i, f) in v.fields.iter().enumerate() {
617                    let last = i == last_at;
618                    let ty = &f.ty;
619                    let tr = if last {
620                        quote!(::object_rainbow::ReflessObject)
621                    } else {
622                        quote!(::object_rainbow::ReflessInline)
623                    };
624                    generics.make_where_clause().predicates.push(
625                        parse_quote_spanned! { ty.span() =>
626                            #ty: #tr
627                        },
628                    );
629                }
630            }
631        }
632        Data::Union(data) => {
633            return Err(Error::new_spanned(
634                data.union_token,
635                "`union`s are not supported",
636            ));
637        }
638    }
639    Ok(generics)
640}
641
642#[proc_macro_derive(ReflessInline)]
643pub fn derive_refless_inline(input: TokenStream) -> TokenStream {
644    let input = parse_macro_input!(input as DeriveInput);
645    let name = input.ident;
646    let generics = match bounds_refless_inline(input.generics, &input.data) {
647        Ok(g) => g,
648        Err(e) => return e.into_compile_error().into(),
649    };
650    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
651    let output = quote! {
652        impl #impl_generics ::object_rainbow::ReflessInline for #name #ty_generics #where_clause {}
653    };
654    TokenStream::from(output)
655}
656
657fn bounds_refless_inline(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
658    match data {
659        Data::Struct(data) => {
660            for f in data.fields.iter() {
661                let ty = &f.ty;
662                generics
663                    .make_where_clause()
664                    .predicates
665                    .push(parse_quote_spanned! { ty.span() =>
666                        #ty: ::object_rainbow::ReflessInline
667                    });
668            }
669        }
670        Data::Enum(data) => {
671            for v in data.variants.iter() {
672                for f in v.fields.iter() {
673                    let ty = &f.ty;
674                    generics.make_where_clause().predicates.push(
675                        parse_quote_spanned! { ty.span() =>
676                            #ty: ::object_rainbow::ReflessInline
677                        },
678                    );
679                }
680            }
681        }
682        Data::Union(data) => {
683            return Err(Error::new_spanned(
684                data.union_token,
685                "`union`s are not supported",
686            ));
687        }
688    }
689    Ok(generics)
690}
691
692#[proc_macro_derive(Size)]
693pub fn derive_size(input: TokenStream) -> TokenStream {
694    let input = parse_macro_input!(input as DeriveInput);
695    let name = input.ident;
696    let size_arr = gen_size_arr(&input.data);
697    let size = gen_size(&input.data);
698    let generics = match bounds_size(input.generics.clone(), &input.data, &size_arr) {
699        Ok(g) => g,
700        Err(e) => return e.into_compile_error().into(),
701    };
702    let (_, ty_generics, where_clause) = generics.split_for_impl();
703    let mut generics = input.generics;
704    generics.params.push(parse_quote!(
705        __Output: ::object_rainbow::typenum::Unsigned
706    ));
707    let (impl_generics, _, _) = generics.split_for_impl();
708    let output = quote! {
709        const _: () = {
710            use ::object_rainbow::typenum::tarr;
711
712            impl #impl_generics ::object_rainbow::Size for #name #ty_generics #where_clause {
713                const SIZE: usize = #size;
714
715                type Size = <#size_arr as ::object_rainbow::typenum::FoldAdd>::Output;
716            }
717        };
718    };
719    TokenStream::from(output)
720}
721
722fn bounds_size(
723    mut generics: Generics,
724    data: &Data,
725    size_arr: &proc_macro2::TokenStream,
726) -> syn::Result<Generics> {
727    match data {
728        Data::Struct(data) => {
729            for f in data.fields.iter() {
730                let ty = &f.ty;
731                generics
732                    .make_where_clause()
733                    .predicates
734                    .push(parse_quote_spanned! { ty.span() =>
735                        #ty: ::object_rainbow::Size
736                    });
737            }
738        }
739        Data::Enum(data) => {
740            for v in data.variants.iter() {
741                for f in v.fields.iter() {
742                    let ty = &f.ty;
743                    generics.make_where_clause().predicates.push(
744                        parse_quote_spanned! { ty.span() =>
745                            #ty: ::object_rainbow::Size
746                        },
747                    );
748                }
749            }
750            for v in data.variants.iter().skip(1) {
751                let arr = fields_size_arr(&v.fields, true);
752                generics.make_where_clause().predicates.push(parse_quote!(
753                    #arr: ::object_rainbow::typenum::FoldAdd<Output = __Output>
754                ));
755            }
756        }
757        Data::Union(data) => {
758            return Err(Error::new_spanned(
759                data.union_token,
760                "`union`s are not supported",
761            ));
762        }
763    }
764    generics.make_where_clause().predicates.push(parse_quote!(
765        #size_arr: ::object_rainbow::typenum::FoldAdd<Output = __Output>
766    ));
767    Ok(generics)
768}
769
770fn fields_size_arr(fields: &syn::Fields, as_enum: bool) -> proc_macro2::TokenStream {
771    let kind_size = quote! {
772        <
773            <
774                <
775                    Self
776                    as
777                    ::object_rainbow::Enum
778                >::Kind
779                as
780                ::object_rainbow::enumkind::EnumKind
781            >::Tag
782            as  ::object_rainbow::Size
783        >::Size
784    };
785    if fields.is_empty() {
786        return if as_enum {
787            quote! { tarr![#kind_size, ::object_rainbow::typenum::consts::U0] }
788        } else {
789            quote! { tarr![::object_rainbow::typenum::consts::U0] }
790        };
791    }
792    let size_arr = fields.iter().map(|f| {
793        let ty = &f.ty;
794        quote! { <#ty as ::object_rainbow::Size>::Size }
795    });
796    if as_enum {
797        quote! { tarr![#kind_size, #(#size_arr),*] }
798    } else {
799        quote! { tarr![#(#size_arr),*] }
800    }
801}
802
803fn gen_size_arr(data: &Data) -> proc_macro2::TokenStream {
804    match data {
805        Data::Struct(data) => fields_size_arr(&data.fields, false),
806        Data::Enum(data) => {
807            if let Some(v) = data.variants.first() {
808                fields_size_arr(&v.fields, true)
809            } else {
810                Error::new_spanned(data.enum_token, "empty `enum`s are not supported")
811                    .into_compile_error()
812            }
813        }
814        Data::Union(data) => {
815            Error::new_spanned(data.union_token, "`union`s are not supported").into_compile_error()
816        }
817    }
818}
819
820fn fields_size(fields: &syn::Fields) -> proc_macro2::TokenStream {
821    if fields.is_empty() {
822        return quote! {0};
823    }
824    let size = fields.iter().map(|f| {
825        let ty = &f.ty;
826        quote! { <#ty as ::object_rainbow::Size>::SIZE }
827    });
828    quote! {
829        #(#size)+*
830    }
831}
832
833fn gen_size(data: &Data) -> proc_macro2::TokenStream {
834    match data {
835        Data::Struct(data) => fields_size(&data.fields),
836        Data::Enum(data) => {
837            if let Some(v) = data.variants.first() {
838                let size = fields_size(&v.fields);
839                let kind_size = quote! {
840                    <
841                        <
842                            <
843                                Self
844                                as
845                                ::object_rainbow::Enum
846                            >::Kind
847                            as
848                            ::object_rainbow::enumkind::EnumKind
849                        >::Tag
850                        as  ::object_rainbow::Size
851                    >::SIZE
852                };
853                quote! { #kind_size + #size }
854            } else {
855                Error::new_spanned(data.enum_token, "empty `enum`s are not supported")
856                    .into_compile_error()
857            }
858        }
859        Data::Union(data) => {
860            Error::new_spanned(data.union_token, "`union`s are not supported").into_compile_error()
861        }
862    }
863}
864
865#[proc_macro_derive(Parse)]
866pub fn derive_parse(input: TokenStream) -> TokenStream {
867    let input = parse_macro_input!(input as DeriveInput);
868    let name = input.ident;
869    let generics = input.generics.clone();
870    let (_, ty_generics, _) = generics.split_for_impl();
871    let generics = match bounds_parse(input.generics, &input.data) {
872        Ok(g) => g,
873        Err(e) => return e.into_compile_error().into(),
874    };
875    let parse = gen_parse(&input.data);
876    let (impl_generics, _, where_clause) = generics.split_for_impl();
877    let output = quote! {
878        impl #impl_generics ::object_rainbow::Parse<__I> for #name #ty_generics #where_clause {
879            fn parse(mut input: __I) -> ::object_rainbow::Result<Self> {
880                #parse
881            }
882        }
883    };
884    TokenStream::from(output)
885}
886
887fn bounds_parse(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
888    match data {
889        Data::Struct(data) => {
890            let last_at = data.fields.len().checked_sub(1).unwrap_or_default();
891            for (i, f) in data.fields.iter().enumerate() {
892                let last = i == last_at;
893                let ty = &f.ty;
894                let tr = if last {
895                    quote!(::object_rainbow::Parse<__I>)
896                } else {
897                    quote!(::object_rainbow::ParseInline<__I>)
898                };
899                generics
900                    .make_where_clause()
901                    .predicates
902                    .push(parse_quote_spanned! { ty.span() =>
903                        #ty: #tr
904                    });
905            }
906        }
907        Data::Enum(data) => {
908            for v in data.variants.iter() {
909                let last_at = v.fields.len().checked_sub(1).unwrap_or_default();
910                for (i, f) in v.fields.iter().enumerate() {
911                    let last = i == last_at;
912                    let ty = &f.ty;
913                    let tr = if last {
914                        quote!(::object_rainbow::Parse<__I>)
915                    } else {
916                        quote!(::object_rainbow::ParseInline<__I>)
917                    };
918                    generics.make_where_clause().predicates.push(
919                        parse_quote_spanned! { ty.span() =>
920                            #ty: #tr
921                        },
922                    );
923                }
924            }
925        }
926        Data::Union(data) => {
927            return Err(Error::new_spanned(
928                data.union_token,
929                "`union`s are not supported",
930            ));
931        }
932    }
933    generics
934        .params
935        .push(parse_quote!(__I: ::object_rainbow::ParseInput));
936    Ok(generics)
937}
938
939fn gen_parse(data: &Data) -> proc_macro2::TokenStream {
940    match data {
941        Data::Struct(data) => {
942            let arm = fields_parse(&data.fields);
943            quote! { Ok(Self #arm)}
944        }
945        Data::Enum(data) => {
946            let parse = data.variants.iter().map(|v| {
947                let ident = &v.ident;
948                let arm = fields_parse(&v.fields);
949                quote! {
950                    <Self as ::object_rainbow::Enum>::Kind::#ident => Self::#ident #arm,
951                }
952            });
953            quote! {
954                Ok(match input.parse_inline()? {
955                    #(#parse)*
956                })
957            }
958        }
959        Data::Union(data) => {
960            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
961        }
962    }
963}
964
965fn fields_parse(fields: &syn::Fields) -> proc_macro2::TokenStream {
966    let last_at = fields.len().checked_sub(1).unwrap_or_default();
967    match fields {
968        syn::Fields::Named(fields) => {
969            let parse = fields.named.iter().enumerate().map(|(i, f)| {
970                let last = i == last_at;
971                let i = f.ident.as_ref().unwrap();
972                let method = if last {
973                    quote!(parse)
974                } else {
975                    quote!(parse_inline)
976                };
977                quote_spanned! { f.ty.span() =>
978                    #i: input.#method()?
979                }
980            });
981            quote! { { #(#parse),* } }
982        }
983        syn::Fields::Unnamed(fields) => {
984            let parse = fields.unnamed.iter().enumerate().map(|(i, f)| {
985                let last = i == last_at;
986                let method = if last {
987                    quote!(parse)
988                } else {
989                    quote!(parse_inline)
990                };
991                quote_spanned! { f.ty.span() =>
992                    input.#method()?
993                }
994            });
995            quote! { (#(#parse),*) }
996        }
997        syn::Fields::Unit => quote! {},
998    }
999}
1000
1001#[proc_macro_derive(ParseInline)]
1002pub fn derive_parse_inline(input: TokenStream) -> TokenStream {
1003    let input = parse_macro_input!(input as DeriveInput);
1004    let name = input.ident;
1005    let generics = input.generics.clone();
1006    let (_, ty_generics, _) = generics.split_for_impl();
1007    let generics = match bounds_parse_inline(input.generics, &input.data) {
1008        Ok(g) => g,
1009        Err(e) => return e.into_compile_error().into(),
1010    };
1011    let parse_inline = gen_parse_inline(&input.data);
1012    let (impl_generics, _, where_clause) = generics.split_for_impl();
1013    let output = quote! {
1014        impl #impl_generics ::object_rainbow::ParseInline<__I> for #name #ty_generics #where_clause {
1015            fn parse_inline(input: &mut __I) -> ::object_rainbow::Result<Self> {
1016                #parse_inline
1017            }
1018        }
1019    };
1020    TokenStream::from(output)
1021}
1022
1023fn bounds_parse_inline(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
1024    match data {
1025        Data::Struct(data) => {
1026            for f in data.fields.iter() {
1027                let ty = &f.ty;
1028                generics
1029                    .make_where_clause()
1030                    .predicates
1031                    .push(parse_quote_spanned! { ty.span() =>
1032                        #ty: ::object_rainbow::ParseInline<__I>
1033                    });
1034            }
1035        }
1036        Data::Enum(data) => {
1037            for v in data.variants.iter() {
1038                for f in v.fields.iter() {
1039                    let ty = &f.ty;
1040                    generics.make_where_clause().predicates.push(
1041                        parse_quote_spanned! { ty.span() =>
1042                            #ty: ::object_rainbow::ParseInline<__I>
1043                        },
1044                    );
1045                }
1046            }
1047        }
1048        Data::Union(data) => {
1049            return Err(Error::new_spanned(
1050                data.union_token,
1051                "`union`s are not supported",
1052            ));
1053        }
1054    }
1055    generics
1056        .params
1057        .push(parse_quote!(__I: ::object_rainbow::ParseInput));
1058    Ok(generics)
1059}
1060
1061fn fields_parse_inline(fields: &syn::Fields) -> proc_macro2::TokenStream {
1062    match fields {
1063        syn::Fields::Named(fields) => {
1064            let parse = fields.named.iter().map(|f| {
1065                let i = f.ident.as_ref().unwrap();
1066                quote_spanned! { f.ty.span() =>
1067                    #i: input.parse_inline()?
1068                }
1069            });
1070            quote! { { #(#parse),* } }
1071        }
1072        syn::Fields::Unnamed(fields) => {
1073            let parse = fields.unnamed.iter().map(|f| {
1074                quote_spanned! { f.ty.span() =>
1075                    input.parse_inline()?
1076                }
1077            });
1078            quote! { (#(#parse),*) }
1079        }
1080        syn::Fields::Unit => quote! {},
1081    }
1082}
1083
1084fn gen_parse_inline(data: &Data) -> proc_macro2::TokenStream {
1085    match data {
1086        Data::Struct(data) => {
1087            let arm = fields_parse_inline(&data.fields);
1088            quote! { Ok(Self #arm) }
1089        }
1090        Data::Enum(data) => {
1091            let parse_inline = data.variants.iter().map(|v| {
1092                let ident = &v.ident;
1093                let arm = fields_parse_inline(&v.fields);
1094                quote! {
1095                    <Self as ::object_rainbow::Enum>::Kind::#ident => Self::#ident #arm,
1096                }
1097            });
1098            quote! {
1099                Ok(match input.parse_inline()? {
1100                    #(#parse_inline)*
1101                })
1102            }
1103        }
1104        Data::Union(data) => {
1105            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1106        }
1107    }
1108}
1109
1110#[proc_macro_derive(ParseAsInline)]
1111pub fn derive_parse_as_inline(input: TokenStream) -> TokenStream {
1112    let input = parse_macro_input!(input as DeriveInput);
1113    let name = input.ident;
1114    let generics = input.generics.clone();
1115    let (_, ty_generics, _) = generics.split_for_impl();
1116    let generics = match bounds_parse_as_inline(input.generics, &name) {
1117        Ok(g) => g,
1118        Err(e) => return e.into_compile_error().into(),
1119    };
1120    let (impl_generics, _, where_clause) = generics.split_for_impl();
1121    let output = quote! {
1122        impl #impl_generics ::object_rainbow::Parse<__I> for #name #ty_generics #where_clause {
1123            fn parse(input: __I) -> ::object_rainbow::Result<Self> {
1124                ::object_rainbow::ParseInline::<__I>::parse_as_inline(input)
1125            }
1126        }
1127    };
1128    TokenStream::from(output)
1129}
1130
1131fn bounds_parse_as_inline(mut generics: Generics, name: &Ident) -> syn::Result<Generics> {
1132    generics
1133        .make_where_clause()
1134        .predicates
1135        .push(parse_quote_spanned! { name.span() =>
1136            Self: ::object_rainbow::ParseInline::<__I>
1137        });
1138    generics
1139        .params
1140        .push(parse_quote!(__I: ::object_rainbow::ParseInput));
1141    Ok(generics)
1142}
1143
1144fn parse_path(attr: &Attribute) -> syn::Result<Type> {
1145    attr.parse_args::<LitStr>()?.parse()
1146}
1147
1148fn attr_str(attr: &Attribute) -> Option<String> {
1149    Some(attr.path().get_ident()?.to_string())
1150}
1151
1152#[proc_macro_derive(Enum, attributes(enumtag))]
1153pub fn derive_enum(input: TokenStream) -> TokenStream {
1154    let input = parse_macro_input!(input as DeriveInput);
1155    let name = input.ident;
1156    let generics = input.generics.clone();
1157    let (_, ty_generics, _) = generics.split_for_impl();
1158    let generics = input.generics;
1159    let variants = gen_variants(&input.data);
1160    let variant_count = gen_variant_count(&input.data);
1161    let to_tag = gen_to_tag(&input.data);
1162    let from_tag = gen_from_tag(&input.data);
1163    let kind = gen_kind(&input.data);
1164    let (impl_generics, _, where_clause) = generics.split_for_impl();
1165    let mut errors = Vec::new();
1166    let mut enumtag = None;
1167    for attr in &input.attrs {
1168        if attr_str(attr).as_deref() == Some("enumtag") {
1169            match parse_path(attr) {
1170                Ok(path) => {
1171                    if enumtag.is_some() {
1172                        errors.push(Error::new_spanned(path, "duplicate tag"));
1173                    } else {
1174                        enumtag = Some(path);
1175                    }
1176                }
1177                Err(e) => errors.push(e),
1178            }
1179        }
1180    }
1181    let enumtag = enumtag
1182        .unwrap_or_else(|| parse_quote!(::object_rainbow::numeric::Le<::core::num::NonZero<u8>>));
1183    let errors = errors.into_iter().map(|e| e.into_compile_error());
1184    let output = quote! {
1185        const _: () = {
1186            #(#errors)*
1187
1188            use ::object_rainbow::enumkind::EnumKind;
1189
1190            #[derive(Clone, Copy, ::object_rainbow::ParseAsInline)]
1191            enum __Kind {
1192                #variants
1193            }
1194
1195            impl ::object_rainbow::enumkind::EnumKind for __Kind {
1196                type Tag = ::object_rainbow::enumkind::EnumTag<
1197                    #enumtag,
1198                    #variant_count,
1199                >;
1200
1201                fn to_tag(self) -> Self::Tag {
1202                    #to_tag
1203                }
1204
1205                fn from_tag(tag: Self::Tag) -> Self {
1206                    #from_tag
1207                }
1208            }
1209
1210            impl<I: ::object_rainbow::ParseInput> ::object_rainbow::ParseInline<I> for __Kind {
1211                fn parse_inline(input: &mut I) -> ::object_rainbow::Result<Self> {
1212                    Ok(::object_rainbow::enumkind::EnumKind::from_tag(input.parse_inline()?))
1213                }
1214            }
1215
1216            impl #impl_generics ::object_rainbow::Enum for #name #ty_generics #where_clause {
1217                type Kind = __Kind;
1218
1219                fn kind(&self) -> Self::Kind {
1220                    #kind
1221                }
1222            }
1223        };
1224    };
1225    TokenStream::from(output)
1226}
1227
1228fn gen_variants(data: &Data) -> proc_macro2::TokenStream {
1229    match data {
1230        Data::Struct(data) => {
1231            Error::new_spanned(data.struct_token, "`struct`s are not supported").to_compile_error()
1232        }
1233        Data::Enum(data) => {
1234            let variants = data.variants.iter().map(|v| &v.ident);
1235            quote! { #(#variants),* }
1236        }
1237        Data::Union(data) => {
1238            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1239        }
1240    }
1241}
1242
1243fn gen_variant_count(data: &Data) -> proc_macro2::TokenStream {
1244    match data {
1245        Data::Struct(data) => {
1246            Error::new_spanned(data.struct_token, "`struct`s are not supported").to_compile_error()
1247        }
1248        Data::Enum(data) => {
1249            let variant_count = data.variants.len();
1250            quote! { #variant_count }
1251        }
1252        Data::Union(data) => {
1253            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1254        }
1255    }
1256}
1257
1258fn gen_to_tag(data: &Data) -> proc_macro2::TokenStream {
1259    match data {
1260        Data::Struct(data) => {
1261            Error::new_spanned(data.struct_token, "`struct`s are not supported").to_compile_error()
1262        }
1263        Data::Enum(data) => {
1264            let to_tag = data.variants.iter().enumerate().map(|(i, v)| {
1265                let ident = &v.ident;
1266                quote_spanned! { ident.span() =>
1267                    Self::#ident => ::object_rainbow::enumkind::EnumTag::from_const::<#i>(),
1268                }
1269            });
1270            quote! {
1271                match self {
1272                    #(#to_tag)*
1273                }
1274            }
1275        }
1276        Data::Union(data) => {
1277            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1278        }
1279    }
1280}
1281
1282fn gen_from_tag(data: &Data) -> proc_macro2::TokenStream {
1283    match data {
1284        Data::Struct(data) => {
1285            Error::new_spanned(data.struct_token, "`struct`s are not supported").to_compile_error()
1286        }
1287        Data::Enum(data) => {
1288            let from_tag = data.variants.iter().enumerate().map(|(i, v)| {
1289                let ident = &v.ident;
1290                quote_spanned! { ident.span() =>
1291                    #i => Self::#ident,
1292                }
1293            });
1294            quote! {
1295                match tag.to_usize() {
1296                    #(#from_tag)*
1297                    _ => unreachable!(),
1298                }
1299            }
1300        }
1301        Data::Union(data) => {
1302            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1303        }
1304    }
1305}
1306
1307fn gen_kind(data: &Data) -> proc_macro2::TokenStream {
1308    match data {
1309        Data::Struct(data) => {
1310            Error::new_spanned(data.struct_token, "`struct`s are not supported").to_compile_error()
1311        }
1312        Data::Enum(data) => {
1313            let variants = data.variants.iter().map(|v| {
1314                let ident = &v.ident;
1315                quote_spanned! { ident.span() =>
1316                    Self::#ident {..} => __Kind::#ident,
1317                }
1318            });
1319            quote! {
1320                match self {
1321                    #(#variants)*
1322                }
1323            }
1324        }
1325        Data::Union(data) => {
1326            Error::new_spanned(data.union_token, "`union`s are not supported").to_compile_error()
1327        }
1328    }
1329}
1330
1331#[proc_macro_derive(MaybeHasNiche)]
1332pub fn derive_maybe_has_niche(input: TokenStream) -> TokenStream {
1333    let input = parse_macro_input!(input as DeriveInput);
1334    let name = input.ident;
1335    let mn_array = gen_mn_array(&input.data);
1336    let (_, ty_generics, _) = input.generics.split_for_impl();
1337    let generics = match bounds_maybe_has_niche(input.generics.clone(), &input.data) {
1338        Ok(g) => g,
1339        Err(e) => return e.into_compile_error().into(),
1340    };
1341    let (impl_generics, _, where_clause) = generics.split_for_impl();
1342    let output = quote! {
1343        const _: () = {
1344            use ::object_rainbow::typenum::tarr;
1345
1346            impl #impl_generics ::object_rainbow::MaybeHasNiche for #name #ty_generics #where_clause {
1347                type MnArray = #mn_array;
1348            }
1349        };
1350    };
1351    TokenStream::from(output)
1352}
1353
1354fn bounds_maybe_has_niche(mut generics: Generics, data: &Data) -> syn::Result<Generics> {
1355    match data {
1356        Data::Struct(data) => {
1357            for f in data.fields.iter() {
1358                let ty = &f.ty;
1359                generics
1360                    .make_where_clause()
1361                    .predicates
1362                    .push(parse_quote_spanned! { ty.span() =>
1363                        #ty: ::object_rainbow::MaybeHasNiche<
1364                            MnArray: ::object_rainbow::MnArray<
1365                                MaybeNiche: ::object_rainbow::MaybeNiche
1366                            >
1367                        >
1368                    });
1369            }
1370        }
1371        Data::Enum(data) => {
1372            generics.params.push(parse_quote!(
1373                __N: ::object_rainbow::typenum::Unsigned
1374            ));
1375            for (i, v) in data.variants.iter().enumerate() {
1376                let mn_array = fields_mn_array(&v.fields, Some(i));
1377                generics
1378                    .make_where_clause()
1379                    .predicates
1380                    .push(parse_quote_spanned! { v.span() =>
1381                        #mn_array: ::object_rainbow::MnArray<
1382                            MaybeNiche: ::object_rainbow::NicheOr<N = __N>
1383                        >
1384                    });
1385                for f in v.fields.iter() {
1386                    let ty = &f.ty;
1387                    generics.make_where_clause().predicates.push(
1388                        parse_quote_spanned! { ty.span() =>
1389                            #ty: ::object_rainbow::MaybeHasNiche<
1390                                MnArray: ::object_rainbow::MnArray<
1391                                    MaybeNiche: ::object_rainbow::MaybeNiche
1392                                >
1393                            >
1394                        },
1395                    );
1396                }
1397            }
1398        }
1399        Data::Union(data) => {
1400            return Err(Error::new_spanned(
1401                data.union_token,
1402                "`union`s are not supported",
1403            ));
1404        }
1405    }
1406    Ok(generics)
1407}
1408
1409fn fields_mn_array(fields: &syn::Fields, variant: Option<usize>) -> proc_macro2::TokenStream {
1410    let mn_array = fields.iter().map(|f| {
1411        let ty = &f.ty;
1412        quote! {
1413            <
1414                <
1415                    #ty
1416                    as
1417                    ::object_rainbow::MaybeHasNiche
1418                >::MnArray
1419                as
1420                ::object_rainbow::MnArray
1421            >::MaybeNiche
1422        }
1423    });
1424    if let Some(variant) = variant {
1425        let kind_niche = quote! {
1426            ::object_rainbow::AutoEnumNiche<Self, #variant>
1427        };
1428        quote! { tarr![#kind_niche, ::object_rainbow::NoNiche<::object_rainbow::HackNiche<#variant>>, #(#mn_array),*] }
1429    } else {
1430        quote! { tarr![#(#mn_array),*] }
1431    }
1432}
1433
1434fn gen_mn_array(data: &Data) -> proc_macro2::TokenStream {
1435    match data {
1436        Data::Struct(data) => fields_mn_array(&data.fields, None),
1437        Data::Enum(data) => {
1438            let mn_array = data.variants.iter().enumerate().map(|(i, v)| {
1439                let mn_array = fields_mn_array(&v.fields, Some(i));
1440                quote! { <#mn_array as ::object_rainbow::MnArray>::MaybeNiche }
1441            });
1442            quote! {
1443                ::object_rainbow::NicheFoldOrArray<tarr![#(#mn_array),*]>
1444            }
1445        }
1446        Data::Union(data) => {
1447            Error::new_spanned(data.union_token, "`union`s are not supported").into_compile_error()
1448        }
1449    }
1450}