syn_helpers/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod derive;
4mod fields;
5mod generic_helpers;
6mod lists_and_arguments;
7mod model;
8
9use std::error::Error;
10
11use either_n::Either2;
12use proc_macro2::{Ident, Span, TokenStream};
13use syn::{Attribute, ConstParam, Expr, FnArg, GenericParam, Path, Stmt, Type, TypeParam};
14
15pub use proc_macro2;
16pub use quote::{format_ident, quote, ToTokens};
17pub use syn;
18
19pub use derive::*;
20pub use fields::*;
21pub use lists_and_arguments::*;
22pub use model::Item;
23
24/// Returns true if same variant and same name, ignores bounds
25pub(crate) fn generic_parameters_have_same_name(
26    generic_parameter1: &GenericParam,
27    generic_parameter2: &GenericParam,
28) -> bool {
29    match (generic_parameter1, generic_parameter2) {
30        (GenericParam::Type(gtp1), GenericParam::Type(gtp2)) => gtp1.ident == gtp2.ident,
31        (GenericParam::Lifetime(glp1), GenericParam::Lifetime(glp2)) => {
32            glp1.lifetime.ident == glp2.lifetime.ident
33        }
34        (GenericParam::Const(gcp1), GenericParam::Const(gcp2)) => gcp1.ident == gcp2.ident,
35        _ => false,
36    }
37}
38
39/// Removes the bounds and uses the parameter as a reference
40pub(crate) fn generic_param_to_generic_argument_token_stream(
41    trait_generic_parameter: &GenericParam,
42) -> TokenStream {
43    match trait_generic_parameter {
44        GenericParam::Const(ConstParam { ident, .. })
45        | GenericParam::Type(TypeParam { ident, .. }) => ident.to_token_stream(),
46        GenericParam::Lifetime(lifetime) => lifetime.to_token_stream(),
47    }
48}
49
50pub fn dyn_error_to_compile_error_tokens(err: Box<dyn Error>) -> TokenStream {
51    let error_as_string = syn::LitStr::new(&err.to_string(), Span::call_site());
52    quote!(compile_error!(#error_as_string);)
53}
54
55/// A declaration for a Rust [trait](https://doc.rust-lang.org/rust-by-example/trait.html)
56pub struct Trait {
57    pub name: Path,
58    pub generic_parameters: Option<Vec<GenericParam>>,
59    pub items: Vec<TraitItem>,
60}
61
62/// Statements returned from the handler
63type HandlerResult = Result<Vec<Stmt>, Box<dyn Error>>;
64
65/// A item under a trait
66pub enum TraitItem {
67    Method {
68        name: Ident,
69        generic_parameters: Option<Vec<GenericParam>>,
70        self_type: TypeOfSelf,
71        other_parameters: Vec<FnArg>,
72        return_type: Option<Type>,
73        handler: Box<dyn for<'a> Fn(Item<'a>) -> HandlerResult>,
74    },
75    AssociatedFunction {
76        name: Ident,
77        generic_parameters: Option<Vec<GenericParam>>,
78        parameters: Vec<FnArg>,
79        return_type: Option<Type>,
80        handler: Box<dyn for<'a> Fn(&'a mut Structure) -> HandlerResult>,
81    },
82}
83
84/// What ownership the method requires
85#[derive(Clone, Copy)]
86pub enum TypeOfSelf {
87    /// `&self`
88    Reference,
89    /// `&mut self`
90    MutableReference,
91    /// `self`
92    Owned,
93}
94
95impl TypeOfSelf {
96    fn as_parameter_tokens(&self) -> TokenStream {
97        match self {
98            TypeOfSelf::Reference => quote!(&self),
99            TypeOfSelf::MutableReference => quote!(&mut self),
100            TypeOfSelf::Owned => quote!(self),
101        }
102    }
103
104    fn as_matcher_tokens(&self) -> TokenStream {
105        match self {
106            TypeOfSelf::Reference => quote!(ref),
107            TypeOfSelf::MutableReference => quote!(ref mut),
108            TypeOfSelf::Owned => TokenStream::default(),
109        }
110    }
111}
112
113impl TraitItem {
114    /// Create a new method (something that takes `self`, `&self` or `&mut self`)
115    pub fn new_method(
116        name: Ident,
117        generic_parameters: Option<Vec<GenericParam>>,
118        self_type: TypeOfSelf,
119        other_parameters: Vec<FnArg>,
120        return_type: Option<Type>,
121        handler: impl for<'a> Fn(Item<'a>) -> HandlerResult + 'static,
122    ) -> Self {
123        Self::Method {
124            name,
125            generic_parameters,
126            self_type,
127            other_parameters,
128            return_type,
129            handler: Box::new(handler),
130        }
131    }
132
133    /// Create a new associated function (doesn't take any reference of self)
134    pub fn new_associated_function(
135        name: Ident,
136        generic_parameters: Option<Vec<GenericParam>>,
137        parameters: Vec<FnArg>,
138        return_type: Option<Type>,
139        handler: impl for<'a> Fn(&'a mut Structure) -> HandlerResult + 'static,
140    ) -> Self {
141        Self::AssociatedFunction {
142            name,
143            generic_parameters,
144            parameters,
145            return_type,
146            handler: Box::new(handler),
147        }
148    }
149}
150
151/// An [Enum](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html)
152pub struct EnumStructure {
153    name: Ident,
154    attrs: Vec<Attribute>,
155    variants: Vec<EnumVariant>,
156}
157
158impl EnumStructure {
159    pub fn get_variants(&self) -> &[EnumVariant] {
160        &self.variants
161    }
162
163    pub fn get_variants_mut(&mut self) -> &mut [EnumVariant] {
164        self.variants.as_mut_slice()
165    }
166}
167
168impl HasAttributes for EnumStructure {
169    fn get_attributes(&self) -> &[Attribute] {
170        &self.attrs
171    }
172}
173
174/// A member of an [EnumStructure]
175pub struct EnumVariant {
176    full_path: Path,
177    pub idx: usize,
178    fields: Fields,
179}
180
181/// A [Struct](https://doc.rust-lang.org/rust-by-example/custom_types/structs.html)
182pub struct StructStructure {
183    name: Ident,
184    fields: Fields,
185}
186
187/// A Rust structure which can be *created* (either a struct or enum variant)
188pub trait Constructable {
189    /// Get the path required to construct the expression
190    fn get_constructor_path(&self) -> Path;
191
192    /// Builds a constructor expression by evaluating a expression generator for each field
193    fn build_constructor(
194        &self,
195        generator: impl Fn(NamedOrUnnamedField) -> Result<Expr, Box<dyn Error>>,
196    ) -> Result<Expr, Box<dyn std::error::Error>>;
197
198    fn get_fields(&self) -> &Fields;
199    fn get_fields_mut(&mut self) -> &mut Fields;
200}
201
202impl Constructable for StructStructure {
203    fn build_constructor(
204        &self,
205        generator: impl Fn(NamedOrUnnamedField) -> Result<Expr, Box<dyn Error>>,
206    ) -> Result<Expr, Box<dyn std::error::Error>> {
207        self.fields
208            .to_constructor(generator, self.name.clone().into())
209    }
210
211    fn get_fields(&self) -> &Fields {
212        &self.fields
213    }
214
215    fn get_fields_mut(&mut self) -> &mut Fields {
216        &mut self.fields
217    }
218
219    fn get_constructor_path(&self) -> Path {
220        self.name.clone().into()
221    }
222}
223
224impl Constructable for EnumVariant {
225    fn build_constructor(
226        &self,
227        generator: impl Fn(NamedOrUnnamedField) -> Result<Expr, Box<dyn Error>>,
228    ) -> Result<Expr, Box<dyn std::error::Error>> {
229        self.fields
230            .to_constructor(generator, self.full_path.clone())
231    }
232
233    fn get_fields(&self) -> &Fields {
234        &self.fields
235    }
236
237    fn get_fields_mut(&mut self) -> &mut Fields {
238        &mut self.fields
239    }
240
241    fn get_constructor_path(&self) -> Path {
242        self.full_path.clone()
243    }
244}
245
246pub trait HasAttributes {
247    fn get_attributes(&self) -> &[Attribute];
248}
249
250/// Either a [StructStructure] or [EnumStructure]
251pub enum Structure {
252    Struct(StructStructure),
253    Enum(EnumStructure),
254}
255
256/// Either a [StructStructure] or [EnumVariant] (with it's parents attributes)
257pub enum ConstructableStructure<'a> {
258    Struct(&'a mut StructStructure),
259    EnumVariant(&'a mut EnumVariant, &'a [Attribute]),
260}
261
262impl Structure {
263    /// Iterator over all the fields
264    fn all_fields(&self) -> impl Iterator<Item = NamedOrUnnamedField<'_>> {
265        match self {
266            Structure::Struct(r#struct) => Either2::One(r#struct.get_fields().fields_iterator()),
267            Structure::Enum(r#enum) => Either2::Two(
268                r#enum
269                    .variants
270                    .iter()
271                    .flat_map(|variant| variant.get_fields().fields_iterator()),
272            ),
273        }
274    }
275
276    /// The top attributes
277    pub fn get_attributes(&self) -> &[Attribute] {
278        match self {
279            // attributes have been moved to fields here
280            Structure::Struct(r#struct) => r#struct.fields.get_field_attributes(),
281            Structure::Enum(r#enum) => r#enum.attrs.as_slice(),
282        }
283    }
284
285    /// The declaration name
286    pub fn get_name(&self) -> &Ident {
287        match self {
288            Structure::Struct(r#struct) => &r#struct.name,
289            Structure::Enum(r#enum) => &r#enum.name,
290        }
291    }
292}
293
294impl Constructable for ConstructableStructure<'_> {
295    fn build_constructor(
296        &self,
297        generator: impl Fn(NamedOrUnnamedField) -> Result<Expr, Box<dyn Error>>,
298    ) -> Result<Expr, Box<dyn std::error::Error>> {
299        match self {
300            ConstructableStructure::Struct(r#struct) => r#struct.build_constructor(generator),
301            ConstructableStructure::EnumVariant(enum_variant, _) => {
302                enum_variant.build_constructor(generator)
303            }
304        }
305    }
306
307    fn get_fields(&self) -> &Fields {
308        match self {
309            ConstructableStructure::Struct(r#struct) => r#struct.get_fields(),
310            ConstructableStructure::EnumVariant(enum_variant, _) => enum_variant.get_fields(),
311        }
312    }
313
314    fn get_fields_mut(&mut self) -> &mut Fields {
315        match self {
316            ConstructableStructure::Struct(r#struct) => r#struct.get_fields_mut(),
317            ConstructableStructure::EnumVariant(enum_variant, _) => enum_variant.get_fields_mut(),
318        }
319    }
320
321    fn get_constructor_path(&self) -> Path {
322        match self {
323            ConstructableStructure::Struct(r#struct) => r#struct.get_constructor_path(),
324            ConstructableStructure::EnumVariant(enum_variant, _) => {
325                enum_variant.get_constructor_path()
326            }
327        }
328    }
329}
330
331impl<'a> ConstructableStructure<'a> {
332    pub fn as_enum_variant(&'a self) -> Option<&'a EnumVariant> {
333        if let Self::EnumVariant(variant, _) = self {
334            Some(&**variant)
335        } else {
336            None
337        }
338    }
339
340    pub fn all_attributes<'b: 'a>(&'b self) -> impl Iterator<Item = &'b Attribute> + '_ {
341        match self {
342            ConstructableStructure::Struct(r#struct) => {
343                Either2::One(r#struct.get_fields().get_field_attributes().iter())
344            }
345            ConstructableStructure::EnumVariant(r#enum, parent_attrs) => Either2::Two(
346                parent_attrs
347                    .iter()
348                    .chain(r#enum.get_fields().get_field_attributes().iter()),
349            ),
350        }
351    }
352}
353
354/// Prints a path out as its source representation
355pub fn path_to_string(path: Path) -> String {
356    let mut buf = String::new();
357    if path.leading_colon.is_some() {
358        buf.push_str("::");
359    }
360    for (idx, segment) in path.segments.iter().enumerate() {
361        buf.push_str(&segment.ident.to_string());
362        if !segment.arguments.is_empty() {
363            todo!()
364        }
365        if idx != path.segments.len() - 1 {
366            buf.push_str("::");
367        }
368    }
369    buf
370}