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