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