1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use syn::{Data, Fields, Ident, Index};
5
6fn impl_from_value(ast: &syn::DeriveInput) -> TokenStream {
7 let name = &ast.ident;
8
9 if let Data::Struct(data) = &ast.data {
10 let returned = match &data.fields {
11 Fields::Named(fields) => {
12 let fields = fields
13 .named
14 .iter()
15 .map(|f| {
16 let name = f.ident.as_ref().expect("named structure field must have an identifier");
17 let ty = &f.ty;
18
19 quote! { #name: <#ty>::from_value(val.remove(stringify!(#name)).ok_or(pochoir::lang::Error::MissingField(stringify!(#name).to_string()))?)? }
20 }).collect::<Vec<_>>();
21
22 quote! { {#(#fields),*} }
23 }
24 Fields::Unnamed(fields) => {
25 let fields = fields
26 .unnamed
27 .iter()
28 .enumerate()
29 .map(|(i, f)| {
30 let name = format!("__field{i}");
31 let ty = &f.ty;
32
33 quote! { <#ty>::from_value(val.remove(#name).ok_or(pochoir::lang::Error::MissingField(#name.to_string()))?)? }
34 }).collect::<Vec<_>>();
35
36 quote! { (#(#fields),*) }
37 }
38 Fields::Unit => {
39 quote! {}
40 }
41 };
42
43 quote! {
44 impl pochoir::lang::FromValue for #name {
45 fn from_value(val: Value) -> ::std::result::Result<Self, Box<dyn ::std::error::Error>> {
46 if let Value::Object(mut val) = val {
47 Ok(Self #returned)
48 } else {
49 Err(Box::new(pochoir::lang::Error::MismatchedTypes {
50 expected: "Object".to_string(),
51 found: val.type_name().to_string(),
52 }))
53 }
54 }
55 }
56 }
57 .into()
58 } else if let Data::Enum(data) = &ast.data {
59 let fields = data.variants.iter().map(|v| {
60 let variant_name = &v.ident;
61
62 match &v.fields {
63 Fields::Named(fields) => {
64 let fields = fields
65 .named
66 .iter()
67 .map(|f| {
68 let name = f.ident.as_ref().expect("named structure field must have an identifier");
69 let ty = &f.ty;
70
71 quote! { #name: <#ty>::from_value(val.remove(stringify!(#name)).ok_or(pochoir::lang::Error::MissingField(stringify!(#name).to_string()))?)? }
72 }).collect::<Vec<_>>();
73
74 quote! { stringify!(#variant_name) => Self::#variant_name { #(#fields),* } }
75 }
76 Fields::Unnamed(fields) => {
77 let fields = fields
78 .unnamed
79 .iter()
80 .enumerate()
81 .map(|(i, f)| {
82 let name = format!("__field{i}");
83 let ty = &f.ty;
84
85 quote! { <#ty>::from_value(val.remove(#name).ok_or(pochoir::lang::Error::MissingField(#name.to_string()))?)? }
86 }).collect::<Vec<_>>();
87
88 quote! { stringify!(#variant_name) => Self::#variant_name ( #(#fields),* ) }
89 }
90 Fields::Unit => {
91 quote! { stringify!(#variant_name) => Self::#variant_name }
92 }
93 }
94 }).collect::<Vec<_>>();
95
96 quote! {
97 impl pochoir::lang::FromValue for #name {
98 fn from_value(val: Value) -> ::std::result::Result<Self, Box<dyn ::std::error::Error>> {
99 if let Value::Object(mut val) = val {
100 Ok(match val.get("__enum_variant")
101 .ok_or(pochoir::lang::Error::MissingField("__enum_variant".to_string()))?
102 .as_string().expect("__enum_variant should be a string")
103 {
104 #(#fields,)*
105 v => return Err(Box::new(pochoir::lang::Error::UnknownEnumVariant(v.to_string()))),
106 })
107 } else {
108 Err(Box::new(pochoir::lang::Error::MismatchedTypes {
109 expected: "Object".to_string(),
110 found: val.type_name().to_string(),
111 }))
112 }
113 }
114 }
115 }
116 .into()
117 } else {
118 panic!("FromValue works only on structures and enumerations")
119 }
120}
121
122fn impl_into_value(ast: &syn::DeriveInput) -> TokenStream {
123 let name = &ast.ident;
124
125 if let Data::Struct(data) = &ast.data {
126 let fields = match &data.fields {
127 Fields::Named(fields) => fields
128 .named
129 .iter()
130 .map(|f| {
131 let name = f
132 .ident
133 .as_ref()
134 .expect("named structure field must have an identifier");
135 quote! { stringify!(#name) => self.#name.into_value() }
136 })
137 .collect::<Vec<_>>(),
138 Fields::Unnamed(fields) => (0..fields.unnamed.len())
139 .map(|i| {
140 let name = Ident::new(&format!("__field{i}"), Span::call_site());
141 let i = Index::from(i);
142 quote! { stringify!(#name) => self.#i.into_value() }
143 })
144 .collect::<Vec<_>>(),
145 Fields::Unit => {
146 vec![]
147 }
148 };
149
150 quote! {
151 impl pochoir::lang::IntoValue for #name {
152 fn into_value(self) -> pochoir::lang::Value {
153 pochoir::lang::object! {
154 #(#fields),*
155 }.into_value()
156 }
157 }
158 }
159 .into()
160 } else if let Data::Enum(data) = &ast.data {
161 let fields = data
162 .variants
163 .iter()
164 .map(|v| {
165 let variant_name = &v.ident;
166
167 match &v.fields {
168 Fields::Named(fields) => {
169 let fields = fields.named.iter().map(|f| f.ident.as_ref().expect("named structure field must have an identifier")).collect::<Vec<_>>();
170
171 quote! { Self::#variant_name { #(#fields),* } => pochoir::lang::object! {"__enum_variant" => stringify!(#variant_name), #(stringify!(#fields) => #fields.into_value()),* }.into_value() }
172 },
173 Fields::Unnamed(fields) => {
174 let fields = (0..fields.unnamed.len()).map(|i| Ident::new(&format!("__field{i}"), Span::call_site())).collect::<Vec<_>>();
175
176 quote! { Self::#variant_name(#(#fields),*) => pochoir::lang::object! {"__enum_variant" => stringify!(#variant_name), #(stringify!(#fields) => #fields.into_value()),* }.into_value() }
177 },
178 Fields::Unit => {
179 quote! { Self::#variant_name => pochoir::lang::object! { "__enum_variant" => stringify!(#variant_name) }.into_value() }
180 }
181 }
182 });
183
184 quote! {
185 impl pochoir::lang::IntoValue for #name {
186 fn into_value(self) -> pochoir::lang::Value {
187 match self {
188 #(#fields),*
189 }
190 }
191 }
192 }
193 .into()
194 } else {
195 panic!("IntoValue works only on structures and enumerations")
196 }
197}
198
199#[proc_macro_derive(FromValue)]
205pub fn from_value_derive(input: TokenStream) -> TokenStream {
206 let ast = syn::parse(input).unwrap();
207 impl_from_value(&ast)
208}
209
210#[proc_macro_derive(IntoValue)]
212pub fn into_value_derive(input: TokenStream) -> TokenStream {
213 let ast = syn::parse(input).unwrap();
214 impl_into_value(&ast)
215}