fsds_rs_derive/lib.rs
1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, FieldsNamed};
6
7/// Implements `TryFrom<Value>` for a #struct and `From<#struct>` for `Value`.
8///
9/// Note that [`rmpv::Value`] must be in scope for the derive to work.
10///
11/// ## From<#struct> for Value implementation
12///
13/// The implementation of `From<#struct>` for `Value` will create a `Value::Map`
14/// with the field names as keys and the field values as values.
15///
16/// Every field of the struct must implement `Into<Value>`.
17///
18/// ## TryFrom<Value> for #struct implementation
19///
20/// The implementation of `TryFrom<Value>` for `#struct` will try to convert a
21/// `Value::Map` to a struct.
22///
23/// Every field of the struct must implement `TryFrom<Value>`. The struct must
24/// have the same fields as the `Value::Map` keys.
25#[proc_macro_derive(FromIntoValue)]
26pub fn from_and_into_for_value_derive(input: TokenStream) -> TokenStream {
27 // Parsing TokenStream into DeriveInput.
28 let input = parse_macro_input!(input as DeriveInput);
29
30 // Extracting the struct name.
31 let name = input.ident;
32
33 // Extracting the fields of the struct.
34 let fields = if let Data::Struct(DataStruct {
35 fields: Fields::Named(FieldsNamed { named, .. }),
36 ..
37 }) = &input.data
38 {
39 named
40 } else {
41 // Works only for structs with named fields.
42 unimplemented!();
43 };
44
45 // ----------------------- //
46 // FROM<#struct> FOR VALUE //
47 // ----------------------- //
48
49 // Converting the struct fields into `Value`s.
50 let field_from_impl = fields.iter().map(|field| {
51 let field_name = &field.ident;
52 quote! {
53 vec.push((stringify!(#field_name).into(), value.#field_name.into()));
54 }
55 });
56
57 // From<#struct> for Value implementation.
58 let from_impl = quote! {
59 impl From<#name> for Value {
60 fn from(value: #name) -> Self {
61 let mut vec = Vec::new();
62
63 #(#field_from_impl)*
64
65 Value::Map(vec)
66 }
67 }
68 };
69
70 // -------------------------- //
71 // TRYFROM<VALUE> FOR #struct //
72 // -------------------------- //
73
74 let try_from_impl = {
75 // Converting the `Value::Map` fields into the struct fields.
76 let fields_def = fields.iter().map(|field| {
77 let field_name = &field.ident;
78 quote! {
79 let pos = map.iter().position(|(k, _)| k
80 .as_str()
81 .unwrap_or("Value::Map should contain only String keys to be converted to a struct.")
82 == stringify!(#field_name)
83 ).ok_or(anyhow::anyhow!("Field {} not found in Value::Map.", stringify!(#field_name)))?;
84 let #field_name = map
85 .remove(pos)
86 .1
87 .try_into()
88 .map_err(|_| anyhow::anyhow!("Every field of {} should be convertible to Value.", stringify!(#name)))?;
89 }
90 });
91
92 // Populating the struct fields.
93 let fields = fields.iter().map(|field| {
94 let field_name = &field.ident;
95 quote! {
96 #field_name
97 }
98 });
99
100 // TryFrom<Value> for #struct implementation.
101 quote! {
102 impl TryFrom<Value> for #name {
103 type Error = anyhow::Error;
104
105 fn try_from(value: Value) -> Result<Self, Self::Error> {
106 match value {
107 Value::Map(mut map) => {
108 #(#fields_def)*
109 if map.is_empty() {
110 Ok(#name {
111 #(#fields),*
112 })
113 } else {
114 Err(anyhow::anyhow!("Value::Map contains extra fields: {:?}", map))
115 }
116 }
117 _ => Err(anyhow::anyhow!("Value should be a Map to be converted to {}", stringify!(#name))),
118 }
119 }
120 }
121 }
122 };
123
124 // Expanding the macro.
125 let expanded = quote! {
126 #from_impl
127 #try_from_impl
128 };
129
130 // Returning the generated impl.
131 TokenStream::from(expanded)
132}