rvs_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::*;
4use syn::*;
5
6#[proc_macro_derive(ValueStruct)]
7pub fn value_struct_macro(input: TokenStream) -> TokenStream {
8    let item: syn::Item = syn::parse(input).expect("failed to parse input");
9    let span = Span::call_site();
10    match item {
11        Item::Struct(ref struct_item) => match struct_item.fields {
12            Fields::Unnamed(ref unnamed_fields) if unnamed_fields.unnamed.len() == 1 => {
13                let field = unnamed_fields.unnamed.first().unwrap();
14                let struct_name = &struct_item.ident;
15                let field_type = &field.ty;
16                let parsed_field_type = parse_field_type(field_type);
17
18                let type_dependent_impls =
19                    create_dependent_impls(struct_name, field_type, parsed_field_type.as_ref());
20
21                let output = quote! {
22                    #type_dependent_impls
23                };
24
25                output.into()
26            }
27            _ => Error::new(
28                span,
29                "ValueStruct works only on structs with one unnamed field",
30            )
31            .to_compile_error()
32            .into(),
33        },
34        _ => Error::new(span, "ValueStruct works only on structs")
35            .to_compile_error()
36            .into(),
37    }
38}
39
40enum ParsedType {
41    StringType,
42    ScalarType,
43}
44
45#[inline]
46fn parse_field_type(field_type: &Type) -> Option<ParsedType> {
47    match field_type {
48        Type::Path(ref path) => {
49            let full_type_path: &String = &path
50                .path
51                .segments
52                .iter()
53                .map(|s| s.ident.to_string())
54                .collect::<Vec<String>>()
55                .join("::");
56
57            match full_type_path.as_str() {
58                "String" | "std::string::String" => Some(ParsedType::StringType),
59                "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
60                | "u128" | "usize" => Some(ParsedType::ScalarType),
61                _ => None,
62            }
63        }
64        _ => None,
65    }
66}
67
68#[inline]
69fn create_dependent_impls(
70    struct_name: &Ident,
71    field_type: &Type,
72    parsed_field_type: Option<&ParsedType>,
73) -> proc_macro2::TokenStream {
74    let all_types_base_impl = quote! {
75
76        impl #struct_name {
77            pub const fn new(value: #field_type) -> Self {
78                Self(value)
79            }
80        }
81
82        impl ValueStruct for #struct_name {
83            type ValueType = #field_type;
84
85            #[inline]
86            fn value(&self) -> &Self::ValueType {
87                &self.0
88            }
89
90            #[inline]
91            fn into_value(self) -> Self::ValueType {
92                self.0
93            }
94        }
95
96       impl std::convert::From<#field_type> for #struct_name {
97            fn from(value: #field_type) -> Self {
98                #struct_name(value)
99            }
100       }
101
102        impl std::convert::From<&#field_type> for #struct_name {
103            fn from(value: &#field_type) -> Self {
104                #struct_name(value.clone())
105            }
106        }
107    };
108
109    match parsed_field_type {
110        Some(ParsedType::ScalarType) => {
111            quote! {
112                #all_types_base_impl
113
114                impl std::fmt::Display for #struct_name {
115                    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116                        self.value().fmt(f)
117                    }
118                }
119            }
120        }
121        Some(ParsedType::StringType) => {
122            quote! {
123                #all_types_base_impl
124
125                impl std::convert::From<&str> for #struct_name {
126                    fn from(value: &str) -> Self {
127                        #struct_name(String::from(value))
128                    }
129                }
130
131                impl std::str::FromStr for #struct_name {
132                    type Err = std::string::ParseError;
133
134                    fn from_str(s: &str) -> Result<Self, Self::Err> {
135                        Ok(#struct_name(s.into()))
136                    }
137                }
138
139                impl std::convert::AsRef<str> for #struct_name {
140                    fn as_ref(&self) -> &str {
141                        self.value().as_str()
142                    }
143                }
144
145                impl std::fmt::Display for #struct_name {
146                    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
147                        self.value().fmt(f)
148                    }
149                }
150
151            }
152        }
153        _ => {
154            quote! {
155                #all_types_base_impl
156            }
157        }
158    }
159}