try_from_map/lib.rs
1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5mod field;
6mod generation;
7
8/// Derive `TryFrom<HashMap<String, String>>` for a struct.
9///
10/// This macro will generate an implementation of `TryFrom<HashMap<String, String>>` for the annotated struct.
11/// It will attempt to parse each field from the map, and return an error if any field is missing or cannot be parsed.
12/// Fields of type `Option<T>` are supported, and will be set to None if the field is missing from the map.
13///
14/// Supports structs with named fields that either `impl FromStr` or `serde::Deserialize`.
15/// Fields that implement `serde::Deserialize` can be annotated with `#[serde_json]` to parse the value as JSON.
16///
17/// # Example
18///
19/// ```rust
20/// use try_from_map::TryFromMap;
21///
22/// #[derive(TryFromMap, Debug)]
23/// struct Foo {
24/// a: i32,
25/// b: f32,
26/// c: Option<bool>,
27/// #[serde_json] // Parse as JSON as Vec<f64> does not impl FromStr
28/// d: Vec<f64>,
29/// }
30///
31///
32/// let map = std::collections::HashMap::from([
33/// ("a".to_string(), "42".to_string()),
34/// ("b".to_string(), "3.14".to_string()),
35/// ("d".to_string(), "[3.14, 2.71]".to_string()),
36/// ]);
37///
38/// let foo = Foo::try_from(map).unwrap();
39///
40/// println!("{:?}", foo);
41///
42/// assert_eq!(foo.a, 42);
43/// assert_eq!(foo.b, 3.14);
44/// assert_eq!(foo.c, None);
45/// assert_eq!(foo.d, vec![3.14, 2.71]);
46///
47/// ```
48#[proc_macro_derive(TryFromMap, attributes(serde_json))]
49pub fn derive_try_from_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
50 let parsed = parse_macro_input!(input as DeriveInput);
51 let output = _derive_try_from_map(parsed);
52
53 match output {
54 Ok(output) => output.into(),
55 Err(e) => e.into_compile_error().into(),
56 }
57}
58
59fn _derive_try_from_map(parsed: DeriveInput) -> syn::Result<TokenStream> {
60 let struct_name = parsed.ident.clone();
61 let fields = crate::field::parse_fields(&parsed)?;
62
63 let from_impl = crate::generation::generate_from_impl(&struct_name, &fields);
64
65 Ok(quote! {
66 #from_impl
67 })
68}