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}