1extern 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#[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 pub fn full() -> #kind {
158 Default::default()
159 }
160 }
161 };
162
163 tokens.into()
164}
165
166#[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 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#[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 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 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 pub fn is_kind(&self, kind: impl std::fmt::Display) -> bool {
368 self.0["type"] == kind.to_string()
369 }
370
371 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#[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#[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 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 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 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 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 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 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 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 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 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 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 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}