impl_tools_lib/
fields.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Custom version of [`syn`] fields types supporting initializers
7
8use proc_macro2::TokenStream;
9use proc_macro_error2::emit_error;
10use quote::{ToTokens, TokenStreamExt};
11use syn::parse::{Parse, ParseStream, Result};
12use syn::punctuated::Punctuated;
13use syn::{token, Attribute, Expr, Ident, Token, Type, Visibility};
14
15/// Struct style: unit/tuple/regular
16#[derive(Debug)]
17pub enum StructStyle {
18    /// A unit struct (e.g. `struct Foo;`)
19    Unit(Token![;]),
20    /// A tuple struct (e.g. `struct Foo(f32, f32);`)
21    Tuple(token::Paren, Token![;]),
22    /// A regular struct (e.g. `struct Foo { x: f32, y: f32 }`)
23    Regular(token::Brace),
24}
25
26/// Data stored within an enum variant or struct.
27///
28/// This is a variant of [`syn::Fields`] supporting field initializers.
29#[derive(Debug)]
30pub enum Fields {
31    /// Named fields of a struct or struct variant such as `Point { x: f64, y: f64 }`.
32    Named(FieldsNamed),
33    /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`.
34    Unnamed(FieldsUnnamed),
35    /// Unit struct or unit variant such as `None`.
36    Unit,
37}
38
39/// Named fields of a struct or struct variant such as `Point { x: f64, y: f64 }`.
40///
41/// This is a variant of [`syn::FieldsNamed`] supporting field initializers.
42#[derive(Debug)]
43pub struct FieldsNamed {
44    /// `{ ... }` around fields
45    pub brace_token: token::Brace,
46    /// Fields
47    pub fields: Punctuated<Field, Token![,]>,
48}
49
50/// Unnamed fields of a tuple struct or tuple variant such as `Some(T)`.
51///
52/// This is a variant of [`syn::FieldsUnnamed`] supporting field initializers.
53#[derive(Debug)]
54pub struct FieldsUnnamed {
55    /// `( ... )` around fields
56    pub paren_token: token::Paren,
57    /// Fields
58    pub fields: Punctuated<Field, Token![,]>,
59}
60
61/// A field of a struct or enum variant.
62///
63/// This is a variant of [`syn::Field`] supporting field initializers.
64#[derive(Debug)]
65pub struct Field {
66    /// Attributes tagged on the field.
67    pub attrs: Vec<Attribute>,
68    /// Visibility of the field.
69    pub vis: Visibility,
70    /// Name of the field, if any.
71    pub ident: Option<Ident>,
72    /// `:` token before type
73    pub colon_token: Option<Token![:]>,
74    /// Type of the field.
75    pub ty: Type,
76    /// Optional field initializer.
77    ///
78    /// This is considered legal input when parsing, but not legal output. An
79    /// attribute rule such as [`AttrImplDefault`](crate::scope::AttrImplDefault)
80    /// must remove the initializer before output is generated.
81    pub assign: Option<(Token![=], Expr)>,
82}
83
84// Copied from syn, modified
85pub(crate) mod parsing {
86    use super::*;
87    use syn::ext::IdentExt;
88    use syn::{braced, parenthesized, WhereClause};
89
90    impl Parse for FieldsNamed {
91        fn parse(input: ParseStream) -> Result<Self> {
92            let content;
93            let brace_token = braced!(content in input);
94            Ok(FieldsNamed {
95                brace_token,
96                fields: content.parse_terminated(Field::parse_named, Token![,])?,
97            })
98        }
99    }
100
101    impl Parse for FieldsUnnamed {
102        fn parse(input: ParseStream) -> Result<Self> {
103            let content;
104            let paren_token = parenthesized!(content in input);
105            Ok(FieldsUnnamed {
106                paren_token,
107                fields: content.parse_terminated(Field::parse_unnamed, Token![,])?,
108            })
109        }
110    }
111
112    impl Field {
113        /// Parse a named field
114        pub fn parse_named(input: ParseStream) -> Result<Self> {
115            Ok(Field {
116                attrs: input.call(Attribute::parse_outer)?,
117                vis: input.parse()?,
118                ident: Some(if input.peek(Token![_]) {
119                    input.call(Ident::parse_any)
120                } else {
121                    input.parse()
122                }?),
123                colon_token: Some(input.parse()?),
124                ty: input.parse()?,
125                assign: if input.peek(Token![=]) {
126                    Some((input.parse()?, input.parse()?))
127                } else {
128                    None
129                },
130            })
131        }
132
133        /// Parse an unnamed field
134        pub fn parse_unnamed(input: ParseStream) -> Result<Self> {
135            Ok(Field {
136                attrs: input.call(Attribute::parse_outer)?,
137                vis: input.parse()?,
138                ident: None,
139                colon_token: None,
140                ty: input.parse()?,
141                assign: if input.peek(Token![=]) {
142                    Some((input.parse()?, input.parse()?))
143                } else {
144                    None
145                },
146            })
147        }
148    }
149
150    pub(crate) fn data_struct(
151        input: ParseStream,
152    ) -> Result<(Option<WhereClause>, Fields, Option<Token![;]>)> {
153        let mut lookahead = input.lookahead1();
154        let mut where_clause = None;
155        if lookahead.peek(Token![where]) {
156            where_clause = Some(input.parse()?);
157            lookahead = input.lookahead1();
158        }
159
160        if where_clause.is_none() && lookahead.peek(token::Paren) {
161            let fields = input.parse()?;
162
163            lookahead = input.lookahead1();
164            if lookahead.peek(Token![where]) {
165                where_clause = Some(input.parse()?);
166                lookahead = input.lookahead1();
167            }
168
169            if lookahead.peek(Token![;]) {
170                let semi = input.parse()?;
171                Ok((where_clause, Fields::Unnamed(fields), Some(semi)))
172            } else {
173                Err(lookahead.error())
174            }
175        } else if lookahead.peek(token::Brace) {
176            let fields = parse_braced(input)?;
177            Ok((where_clause, Fields::Named(fields), None))
178        } else if lookahead.peek(Token![;]) {
179            let semi = input.parse()?;
180            Ok((where_clause, Fields::Unit, Some(semi)))
181        } else {
182            Err(lookahead.error())
183        }
184    }
185
186    fn parse_braced(input: ParseStream) -> Result<FieldsNamed> {
187        let content;
188        let brace_token = braced!(content in input);
189        let fields = content.parse_terminated(Field::parse_named, Token![,])?;
190        Ok(FieldsNamed {
191            brace_token,
192            fields,
193        })
194    }
195}
196
197mod printing {
198    use super::*;
199
200    impl ToTokens for FieldsNamed {
201        fn to_tokens(&self, tokens: &mut TokenStream) {
202            self.brace_token.surround(tokens, |tokens| {
203                self.fields.to_tokens(tokens);
204            });
205        }
206    }
207
208    impl ToTokens for FieldsUnnamed {
209        fn to_tokens(&self, tokens: &mut TokenStream) {
210            self.paren_token.surround(tokens, |tokens| {
211                self.fields.to_tokens(tokens);
212            });
213        }
214    }
215
216    impl ToTokens for Field {
217        fn to_tokens(&self, tokens: &mut TokenStream) {
218            tokens.append_all(&self.attrs);
219            self.vis.to_tokens(tokens);
220            if let Some(ident) = &self.ident {
221                ident.to_tokens(tokens);
222                self.colon_token.unwrap_or_default().to_tokens(tokens);
223            }
224            self.ty.to_tokens(tokens);
225
226            if let Some(ref assign) = self.assign {
227                emit_error!(
228                    assign.0,
229                    "default value on `struct` field in output";
230                    help = "did you mean to use the `#[impl_default]` attribute?",
231                );
232            }
233        }
234    }
235}