activitystreams_derive/
lib.rs

1/*
2 * This file is part of ActivityStreams Derive.
3 *
4 * Copyright © 2020 Riley Trautman
5 *
6 * ActivityStreams Derive is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ActivityStreams Derive is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with ActivityStreams Derive.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20//! Derive macros for Activity Streams
21//!
22//! ## Examples
23//!
24//! First, add `serde` and `activitystreams-derive` to your Cargo.toml
25//! ```toml
26//! activitystreams-derive = "0.5.0"
27//! # or activitystreams = "0.5.0"
28//! serde = { version = "1.0", features = ["derive"] }
29//! ```
30//!
31//! ```rust
32//! use activitystreams_derive::{properties, UnitString};
33//! // or activitystreams::{properties, UnitString};
34//! use serde_json::Value;
35//!
36//! /// Using the UnitString derive macro
37//! ///
38//! /// This macro implements Serialize and Deserialize for the given type, making this type
39//! /// represent the string "SomeKind" in JSON.
40//! #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, UnitString)]
41//! #[unit_string(SomeKind)]
42//! pub struct MyKind;
43//!
44//! /// Using the properties macro
45//! ///
46//! /// This macro generates getters and setters for the associated fields.
47//! properties! {
48//!     My {
49//!         context {
50//!             types [
51//!                 String,
52//!             ],
53//!             rename("@context"),
54//!         },
55//!         kind {
56//!             types [
57//!                 MyKind,
58//!             ],
59//!             functional,
60//!             required,
61//!             rename("type"),
62//!         },
63//!         required_key {
64//!             types [
65//!                 Value,
66//!             ],
67//!             functional,
68//!             required,
69//!             alias [
70//!                 "someKey",
71//!                 "existingKey",
72//!                 "woooKey",
73//!             ],
74//!         },
75//!     }
76//! }
77//!
78//! fn main () -> Result<(), Box<dyn std::error::Error>> {
79//!     let s = r#"{
80//!         "@context": "http://www.w3c.org/ns#activitystreams",
81//!         "type": "SomeKind",
82//!         "woooKey": {
83//!             "key": "value"
84//!         }
85//!     }"#;
86//!
87//!     let m: MyProperties = serde_json::from_str(s)?;
88//!     assert_eq!(&MyKind, m.get_kind());
89//!     Ok(())
90//! }
91//! ```
92
93extern crate proc_macro;
94
95use proc_macro::TokenStream;
96use proc_macro2::TokenTree;
97use quote::{quote, ToTokens};
98use syn::{
99    braced, bracketed, parenthesized,
100    parse::{Parse, ParseStream, Peek},
101    parse_macro_input,
102    punctuated::Punctuated,
103    token, Attribute, Data, DeriveInput, Fields, Ident, LitStr, Result, Token, Type,
104};
105
106/// Generate a type with default extensions
107///
108/// This derive
109/// ```ignore
110/// use activitystreams::{extensions::Ext, Extensible};
111///
112/// #[derive(Clone, Debug, Default, Extensible)]
113/// #[extension(MyExtension)]
114/// #[extension(MyOtherExtension)]
115/// pub struct MyType;
116/// ```
117///
118/// Produces this code
119/// ```ignore
120/// impl MyType {
121///     pub fn full() -> Ext<Ext<MyType, MyExtension>, OtherExtension> {
122///         Default::default()
123///     }
124/// }
125/// ```
126#[proc_macro_derive(Extensible, attributes(extension))]
127pub fn extensible(input: TokenStream) -> TokenStream {
128    let input: DeriveInput = syn::parse(input).unwrap();
129
130    let name = input.ident;
131
132    let kind: proc_macro2::TokenStream = input
133        .attrs
134        .iter()
135        .filter_map(move |attr| {
136            if attr
137                .path
138                .segments
139                .last()
140                .map(|segment| segment.ident == "extension")
141                .unwrap_or(false)
142            {
143                Some(from_value(attr.clone()))
144            } else {
145                None
146            }
147        })
148        .fold(quote! {#name}, |acc, ident| {
149            quote! { Ext<#acc, #ident> }
150        });
151
152    let tokens = quote! {
153        impl #name {
154            /// Generate a fully extended type
155            ///
156            /// This effect can be achieved with `Self::new().extend(SomeExtension::default())`
157            pub fn full() -> #kind {
158                Default::default()
159            }
160        }
161    };
162
163    tokens.into()
164}
165
166/// Derive implementations for activitystreams objects
167///
168/// ```ignore
169/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PropRefs)]
170/// #[prop_refs(Object)]
171/// pub struct MyStruct {
172///     /// Derive AsRef<MyProperties> and AsMut<MyProperties> delegating to `my_field`
173///     #[prop_refs]
174///     my_field: MyProperties,
175///
176///     /// Derive the above, plus Object (activitystreams)
177///     #[prop_refs]
178///     obj_field: ObjectProperties,
179/// }
180/// ```
181#[proc_macro_derive(PropRefs, attributes(prop_refs))]
182pub fn ref_derive(input: TokenStream) -> TokenStream {
183    let input: DeriveInput = syn::parse(input).unwrap();
184
185    let name = input.ident;
186
187    let data = match input.data {
188        Data::Struct(s) => s,
189        _ => panic!("Can only derive for structs"),
190    };
191
192    let fields = match data.fields {
193        Fields::Named(fields) => fields,
194        _ => panic!("Can only derive for named fields"),
195    };
196
197    let name2 = name.clone();
198    let base_impl = quote! {
199        impl Base for #name {}
200
201        impl #name {
202            /// Create from default
203            pub fn new() -> Self {
204                Default::default()
205            }
206        }
207
208        impl std::convert::TryFrom<#name> for BaseBox {
209            type Error = std::io::Error;
210
211            fn try_from(s: #name) -> Result<Self, Self::Error> {
212                BaseBox::from_concrete(s)
213            }
214        }
215    };
216    let trait_impls: proc_macro2::TokenStream = input
217        .attrs
218        .iter()
219        .filter_map(move |attr| {
220            if attr
221                .path
222                .segments
223                .last()
224                .map(|segment| segment.ident == "prop_refs")
225                .unwrap_or(false)
226            {
227                let object = from_value(attr.clone());
228                let name = name2.clone();
229                let box_name = Ident::new(&format!("{}Box", object), name.span());
230
231                Some(quote! {
232                    impl #object for #name {}
233
234                    impl std::convert::TryFrom<#name> for #box_name {
235                        type Error = std::io::Error;
236
237                        fn try_from(s: #name) -> Result<Self, Self::Error> {
238                            #box_name::from_concrete(s)
239                        }
240                    }
241                })
242            } else {
243                None
244            }
245        })
246        .collect();
247
248    let tokens: proc_macro2::TokenStream = fields
249        .named
250        .iter()
251        .filter_map(|field| {
252            let our_attr = field.attrs.iter().find(|attribute| {
253                attribute
254                    .path
255                    .segments
256                    .last()
257                    .map(|segment| segment.ident == "prop_refs")
258                    .unwrap_or(false)
259            });
260
261            our_attr.map(move |_| (field.ident.clone().unwrap(), field.ty.clone()))
262        })
263        .map(move |(ident, ty)| {
264            let name = name.clone();
265            quote! {
266                impl AsRef<#ty> for #name {
267                    fn as_ref(&self) -> &#ty {
268                        &self.#ident
269                    }
270                }
271
272                impl AsMut<#ty> for #name {
273                    fn as_mut(&mut self) -> &mut #ty {
274                        &mut self.#ident
275                    }
276                }
277            }
278        })
279        .collect();
280
281    let full = quote! {
282        #base_impl
283        #trait_impls
284        #tokens
285    };
286
287    full.into()
288}
289
290/// Derive a wrapper type based on serde_json::Value to contain any possible trait type
291///
292/// The following code
293/// ```ignore
294/// #[wrapper_type]
295/// pub trait Object {}
296/// ```
297/// produces the following type
298/// ```ignore
299/// pub struct ObjectBox(pub serde_json::Value);
300///
301/// impl ObjectBox {
302///     pub fn from_concrete<T>(t: T) -> Result<Self, std::io::Error>
303///     where
304///         T: Object + serde::ser::Serialize;
305///
306///     pub fn into_concrete<T>(self) -> Result<T, std::io::Error>
307///     where
308///         T: Object + serde::de::DeserializeOwned;
309///
310///     pub fn is_type(&self, kind: impl std::fmt::Display) -> bool;
311///
312///     pub fn type(&self) -> Option<&str>;
313/// }
314#[proc_macro_attribute]
315pub fn wrapper_type(_: TokenStream, input: TokenStream) -> TokenStream {
316    let input: syn::ItemTrait = syn::parse(input).unwrap();
317    let trait_name = input.ident.clone();
318    let type_name = Ident::new(&format!("{}Box", trait_name), trait_name.span());
319
320    let doc_line = to_doc(&format!("A wrapper type around a generic `{}`", trait_name));
321    let tokens = quote! {
322        #input
323
324        #doc_line
325        #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
326        #[serde(transparent)]
327        pub struct #type_name(serde_json::Map<String, serde_json::Value>);
328
329        impl #type_name {
330            /// Coerce a concrete type into this wrapper type
331            ///
332            /// This is done automatically via TryFrom in proprties setter methods
333            pub fn from_concrete<T>(t: T) -> Result<Self, std::io::Error>
334            where
335                T: #trait_name + serde::ser::Serialize,
336            {
337                match serde_json::to_value(t)? {
338                    serde_json::Value::Object(map) => Ok(#type_name(map)),
339                    _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Not an object")),
340                }
341            }
342
343            /// Attempt to deserialize the wrapper type to a concrete type
344            ///
345            /// Before this method is called, the type should be verified via the `kind` or
346            /// `is_kind` methods
347            pub fn into_concrete<T>(self) -> Result<T, std::io::Error>
348            where
349                T: #trait_name + serde::de::DeserializeOwned,
350            {
351                Ok(serde_json::from_value(serde_json::Value::Object(self.0))?)
352            }
353
354            /// Return whether the given wrapper type is expected.
355            ///
356            /// For example
357            /// ```ignore
358            /// use activitystreams::object::{
359            ///     kind::ImageType,
360            ///     apub::Image,
361            /// };
362            /// if my_wrapper_type.is_kind(ImageType) {
363            ///     let image = my_wrapper_type.into_concrete::<Image>()?;
364            ///     ...
365            /// }
366            /// ```
367            pub fn is_kind(&self, kind: impl std::fmt::Display) -> bool {
368                self.0["type"] == kind.to_string()
369            }
370
371            /// Return the kind of wrapper type, if present
372            ///
373            /// Example
374            /// ```ignore
375            /// match my_wrapper_type.kind() {
376            ///     Some("Image") => {
377            ///         let image = my_wrapper_type.into_concrete::<Image>()?;
378            ///         ...
379            ///     }
380            ///     _ => ...,
381            /// }
382            /// ```
383            pub fn kind(&self) -> Option<&str> {
384                match self.0["type"] {
385                    serde_json::Value::String(ref s) => Some(s),
386                    _ => None,
387                }
388            }
389        }
390    };
391
392    tokens.into()
393}
394
395/// Derive implementations Serialize and Deserialize for a constant string Struct type
396///
397/// ```ignore
398/// /// Derive Serialize and Deserialize such that MyStruct becomes the "MyType" string
399/// #[derive(Clone, Debug, UnitString)]
400/// #[unit_string(MyType)]
401/// pub struct MyStruct;
402///
403/// // usage
404/// let _: HashMap<String, MyStruct> = serde_json::from_str(r#"{"type":"MyType"}"#)?;
405/// ```
406#[proc_macro_derive(UnitString, attributes(unit_string))]
407pub fn unit_string(input: TokenStream) -> TokenStream {
408    let input: DeriveInput = syn::parse(input).unwrap();
409
410    let name = input.ident;
411
412    let attr = input
413        .attrs
414        .iter()
415        .find(|attribute| {
416            attribute
417                .path
418                .segments
419                .last()
420                .map(|segment| segment.ident == "unit_string")
421                .unwrap_or(false)
422        })
423        .unwrap()
424        .clone();
425
426    let visitor_name = from_value(attr);
427    let value = format!("{}", visitor_name);
428
429    let serialize = quote! {
430        impl ::serde::ser::Serialize for #name {
431            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
432            where
433                S: ::serde::ser::Serializer,
434            {
435                serializer.serialize_str(#value)
436            }
437        }
438    };
439
440    let expecting = quote! {
441        fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
442            write!(formatter, "The string '{}'", #value)
443        }
444    };
445
446    let visit = quote! {
447        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
448        where
449            E: ::serde::de::Error,
450        {
451            if v == #value {
452                Ok(#name)
453            } else {
454                Err(::serde::de::Error::custom("Invalid type"))
455            }
456        }
457    };
458
459    let visitor = quote! {
460        struct #visitor_name;
461
462        impl<'de> ::serde::de::Visitor<'de> for #visitor_name {
463            type Value = #name;
464
465            #expecting
466
467            #visit
468        }
469    };
470
471    let deserialize = quote! {
472        impl<'de> ::serde::de::Deserialize<'de> for #name {
473            fn deserialize<D>(deserializer: D) -> Result<#name, D::Error>
474            where
475                D: ::serde::de::Deserializer<'de>,
476            {
477                deserializer.deserialize_str(#visitor_name)
478            }
479        }
480    };
481
482    let display = quote! {
483        impl std::fmt::Display for #name {
484            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
485                write!(f, "{}", #value)
486            }
487        }
488    };
489
490    let c = quote! {
491        #serialize
492        #visitor
493        #deserialize
494        #display
495    };
496
497    c.into()
498}
499
500fn from_value(attr: Attribute) -> Ident {
501    let group = attr
502        .tokens
503        .into_iter()
504        .filter_map(|token_tree| match token_tree {
505            TokenTree::Group(group) => Some(group),
506            _ => None,
507        })
508        .next()
509        .unwrap();
510
511    group
512        .stream()
513        .into_iter()
514        .filter_map(|token_tree| match token_tree {
515            TokenTree::Ident(ident) => Some(ident),
516            _ => None,
517        })
518        .next()
519        .unwrap()
520}
521
522fn to_doc(s: &str) -> proc_macro2::TokenStream {
523    format!("/// {}", s).parse().unwrap()
524}
525
526fn many_docs(v: &[String]) -> proc_macro2::TokenStream {
527    v.iter()
528        .map(|d| {
529            let d = to_doc(d);
530            quote! {
531                #d
532            }
533        })
534        .collect()
535}
536
537/// Generate structs and enums for activitystreams objects
538///
539/// ```rust
540/// use activitystreams_derive::properties;
541///
542/// #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
543/// pub struct MyStruct;
544///
545/// properties! {
546///     Hello {
547///         docs [ "Defining the HelloProperties struct" ],
548///
549///         field {
550///             types [ String ],
551///         },
552///
553///         other_field {
554///             docs [
555///                 "field documentation",
556///                 "is cool",
557///             ],
558///             types [
559///                 String,
560///                 MyStruct,
561///             ],
562///             functional,
563///             required,
564///             rename("@other_field"),
565///             alias [
566///                 "@second_field",
567///                 "another_field",
568///             ],
569///         },
570///     }
571/// }
572///
573/// let _ = HelloProperties::default();
574/// ```
575#[proc_macro]
576pub fn properties(tokens: TokenStream) -> TokenStream {
577    let Properties { name, docs, fields } = parse_macro_input!(tokens as Properties);
578
579    let docs: proc_macro2::TokenStream = many_docs(&docs);
580
581    let name = Ident::new(&format!("{}Properties", name), name.span());
582
583    let (fields, deps): (Vec<_>, Vec<_>) = fields.iter().filter_map(|field| {
584        if field.description.types.is_empty() {
585            return None;
586        }
587
588        let fname = field.name.clone();
589        let fdocs: proc_macro2::TokenStream = many_docs(&field.description.docs);
590
591        let (ty, deps) = if field.description.types.len() == 1 {
592            let ty = Ident::new(&field.description.types.first().unwrap().to_token_stream().to_string(), fname.span());
593            if field.description.functional {
594                (ty, None)
595            } else {
596                let enum_ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span());
597                let doc_lines = many_docs(&[
598                    format!("Variations for the `{}` field from `{}", fname, name),
599                    String::new(),
600                    format!("`{}` isn't functional, meaning it can be represented as either a single `{}` or a vector of `{}`.", fname, ty, ty),
601                ]);
602                let deps = quote! {
603                    #doc_lines
604                    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
605                    #[serde(rename_all = "camelCase")]
606                    #[serde(untagged)]
607                    pub enum #enum_ty {
608                        Array(Vec<#ty>),
609                        Term(#ty),
610                    }
611
612                    impl Default for #enum_ty {
613                        fn default() -> Self {
614                            #enum_ty::Array(Vec::new())
615                        }
616                    }
617
618                    impl From<#ty> for #enum_ty {
619                        fn from(t: #ty) -> Self {
620                            #enum_ty::Term(t)
621                        }
622                    }
623
624                    impl From<Vec<#ty>> for #enum_ty {
625                        fn from(v: Vec<#ty>) -> Self {
626                            #enum_ty::Array(v)
627                        }
628                    }
629                };
630
631                (enum_ty, Some(deps))
632            }
633        } else {
634            let ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span());
635
636            let v_tokens: proc_macro2::TokenStream = field
637                .description
638                .types
639                .iter()
640                .map(|v_ty| {
641                    quote! {
642                        #v_ty(#v_ty),
643                    }
644                })
645                .collect();
646
647            let first_type = field.description.types.iter().next().unwrap().clone();
648
649            let deps = if !field.description.functional {
650                let term_ty = Ident::new(&camelize(&format!("{}_{}_term_enum", name, fname)), fname.span());
651
652                let from_tokens: proc_macro2::TokenStream = field
653                    .description
654                    .types
655                    .iter()
656                    .map(|v_ty| {
657                        quote! {
658                            impl From<#v_ty> for #term_ty {
659                                fn from(item: #v_ty) -> #term_ty {
660                                    #term_ty::#v_ty(item)
661                                }
662                            }
663                        }
664                    })
665                    .collect();
666
667                let term_doc_lines = many_docs(&[
668                    format!("Terminating variations for the `{}` field from `{}`", fname, name),
669                    String::new(),
670                    format!("Since {} can be one of multiple types, this enum represents all possibilities of {}", fname, fname),
671                ]);
672                let doc_lines = many_docs(&[
673                    format!("Non-Terminating variations for the `{}` field from `{}`", fname, name),
674                    String::new(),
675                    format!("`{}` isn't functional, meaning it can be represented as either a single `{}` or a vector of `{}`", fname, term_ty, term_ty),
676                ]);
677                quote! {
678                    #term_doc_lines
679                    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
680                    #[serde(rename_all = "camelCase")]
681                    #[serde(untagged)]
682                    pub enum #term_ty {
683                        #v_tokens
684                    }
685
686                    #doc_lines
687                    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
688                    #[serde(rename_all = "camelCase")]
689                    #[serde(untagged)]
690                    pub enum #ty {
691                        Array(Vec<#term_ty>),
692                        Term(#term_ty),
693                    }
694
695                    impl Default for #ty {
696                        fn default() -> Self {
697                            #ty::Array(Vec::new())
698                        }
699                    }
700
701                    impl From<#term_ty> for #ty {
702                        fn from(term: #term_ty) -> Self {
703                            #ty::Term(term)
704                        }
705                    }
706
707                    impl From<Vec<#term_ty>> for #ty {
708                        fn from(v: Vec<#term_ty>) -> Self {
709                            #ty::Array(v)
710                        }
711                    }
712
713                    #from_tokens
714                }
715            } else {
716                let from_tokens: proc_macro2::TokenStream = field
717                    .description
718                    .types
719                    .iter()
720                    .map(|v_ty| {
721                        quote! {
722                            impl From<#v_ty> for #ty {
723                                fn from(item: #v_ty) -> #ty {
724                                    #ty::#v_ty(item)
725                                }
726                            }
727                        }
728                    })
729                    .collect();
730
731                let doc_lines = many_docs(&[
732                    format!("Variations for the `{}` field from `{}`", fname, name),
733                    String::new(),
734                    format!("`{}` isn't functional, meaning it can only be represented as a single `{}`", fname, ty),
735                    String::new(),
736                    format!("This enum's variants represent all valid types to construct a `{}`", fname),
737                ]);
738                quote! {
739                    #doc_lines
740                    #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
741                    #[serde(rename_all = "camelCase")]
742                    #[serde(untagged)]
743                    pub enum #ty {
744                        #v_tokens
745                    }
746
747                    impl Default for #ty {
748                        fn default() -> Self {
749                            #ty::#first_type(Default::default())
750                        }
751                    }
752
753                    #from_tokens
754                }
755            };
756
757            (ty, Some(deps))
758        };
759
760        let alias_tokens: proc_macro2::TokenStream = field.description.aliases.iter().map(|alias| quote!{
761            #[serde(alias = #alias)]
762        }).collect();
763        let rename_tokens: proc_macro2::TokenStream = field.description.rename.iter().map(|rename| quote!{
764            #[serde(rename = #rename)]
765        }).collect();
766
767        let field_tokens = if field.description.required {
768            quote! {
769                pub #fname: #ty,
770            }
771        } else {
772            quote! {
773                #[serde(skip_serializing_if = "Option::is_none")]
774                pub #fname: Option<#ty>,
775            }
776        };
777
778        let field_tokens = quote!{
779            #fdocs
780            #rename_tokens
781            #alias_tokens
782            #field_tokens
783        };
784
785        let fns = if field.description.types.len() == 1 {
786            let v_ty = field.description.types.first().unwrap().clone();
787
788            let set_ident =
789                Ident::new(&format!("set_{}", fname), fname.span());
790            let get_ident =
791                Ident::new(&format!("get_{}", fname), fname.span());
792
793            let enum_ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span());
794
795            let set_many_ident =
796                Ident::new(&format!("set_many_{}", pluralize(fname.to_string())), fname.span());
797            let get_many_ident =
798                Ident::new(&format!("get_many_{}", pluralize(fname.to_string())), fname.span());
799            let add_ident = Ident::new(&format!("add_{}", fname.to_string()), fname.span());
800
801            if field.description.required {
802                if field.description.functional {
803                    let doc_line = to_doc(&format!("Set `{}` with a type that can be cnoverted into a `{}`", fname, v_ty.to_token_stream()));
804                    let set = quote! {
805                        #doc_line
806                        pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
807                        where
808                            T: std::convert::TryInto<#v_ty>,
809                        {
810                            use std::convert::TryInto;
811                            self.#fname = item.try_into()?;
812                            Ok(self)
813                        }
814                    };
815
816                    let doc_line = to_doc(&format!("Get the `{}` as `{}`", fname, v_ty.to_token_stream()));
817                    let get = quote! {
818                        #doc_line
819                        pub fn #get_ident(&self) -> &#v_ty {
820                            &self.#fname
821                        }
822                    };
823
824                    quote!{
825                        #get
826                        #set
827                    }
828                } else {
829                    let doc_line = to_doc(&format!("Set `{}` with a type that can be converted into a `{}`", fname, v_ty.to_token_stream()));
830                    let set = quote! {
831                        #doc_line
832                        pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
833                        where
834                            T: std::convert::TryInto<#v_ty>,
835                        {
836                            use std::convert::TryInto;
837                            self.#fname = #enum_ty::Term(item.try_into()?);
838                            Ok(self)
839                        }
840                    };
841
842                    let doc_line = to_doc(&format!("Get the `{}` as `{}`", fname, v_ty.to_token_stream()));
843                    let get = quote! {
844                        #doc_line
845                        ///
846                        /// This returns `None` when there is more than one item
847                        pub fn #get_ident(&self) -> Option<&#v_ty> {
848                            match self.#fname {
849                                #enum_ty::Term(ref term) => Some(term),
850                                _ => None,
851                            }
852                        }
853                    };
854
855                    let doc_line = to_doc(&format!("Set the `{}` with a vector of types that can be converted into `{}`s", fname, v_ty.to_token_stream()));
856                    let set_many = quote! {
857                        #doc_line
858                        pub fn #set_many_ident<T>(&mut self, item: Vec<T>) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
859                        where
860                            T: std::convert::TryInto<#v_ty>,
861                        {
862                            let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
863                            self.#fname = #enum_ty::Array(item);
864                            Ok(self)
865                        }
866                    };
867
868                    let doc_line = to_doc(&format!("Add a type that can be converted into a `{}` to the `{}` vec", v_ty.to_token_stream(), fname));
869                    let add = quote! {
870                        #doc_line
871                        pub fn #add_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
872                        where
873                            T: std::convert::TryInto<#v_ty>,
874                        {
875                            let item = item.try_into()?;
876
877                            let new_vec = match self.#fname {
878                                #enum_ty::Array(items) => {
879                                    let mut new_vec = Vec::new();
880                                    new_vec.extend(&items)
881                                    new_vec.push(item);
882                                    new_vec
883                                }
884                                #enum_ty::Term(old_item) => {
885                                    let mut new_vec = Vec::new();
886                                    new_vec.push(old_item.clone());
887                                    new_vec.push(item);
888                                    new_vec
889                                }
890                            };
891
892                            self.#fname = #enum_ty::Array(new_vec);
893                            Ok(self)
894                        }
895                    };
896
897                    let doc_line = to_doc(&format!("Get the `{}` as a slice of `{}`", fname, v_ty.to_token_stream()));
898                    let get_many = quote! {
899                        #doc_line
900                        ///
901                        /// This returns `None` if
902                        /// - There is only one element
903                        pub fn #get_many_ident(&self) -> Option<&[#v_ty]> {
904                            match self.#fname {
905                                #enum_ty::Array(ref array) => Some(array),
906                                _ => None,
907                            }
908                        }
909                    };
910
911                    quote! {
912                        #get
913                        #set
914                        #get_many
915                        #set_many
916                        #add
917                    }
918                }
919            } else if field.description.functional {
920                let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream()));
921                let set = quote! {
922                    #doc_line
923                    pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
924                    where
925                        T: std::convert::TryInto<#v_ty>,
926                    {
927                        use std::convert::TryInto;
928                        self.#fname = Some(item.try_into()?);
929                        Ok(self)
930                    }
931                };
932
933                let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream()));
934                let get = quote! {
935                    #doc_line
936                    ///
937                    /// This returns `None` if there is no value present
938                    pub fn #get_ident(&self) -> Option<&#v_ty> {
939                        self.#fname.as_ref()
940                    }
941                };
942
943                quote!{
944                    #get
945                    #set
946                }
947            } else {
948                let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream()));
949                let set = quote! {
950                    #doc_line
951                    pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
952                    where
953                        T: std::convert::TryInto<#v_ty>,
954                    {
955                        use std::convert::TryInto;
956                        self.#fname = Some(#enum_ty::Term(item.try_into()?));
957                        Ok(self)
958                    }
959                };
960
961                let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream()));
962                let get = quote! {
963                    #doc_line
964                    ///
965                    /// This returns `None` if
966                    /// - There is no value present
967                    /// - There is more than one value present
968                    pub fn #get_ident(&self) -> Option<&#v_ty> {
969                        match self.#fname {
970                            Some(#enum_ty::Term(ref term)) => Some(term),
971                            _ => None,
972                        }
973                    }
974                };
975
976                let doc_line = to_doc(&format!("Set the `{}` with a vector of types that can be converted into `{}`s", fname, v_ty.to_token_stream()));
977                let set_many = quote! {
978                    #doc_line
979                    pub fn #set_many_ident<T>(&mut self, item: Vec<T>) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
980                    where
981                        T: std::convert::TryInto<#v_ty>,
982                    {
983                        let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
984                        self.#fname = Some(#enum_ty::Array(item));
985                        Ok(self)
986                    }
987                };
988
989                let doc_line = to_doc(&format!("Add a type that can be converted into a `{}` to the `{}` vec", v_ty.to_token_stream(), fname));
990                let add = quote! {
991                    #doc_line
992                    pub fn #add_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
993                    where
994                        T: std::convert::TryInto<#v_ty>,
995                    {
996                        let item = item.try_into()?;
997
998                        let new_vec = match self.#fname.take() {
999                            Some(#enum_ty::Array(mut items)) => {
1000                                items.push(item);
1001                                items
1002                            }
1003                            Some(#enum_ty::Term(old_item)) => {
1004                                let mut new_vec = Vec::new();
1005                                new_vec.push(old_item.clone());
1006                                new_vec.push(item);
1007                                new_vec
1008                            }
1009                            None => {
1010                                let mut new_vec = Vec::new();
1011                                new_vec.push(item);
1012                                new_vec
1013                            }
1014                        };
1015
1016                        self.#fname = Some(#enum_ty::Array(new_vec));
1017                        Ok(self)
1018                    }
1019                };
1020
1021                let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, v_ty.to_token_stream()));
1022                let get_many = quote! {
1023                    #doc_line
1024                    ///
1025                    /// This returns `None` if
1026                    /// - There is no value present
1027                    /// - There is only one value present
1028                    pub fn #get_many_ident(&self) -> Option<&[#v_ty]> {
1029                        match self.#fname {
1030                            Some(#enum_ty::Array(ref a)) => Some(a),
1031                            _ => None,
1032                        }
1033                    }
1034                };
1035
1036                quote! {
1037                    #get
1038                    #set
1039                    #get_many
1040                    #set_many
1041                    #add
1042                }
1043            }
1044        } else if field.description.functional {
1045            let tokens: proc_macro2::TokenStream = field
1046                .description
1047                .types
1048                .iter()
1049                .map(|v_ty| {
1050                    let set_ident =
1051                        Ident::new(&format!("set_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span());
1052                    let get_ident =
1053                        Ident::new(&format!("get_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span());
1054
1055                    if field.description.required {
1056                        let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream()));
1057                        let set = quote! {
1058                            #doc_line
1059                            pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1060                            where
1061                                T: std::convert::TryInto<#v_ty>,
1062                            {
1063                                use std::convert::TryInto;
1064                                let item: #v_ty = item.try_into()?;
1065                                self.#fname = item.into();
1066                                Ok(self)
1067                            }
1068                        };
1069
1070                        let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, v_ty.to_token_stream()));
1071                        let get = quote! {
1072                            #doc_line
1073                            ///
1074                            /// This returns `None` if
1075                            /// - The requested type is not the stored type
1076                            pub fn #get_ident(&self) -> Option<&#v_ty> {
1077                                match self.#fname {
1078                                    #ty::#v_ty(ref term) => Some(term),
1079                                    _ => None,
1080                                }
1081                            }
1082                        };
1083
1084                        quote! {
1085                            #get
1086                            #set
1087                        }
1088                    } else {
1089                        let doc_line = to_doc(&format!("Set `{}` with a value that can be converted into `{}`", fname, v_ty.to_token_stream()));
1090                        let set = quote! {
1091                            #doc_line
1092                            pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1093                            where
1094                                T: std::convert::TryInto<#v_ty>,
1095                            {
1096                                use std::convert::TryInto;
1097                                let item: #v_ty = item.try_into()?;
1098                                self.#fname = Some(item.into());
1099                                Ok(self)
1100                            }
1101                        };
1102
1103                        let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream()));
1104                        let get = quote! {
1105                            #doc_line
1106                            ///
1107                            /// This returns `None` if
1108                            /// - There is no value present
1109                            /// - The requested type is not the stored type
1110                            pub fn #get_ident(&self) -> Option<&#v_ty> {
1111                                match self.#fname {
1112                                    Some(#ty::#v_ty(ref term)) => Some(term),
1113                                    _ => None,
1114                                }
1115                            }
1116                        };
1117
1118                        quote! {
1119                            #get
1120                            #set
1121                        }
1122                    }
1123                })
1124                .collect();
1125
1126            quote! {
1127                #tokens
1128            }
1129        } else {
1130            let term_ty = Ident::new(&camelize(&format!("{}_{}_term_enum", name, fname)), fname.span());
1131            let tokens: proc_macro2::TokenStream = field
1132                .description
1133                .types
1134                .iter()
1135                .map(|v_ty| {
1136                    let set_ident =
1137                        Ident::new(&format!("set_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span());
1138                    let get_ident =
1139                        Ident::new(&format!("get_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span());
1140
1141                    let set_many_ident =
1142                        Ident::new(&format!("set_many_{}_{}", fname, pluralize(snakize(&v_ty.to_token_stream().to_string()))), fname.span());
1143                    let get_many_ident =
1144                        Ident::new(&format!("get_many_{}_{}", fname, pluralize(snakize(&v_ty.to_token_stream().to_string()))), fname.span());
1145
1146                    if field.description.required {
1147                        let doc_line = to_doc(&format!("Set `{}` with a value that can be converted into `{}`", fname, v_ty.to_token_stream()));
1148                        let set = quote! {
1149                            #doc_line
1150                            pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1151                            where
1152                                T: std::convert::TryInto<#v_ty>,
1153                            {
1154                                use std::convert::TryInto;
1155                                let item: #v_ty = item.try_into()?;
1156                                let item: #term_ty = item.into();
1157                                self.#fname = item.into();
1158                                Ok(self)
1159                            }
1160                        };
1161
1162                        let doc_line = to_doc(&format!("Get the `{}` as a `{}`", fname, v_ty.to_token_stream()));
1163                        let get = quote! {
1164                            #doc_line
1165                            ///
1166                            /// This returns `None` if
1167                            /// - There is more than one value present
1168                            /// - The requested type is not the stored type
1169                            pub fn #get_ident(&self) -> Option<&#v_ty> {
1170                                match self.#fname {
1171                                    #ty::Term(#term_ty::#v_ty(ref term)) => Some(term),
1172                                    _ => None,
1173                                }
1174                            }
1175                        };
1176
1177                        let doc_line = to_doc(&format!("Set `{}` from a vec of items that can be converted into `{}`s", fname, v_ty.to_token_stream()));
1178                        let set_many = quote! {
1179                            #doc_line
1180                            pub fn #set_many_ident<T>(&mut self, item: Vec<T>) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1181                            where
1182                                T: std::convert::TryInto<#v_ty>,
1183                            {
1184                                let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
1185                                let item: Vec<#term_ty> = item.into_iter().map(Into::into).collect();
1186                                self.#fname = item.into();
1187                                Ok(self)
1188                            }
1189                        };
1190
1191                        let doc_line = to_doc(&format!("Get `{}` as a vec of `&{}`s", fname, v_ty.to_token_stream()));
1192                        let get_many = quote! {
1193                            #doc_line
1194                            ///
1195                            /// This returns `None` if
1196                            /// - There is only one value present
1197                            ///
1198                            /// The returned vec will be empty if no values match the requested
1199                            /// type, but values are present.
1200                            pub fn #get_many_ident(&self) -> Option<impl Iterator<Item = &#v_ty>> {
1201                                match self.#fname {
1202                                    #ty::Array(ref array) => Some(array.iter().filter_map(|i| match i {
1203                                        #term_ty::#v_ty(item) => Some(item),
1204                                        _ => None,
1205                                    })),
1206                                    _ => None,
1207                                }
1208                            }
1209                        };
1210
1211                        quote! {
1212                            #get
1213                            #set
1214                            #get_many
1215                            #set_many
1216                        }
1217                    } else {
1218                        let doc_line = to_doc(&format!("Set `{}` from a value that can be converted into `{}`", fname, v_ty.to_token_stream()));
1219                        let set = quote! {
1220                            #doc_line
1221                            pub fn #set_ident<T>(&mut self, item: T) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1222                            where
1223                                T: std::convert::TryInto<#v_ty>,
1224                            {
1225                                use std::convert::TryInto;
1226                                let item: #v_ty = item.try_into()?;
1227                                let item: #term_ty = item.into();
1228                                self.#fname = Some(item.into());
1229                                Ok(self)
1230                            }
1231                        };
1232
1233                        let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream()));
1234                        let get = quote! {
1235                            #doc_line
1236                            ///
1237                            /// This returns `None` if
1238                            /// - There is no value present
1239                            /// - There is more than one value present
1240                            /// - The requested type is not stored type
1241                            pub fn #get_ident(&self) -> Option<&#v_ty> {
1242                                match self.#fname {
1243                                    Some(#ty::Term(#term_ty::#v_ty(ref term))) => Some(term),
1244                                    _ => None,
1245                                }
1246                            }
1247                        };
1248
1249                        let doc_line = to_doc(&format!("Set `{}` from a vec of items that can be converted into `{}`s", fname, v_ty.to_token_stream()));
1250                        let set_many = quote! {
1251                            #doc_line
1252                            pub fn #set_many_ident<T>(&mut self, item: Vec<T>) -> Result<&mut Self, <T as std::convert::TryInto<#v_ty>>::Error>
1253                            where
1254                                T: std::convert::TryInto<#v_ty>,
1255                            {
1256                                let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
1257                                let item: Vec<#term_ty> = item.into_iter().map(Into::into).collect();
1258                                self.#fname = Some(item.into());
1259                                Ok(self)
1260                            }
1261                        };
1262
1263                        let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, term_ty.to_token_stream()));
1264                        let get_many = quote! {
1265                            #doc_line
1266                            ///
1267                            /// This returns `None` if
1268                            /// - There is no value present
1269                            /// - There is only one value present
1270                            pub fn #get_many_ident(&self) -> Option<impl Iterator<Item = &#v_ty>> {
1271                                match self.#fname {
1272                                    Some(#ty::Array(ref array)) => Some(array.iter().filter_map(|i| match i {
1273                                        #term_ty::#v_ty(item) => Some(item),
1274                                        _ => None,
1275                                    })),
1276                                    _ => None,
1277                                }
1278                            }
1279                        };
1280
1281                        quote! {
1282                            #get
1283                            #set
1284                            #get_many
1285                            #set_many
1286                        }
1287                    }
1288                })
1289                .collect();
1290
1291            let delete = if !field.description.required {
1292                let delete_ident =
1293                    Ident::new(&format!("delete_{}", fname), fname.span());
1294
1295                let doc_line = to_doc(&format!("Set the `{}` field to `None`", fname));
1296                quote! {
1297                    #doc_line
1298                    pub fn #delete_ident(&mut self) -> &mut Self {
1299                        self.#fname = None;
1300                        self
1301                    }
1302                }
1303            } else {
1304                quote! {}
1305            };
1306
1307            quote! {
1308                #tokens
1309
1310                #delete
1311            }
1312        };
1313
1314        Some(((field_tokens, fns), deps))
1315    }).unzip();
1316
1317    let (field_tokens, fn_tokens): (proc_macro2::TokenStream, proc_macro2::TokenStream) =
1318        fields.into_iter().unzip();
1319    let deps_tokens: proc_macro2::TokenStream = deps.into_iter().filter_map(|d| d).collect();
1320
1321    let q = quote! {
1322        #docs
1323        #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
1324        #[serde(rename_all = "camelCase")]
1325        pub struct #name {
1326            #field_tokens
1327        }
1328
1329        impl #name {
1330            #fn_tokens
1331        }
1332
1333        #deps_tokens
1334    };
1335    q.into()
1336}
1337
1338mod kw {
1339    syn::custom_keyword!(types);
1340    syn::custom_keyword!(functional);
1341    syn::custom_keyword!(required);
1342    syn::custom_keyword!(rename);
1343    syn::custom_keyword!(alias);
1344    syn::custom_keyword!(docs);
1345}
1346
1347struct Properties {
1348    name: Ident,
1349    docs: Vec<String>,
1350    fields: Punctuated<Field, Token![,]>,
1351}
1352
1353struct Field {
1354    name: Ident,
1355    description: Description,
1356}
1357
1358struct Description {
1359    docs: Vec<String>,
1360    types: Punctuated<Type, Token![,]>,
1361    functional: bool,
1362    required: bool,
1363    rename: Option<String>,
1364    aliases: Vec<String>,
1365}
1366
1367impl Parse for Properties {
1368    fn parse(input: ParseStream) -> Result<Self> {
1369        let name: Ident = input.parse()?;
1370
1371        let content;
1372        let _: token::Brace = braced!(content in input);
1373
1374        let docs = parse_string_array::<_, kw::docs>(&&content, kw::docs)?;
1375
1376        let fields = Punctuated::<Field, Token![,]>::parse_terminated(&content)?;
1377
1378        Ok(Properties { name, docs, fields })
1379    }
1380}
1381
1382impl Parse for Field {
1383    fn parse(input: ParseStream) -> Result<Self> {
1384        let name: Ident = input.parse()?;
1385
1386        let content;
1387        let _: token::Brace = braced!(content in input);
1388
1389        let description = content.parse()?;
1390
1391        Ok(Field { name, description })
1392    }
1393}
1394
1395impl Parse for Description {
1396    fn parse(input: ParseStream) -> Result<Self> {
1397        let docs = parse_string_array::<_, kw::docs>(&input, kw::docs)?;
1398
1399        let lookahead = input.lookahead1();
1400        if !lookahead.peek(kw::types) {
1401            return Err(lookahead.error());
1402        }
1403        input.parse::<kw::types>()?;
1404
1405        let content;
1406        let _: token::Bracket = bracketed!(content in input);
1407        let types = Punctuated::<Type, Token![,]>::parse_terminated(&content)?;
1408        optional_comma(&input)?;
1409
1410        let functional = parse_kw::<_, kw::functional>(&input, kw::functional)?;
1411        let required = parse_kw::<_, kw::required>(&input, kw::required)?;
1412        let rename = parse_string_group::<_, kw::rename>(&input, kw::rename)?;
1413        let aliases = parse_string_array::<_, kw::alias>(&input, kw::alias)?;
1414
1415        Ok(Description {
1416            docs,
1417            types,
1418            functional,
1419            required,
1420            rename,
1421            aliases,
1422        })
1423    }
1424}
1425
1426fn parse_kw<T: Peek + Copy, U: Parse>(input: ParseStream, t: T) -> Result<bool> {
1427    let lookahead = input.lookahead1();
1428    if lookahead.peek(t) {
1429        input.parse::<U>()?;
1430        optional_comma(&input)?;
1431
1432        return Ok(true);
1433    }
1434
1435    Ok(false)
1436}
1437
1438fn parse_string_array<T: Peek + Copy, U: Parse>(input: ParseStream, t: T) -> Result<Vec<String>> {
1439    let lookahead = input.lookahead1();
1440    if lookahead.peek(t) {
1441        input.parse::<U>()?;
1442        let content;
1443        bracketed!(content in input);
1444
1445        let docs = Punctuated::<LitStr, Token![,]>::parse_terminated(&content)?;
1446        optional_comma(&input)?;
1447        Ok(docs.into_iter().map(|d| d.value()).collect())
1448    } else {
1449        Ok(vec![])
1450    }
1451}
1452
1453fn parse_string_group<T: Peek + Copy, U: Parse>(
1454    input: ParseStream,
1455    t: T,
1456) -> Result<Option<String>> {
1457    let lookahead = input.lookahead1();
1458    if lookahead.peek(t) {
1459        input.parse::<U>()?;
1460        let content;
1461        parenthesized!(content in input);
1462        let s: LitStr = content.parse()?;
1463        optional_comma(&input)?;
1464
1465        return Ok(Some(s.value()));
1466    }
1467
1468    Ok(None)
1469}
1470
1471fn optional_comma(input: ParseStream) -> Result<()> {
1472    let lookahead = input.lookahead1();
1473    if lookahead.peek(Token![,]) {
1474        input.parse::<Token![,]>()?;
1475    }
1476    Ok(())
1477}
1478
1479fn camelize(s: &str) -> String {
1480    let (s, _) = s
1481        .chars()
1482        .fold((String::new(), true), |(mut acc, should_upper), c| {
1483            if c == '_' {
1484                (acc, true)
1485            } else {
1486                if should_upper {
1487                    acc += &c.to_uppercase().to_string();
1488                } else {
1489                    acc += &c.to_string();
1490                }
1491
1492                (acc, false)
1493            }
1494        });
1495
1496    s
1497}
1498
1499fn snakize(s: &str) -> String {
1500    s.chars().fold(String::new(), |mut acc, c| {
1501        if c.is_uppercase() && !acc.is_empty() {
1502            acc += "_";
1503            acc += &c.to_lowercase().to_string();
1504        } else if c.is_uppercase() {
1505            acc += &c.to_lowercase().to_string();
1506        } else {
1507            acc += &c.to_string();
1508        }
1509        acc
1510    })
1511}
1512
1513fn pluralize(s: String) -> String {
1514    if s.ends_with('x') {
1515        s + "es"
1516    } else if s.ends_with('s') {
1517        s
1518    } else {
1519        s + "s"
1520    }
1521}