1use std::collections::HashMap;
37
38use parse::parse_params;
39use proc_macro::TokenStream;
40use syn::{parse_macro_input, DeriveInput, Ident, Path};
41
42mod build;
43mod map;
44mod parse;
45
46#[proc_macro_derive(JustConvert, attributes(convert))]
47pub fn just_convert_derive(input: TokenStream) -> TokenStream {
48 let input = parse_macro_input!(input as DeriveInput);
49 build_impl(input).into()
50}
51
52fn build_impl(input: DeriveInput) -> proc_macro2::TokenStream {
53 let params = match parse_params(&input) {
54 Ok(p) => p,
55 Err(err) => return err.to_compile_error(),
56 };
57
58 params
59 .build()
60 .unwrap_or_else(syn::Error::into_compile_error)
61}
62
63#[derive(Debug)]
64struct Params {
65 name: Ident,
66 from: Vec<PathParams>,
67 into: Vec<PathParams>,
68 fields: Fields,
69}
70
71#[derive(Debug)]
72struct PathParams {
73 path: Path,
74 default: bool,
75 wrap_option: bool,
76}
77
78#[derive(Debug, Clone)]
79struct FieldParams {
80 map: FieldValue<proc_macro2::Literal>,
81 rename: FieldValue<Ident>,
82 wrap: FieldValue<bool>,
83 unwrap: FieldValue<bool>,
84 skip: FieldValue<bool>,
85 a_type: AdditionalType,
86}
87
88impl FieldParams {
89 fn new() -> Self {
90 Self {
91 map: FieldValue::new(),
92 rename: FieldValue::new(),
93 wrap: FieldValue::new(),
94 unwrap: FieldValue::new(),
95 skip: FieldValue::new(),
96 a_type: AdditionalType::None,
97 }
98 }
99}
100
101#[derive(Debug, Default, Clone)]
102struct FieldValue<T> {
103 common: Option<T>,
104 common_from: Option<T>,
105 common_into: Option<T>,
106 from: HashMap<Path, T>,
107 into: HashMap<Path, T>,
108}
109
110impl<T> FieldValue<T> {
111 fn new() -> Self {
112 Self {
113 common: None,
114 common_from: None,
115 common_into: None,
116 from: [].into(),
117 into: [].into(),
118 }
119 }
120
121 fn set_from(&mut self, path: Option<Path>, value: T) {
122 if let Some(path) = path {
123 self.from.insert(path, value);
124 } else {
125 self.common_from = Some(value);
126 }
127 }
128
129 fn set_into(&mut self, path: Option<Path>, value: T) {
130 if let Some(path) = path {
131 self.into.insert(path, value);
132 } else {
133 self.common_into = Some(value);
134 }
135 }
136}
137
138#[derive(Debug, Default, Clone, Copy)]
139enum AdditionalType {
140 #[default]
141 None,
142 Option,
144 OptionVec,
146 Vec,
148 VecOption,
150}
151
152impl AdditionalType {
153 fn is_option(self) -> bool {
154 matches!(self, Self::Option)
155 }
156
157 fn is_option_vec(self) -> bool {
158 matches!(self, Self::OptionVec)
159 }
160
161 fn is_vec(self) -> bool {
162 matches!(self, Self::Vec)
163 }
164
165 fn is_vec_option(self) -> bool {
166 matches!(self, Self::VecOption)
167 }
168}
169
170type Fields = HashMap<Ident, FieldParams>;