1use either::Either;
8use proc_macro2::{Ident, Span, TokenStream};
9use quote::{quote, quote_spanned};
10use syn::spanned::Spanned as _;
11use syn::{
12 Block, Data, DeriveInput, Error, Expr, Field, Fields, Generics, ItemImpl, Result, Type, parse_quote,
13 parse_quote_spanned, parse2,
14};
15
16use crate::attribute::ParseAttribute as _;
17use crate::attribute::krate::crate_from_attributes;
18use crate::attribute::value::{DataAttribute, DefaultValue, FieldFromAttribute, Tag, VariantAttribute};
19use crate::utils::{
20 add_lifetime_generic, add_trait_bounds, find_repr, format_type, path_ends_with, wrap_in_fields_group,
21};
22use crate::value::field_to_ident_key;
23
24pub(crate) fn impl_from_value(mut input: DeriveInput) -> Result<ItemImpl> {
25 let ion = &crate_from_attributes(&mut input.attrs);
26
27 add_trait_bounds(&mut input.generics, &parse_quote!(#ion::conversions::FromValue));
28 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
29 let mut impl_generics: Generics = parse2(quote_spanned!(impl_generics.span() => #impl_generics))?;
30 add_lifetime_generic(&mut impl_generics, parse_quote!('cx));
31
32 let attribute = DataAttribute::from_attributes("ion", &mut input.attrs, ())?;
33
34 let repr = find_repr(&input.attrs)?;
35 let name = &input.ident;
36
37 let (body, requires_object) = impl_body(ion, input.span(), &mut input.data, name, &attribute, repr)?;
38
39 let object = requires_object.then(|| {
40 quote_spanned!(input.span() =>
41 let __ion_object = #ion::Object::from_value(__ion_cx, __ion_value, true, ())?;
42 )
43 });
44
45 parse2(quote_spanned!(input.span() =>
46 #[automatically_derived]
47 impl #impl_generics #ion::conversions::FromValue<'cx> for #name #ty_generics #where_clause {
48 type Config = ();
49
50 fn from_value(__ion_cx: &'cx #ion::Context, __ion_value: &#ion::Value, strict: bool, _: ()) -> #ion::Result<Self> {
51 #object
52 #body
53 }
54 }
55 ))
56}
57
58fn impl_body(
59 ion: &TokenStream, span: Span, data: &mut Data, ident: &Ident, attribute: &DataAttribute, repr: Option<Ident>,
60) -> Result<(Box<Block>, bool)> {
61 match data {
62 Data::Struct(r#struct) => match &r#struct.fields {
63 Fields::Named(_) | Fields::Unnamed(_) => {
64 let requirement = tag_requirement(ion, attribute.tag.0.as_ref(), None)?;
65 let (idents, declarations, requires_object) =
66 map_fields(ion, &mut r#struct.fields, Either::Left(attribute))?;
67 let wrapped = wrap_in_fields_group(idents, &r#struct.fields);
68
69 let block = parse2(quote_spanned!(span => {
70 #requirement
71 #(#declarations)*
72 ::std::result::Result::Ok(Self #wrapped)
73 }))?;
74 Ok((block, requires_object))
75 }
76 Fields::Unit => Ok((parse_quote_spanned!(span => { ::std::result::Result::Ok(Self) }), false)),
77 },
78 Data::Enum(r#enum) => {
79 let mut requires_object = false;
80 let mut requires_discriminant = false;
81
82 let mut variants = Vec::with_capacity(r#enum.variants.len());
83
84 for variant in &mut r#enum.variants {
85 let variant_ident = &variant.ident;
86 let variant_string = variant_ident.to_string();
87
88 let attribute = VariantAttribute::from_attributes("ion", &mut variant.attrs, attribute)?;
89 if attribute.skip {
90 continue;
91 }
92
93 let handle_result = quote!(if let ::std::result::Result::Ok(success) = variant {
94 return ::std::result::Result::Ok(success);
95 });
96 let variant: Block = match &variant.fields {
97 Fields::Named(_) | Fields::Unnamed(_) => {
98 let requirement = tag_requirement(ion, attribute.tag.0.as_ref(), Some(variant_string))?;
99 let (idents, declarations, req_object) =
100 map_fields(ion, &mut variant.fields, Either::Right(&attribute))?;
101 let wrapped = wrap_in_fields_group(idents, &variant.fields);
102 requires_object = requires_object || req_object;
103
104 parse2(quote_spanned!(variant.span() => {
105 let variant: #ion::Result<Self> = (|| {
106 #requirement
107 #(#declarations)*
108 ::std::result::Result::Ok(Self::#variant_ident #wrapped)
109 })();
110 #handle_result
111 }))?
112 }
113 Fields::Unit => match &variant.discriminant {
114 Some((_, discriminant)) => {
115 if repr.is_none() {
116 continue;
117 }
118
119 requires_discriminant = true;
120 parse2(quote_spanned!(
121 variant.fields.span() => {
122 if discriminant == #discriminant {
123 return ::std::result::Result::Ok(Self::#variant_ident);
124 }
125 }
126 ))?
127 }
128 None => parse_quote!({return ::std::result::Result::Ok(Self::#variant_ident);}),
129 },
130 };
131
132 variants.push(variant);
133 }
134
135 let error = format!("Value does not match any of the variants of enum {ident}");
136
137 let mut if_unit = None;
138
139 if requires_discriminant && let Some(repr) = repr {
140 if_unit = Some(quote_spanned!(repr.span() =>
141 let discriminant: #repr = #ion::conversions::FromValue::from_value(__ion_cx, __ion_value, true, #ion::conversions::ConversionBehavior::EnforceRange)?;
142 ));
143 }
144
145 parse2(quote_spanned!(span => {
146 #if_unit
147 #(#variants)*
148
149 ::std::result::Result::Err(#ion::Error::new(#error, #ion::ErrorKind::Type))
150 }))
151 .map(|b| (b, requires_object))
152 }
153 Data::Union(_) => Err(Error::new(
154 span,
155 "#[derive(FromValue)] is not implemented for union types",
156 )),
157 }
158}
159
160fn tag_requirement(ion: &TokenStream, tag: Option<&Tag>, variant: Option<String>) -> Result<Option<TokenStream>> {
161 if variant.is_none() {
162 return if matches!(tag, Some(Tag::External | Tag::Internal(_))) {
163 Err(Error::new(Span::call_site(), "Cannot have Tag for Struct"))
164 } else {
165 Ok(None)
166 };
167 }
168 let variant = variant.unwrap();
169
170 match tag {
171 Some(Tag::External) => {
172 let error = format!("Expected Object at External Tag {variant}");
173 Ok(Some(quote!(
174 let __ion_object: #ion::Object = __ion_object.get_as(__ion_cx, #variant, true, ())?
175 .ok_or_else(|_| #ion::Error::new(#error, #ion::ErrorKind::Type))?;
176 )))
177 }
178 Some(Tag::Internal(key)) => {
179 let missing_error = format!("Expected Internal Tag key {}", key.value());
180 let error = format!("Expected Internal Tag {variant} at key {}", key.value());
181
182 Ok(Some(quote!(
183 let __ion_key: ::std::string::String = __ion_object.get_as(__ion_cx, #key, true, ())?
184 .ok_or_else(|| #ion::Error::new(#missing_error, #ion::ErrorKind::Type))?;
185 if __ion_key != #variant {
186 return Err(#ion::Error::new(#error, #ion::ErrorKind::Type));
187 }
188 )))
189 }
190 _ => Ok(None),
191 }
192}
193
194fn map_fields(
195 ion: &TokenStream, fields: &mut Fields, attribute: Either<&DataAttribute, &VariantAttribute>,
196) -> Result<(Vec<Ident>, Vec<TokenStream>, bool)> {
197 let mut requires_object = matches!(get_tag(attribute), Some(Tag::External | Tag::Internal(_)));
198 let mut idents = Vec::with_capacity(fields.len());
199 let mut declarations = Vec::with_capacity(fields.len());
200
201 for (index, field) in fields.iter_mut().enumerate() {
202 let (ident, mut key) = field_to_ident_key(field, index);
203 let ty = &field.ty;
204
205 let mut optional = false;
206 if let Type::Path(ty) = ty
207 && path_ends_with(&ty.path, "Option")
208 {
209 optional = true;
210 }
211
212 let attribute = FieldFromAttribute::from_attributes("ion", &mut field.attrs, attribute)?;
213 if let Some(name) = attribute.base.name {
214 key = name.value();
215 }
216 if attribute.base.skip {
217 continue;
218 }
219
220 let strict = attribute.strict;
221 let convert = attribute.convert;
222
223 let convert = convert.unwrap_or_else(|| parse_quote!(()));
224 let base = field_base(
225 ion,
226 field,
227 &ident,
228 ty,
229 &key,
230 attribute.base.inherit,
231 &mut requires_object,
232 strict,
233 &convert,
234 attribute.parser.as_deref(),
235 )?;
236
237 let stmt = if optional {
238 quote_spanned!(field.span() => #base.ok();)
239 } else {
240 match attribute.default.0 {
241 Some(DefaultValue::Expr(expr)) => {
242 if attribute.base.inherit {
243 return Err(Error::new(
244 field.span(),
245 "Cannot have Default Expression with Inherited Field. Use a Closure with One Argument Instead",
246 ));
247 }
248 quote_spanned!(field.span() => #base.unwrap_or_else(|_| #expr);)
249 }
250 Some(DefaultValue::Closure(closure)) => {
251 quote_spanned!(field.span() => #base.unwrap_or_else(#closure);)
252 }
253 Some(DefaultValue::Literal(lit)) => quote_spanned!(field.span() => #base.unwrap_or(#lit);),
254 Some(DefaultValue::Default) => quote_spanned!(field.span() => #base.unwrap_or_default();),
255 None => quote_spanned!(field.span() => #base?;),
256 }
257 };
258
259 idents.push(ident);
260 declarations.push(stmt);
261 }
262
263 Ok((idents, declarations, requires_object))
264}
265
266#[expect(clippy::too_many_arguments)]
267fn field_base(
268 ion: &TokenStream, field: &Field, ident: &Ident, ty: &Type, key: &str, inherit: bool, requires_object: &mut bool,
269 strict: bool, convert: &Expr, parser: Option<&Expr>,
270) -> Result<TokenStream> {
271 if inherit {
272 if *requires_object {
273 return Err(Error::new(
274 field.span(),
275 "Inherited Field cannot be parsed from a Tagged Enum",
276 ));
277 }
278 Ok(quote_spanned!(field.span() =>
279 let #ident: #ty = <#ty as #ion::conversions::FromValue>::from_value(__ion_cx, __ion_value, #strict || strict, #convert)
280 ))
281 } else if let Some(parser) = parser {
282 *requires_object = true;
283 let error = format!("Expected Value at Key {key}");
284 Ok(quote_spanned!(field.span() =>
285 let #ident: #ty = __ion_object.get(__ion_cx, #key)?.map(#parser).transpose()?
286 .ok_or_else(|| #ion::Error::new(#error, #ion::ErrorKind::Type))
287 ))
288 } else {
289 *requires_object = true;
290 let error = format!("Expected Value at key {key} of Type {}", format_type(ty));
291 Ok(quote_spanned!(field.span() =>
292 let #ident: #ty = __ion_object.get_as(__ion_cx, #key, #strict || strict, #convert)?
293 .ok_or_else(|| #ion::Error::new(#error, #ion::ErrorKind::Type))
294 ))
295 }
296}
297
298fn get_tag<'a>(attribute: Either<&'a DataAttribute, &'a VariantAttribute>) -> Option<&'a Tag> {
299 match attribute {
300 Either::Left(data) => data.tag.0.as_ref(),
301 Either::Right(variant) => variant.tag.0.as_ref(),
302 }
303}