macro_tools_attr_prop/
macro_tools_attr_prop.rs

1//!
2//! ### Example: Attribute Properties
3//!
4//! This example demonstrates an approach to parsing attributes and their properties.
5//! The attributes are collected into a struct that aggregates them, and attribute properties
6//! are parsed using reusable components from a library. The example shows how to use
7//! `AttributePropertyBoolean` for parsing boolean properties and the roles of the traits
8//! `AttributePropertyComponent` and `AttributeComponent`. The `Assign` trait is
9//! also used to simplify the logic of assigning fields.
10//!
11//! Attributes are collected into a `ItemAttributes` struct, and attribute properties are parsed
12//! using reusable components like `AttributePropertyBoolean`.
13//!
14//! - `AttributeComponent`: A trait that defines how an attribute should be parsed from a `syn::Attribute`.
15//! - `AttributePropertyComponent`: A trait that defines a marker for attribute properties.
16//! - `Assign`: A trait that simplifies the logic of assigning fields to a struct. Using a
17//! component-based approach requires each field to have a unique type, which aligns with the
18//! strengths of strongly-typed languages. This method ensures that the logic of
19//! assigning values to fields is encapsulated within the fields themselves, promoting modularity
20//! and reusability.
21//!
22//! The reusable property components from the library come with parameters that distinguish
23//! different properties of the same type. This is useful when an attribute has multiple boolean
24//! properties, for instance. Such an approach helps to avoid limitations where it is
25//! always possible to define traits for custom types, while it may not be possible for types
26//! defined in other crates.
27//!
28
29#[ cfg( not( all( feature = "enabled", feature = "attr_prop", debug_assertions ) )  ) ]
30fn main(){}
31#[ cfg( all( feature = "enabled", feature = "attr_prop",  debug_assertions )  ) ]
32fn main()
33{
34
35  use macro_tools::
36  {
37    ct,
38    syn_err,
39    return_syn_err,
40    qt,
41    Result,
42    AttributeComponent,
43    AttributePropertyComponent,
44    AttributePropertyBoolean,
45    AttributePropertySingletone,
46    Assign,
47  };
48
49  /// Represents the attributes of a struct. Aggregates all its attributes.
50  #[ derive( Debug, Default ) ]
51  pub struct ItemAttributes
52  {
53    /// Attribute for customizing the mutation process.
54    pub mutator : AttributeMutator,
55  }
56
57  impl ItemAttributes
58  {
59    /// Constructs a `ItemAttributes` instance from an iterator of attributes.
60    ///
61    /// This function parses the provided attributes and assigns them to the
62    /// appropriate fields in the `ItemAttributes` struct.
63    pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self >
64    {
65      let mut result = Self::default();
66
67      // Closure to generate an error message for unknown attributes.
68      let error = | attr : & syn::Attribute | -> syn::Error
69      {
70        let known_attributes = ct::str::format!
71        (
72          "Known attributes are: {}, {}.",
73          "debug",
74          AttributeMutator::KEYWORD,
75        );
76        syn_err!
77        (
78          attr,
79          "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n  {known_attributes}\n  But got: '{}'",
80          qt! { #attr }
81        )
82      };
83
84      for attr in attrs
85      {
86        let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?;
87        let key_str = format!( "{}", key_ident );
88        // if attr::is_standard( & key_str )
89        // {
90        //   continue;
91        // }
92        match key_str.as_ref()
93        {
94          AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ),
95          "debug" => {},
96          _ => {},
97          // _ => return Err( error( attr ) ),
98        }
99      }
100
101      Ok( result )
102    }
103  }
104
105  /// Represents attributes for customizing the mutation process in a forming operation.
106  ///
107  /// ## Example of code
108  ///
109  /// ```ignore
110  /// #[ mutator( custom = true, debug = true ) ]
111  /// ```
112  #[ derive( Debug, Default ) ]
113  pub struct AttributeMutator
114  {
115    /// Indicates whether a custom mutator should be generated.
116    /// Defaults to `false`, meaning no custom mutator is generated unless explicitly requested.
117    pub custom : AttributePropertyCustom,
118    /// Specifies whether to print code generated for the field.
119    /// Defaults to `false`, which means no hint is provided unless explicitly requested.
120    pub debug : AttributePropertyDebug,
121  }
122
123  impl AttributeComponent for AttributeMutator
124  {
125    const KEYWORD : & 'static str = "mutator";
126
127    /// Parses a `syn::Attribute` into an `AttributeMutator`.
128    fn from_meta( attr : & syn::Attribute ) -> Result< Self >
129    {
130      match attr.meta
131      {
132        syn::Meta::List( ref meta_list ) =>
133        {
134          return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() );
135        },
136        syn::Meta::Path( ref _path ) =>
137        {
138          return Ok( Default::default() )
139        },
140        _ => return_syn_err!
141        (
142          attr,
143          "Expects an attribute of format `#[ mutator( custom = true ) ]`. \nGot: {}",
144          qt! { #attr }
145        ),
146      }
147    }
148  }
149
150  // Implement `Assign` trait to allow assigning `AttributeMutator` to `ItemAttributes`.
151  impl< IntoT > Assign< AttributeMutator, IntoT > for ItemAttributes
152  where
153    IntoT : Into< AttributeMutator >,
154  {
155    #[ inline( always ) ]
156    fn assign( & mut self, component : IntoT )
157    {
158      self.mutator = component.into();
159    }
160  }
161
162  // Implement `Assign` trait to allow assigning `AttributePropertyDebug` to `AttributeMutator`.
163  impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeMutator
164  where
165    IntoT : Into< AttributePropertyDebug >,
166  {
167    #[ inline( always ) ]
168    fn assign( & mut self, component : IntoT )
169    {
170      self.debug = component.into();
171    }
172  }
173
174  // Implement `Assign` trait to allow assigning `AttributePropertyCustom` to `AttributeMutator`.
175  impl< IntoT > Assign< AttributePropertyCustom, IntoT > for AttributeMutator
176  where
177    IntoT : Into< AttributePropertyCustom >,
178  {
179    #[ inline( always ) ]
180    fn assign( & mut self, component : IntoT )
181    {
182      self.custom = component.into();
183    }
184  }
185
186  impl syn::parse::Parse for AttributeMutator
187  {
188    fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self >
189    {
190      let mut result = Self::default();
191
192      let error = | ident : & syn::Ident | -> syn::Error
193      {
194        let known = ct::str::format!
195        (
196          "Known entries of attribute {} are: {}, {}.",
197          AttributeMutator::KEYWORD,
198          AttributePropertyCustom::KEYWORD,
199          AttributePropertyDebug::KEYWORD,
200        );
201        syn_err!
202        (
203          ident,
204          r#"Expects an attribute of format '#[ mutator( custom = false ) ]'
205    {known}
206    But got: '{}'
207  "#,
208          qt! { #ident }
209        )
210      };
211
212      while !input.is_empty()
213      {
214        let lookahead = input.lookahead1();
215        if lookahead.peek( syn::Ident )
216        {
217          let ident : syn::Ident = input.parse()?;
218
219          match ident.to_string().as_str()
220          {
221            AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::parse( input )? ),
222            AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ),
223            _ => return Err( error( & ident ) ),
224          }
225        }
226        else
227        {
228          return Err( lookahead.error() );
229        }
230
231        // Optional comma handling
232        if input.peek( syn::Token![,] )
233        {
234          input.parse::< syn::Token![,] >()?;
235        }
236      }
237
238      Ok( result )
239    }
240  }
241
242  // == Attribute properties
243
244  /// Marker type for attribute property to specify whether to provide a sketch as a hint.
245  /// Defaults to `false`, which means no hint is provided unless explicitly requested.
246  #[ derive( Debug, Default, Clone, Copy ) ]
247  pub struct AttributePropertyDebugMarker;
248
249  impl AttributePropertyComponent for AttributePropertyDebugMarker
250  {
251    const KEYWORD : & 'static str = "debug";
252  }
253
254  /// Specifies whether to provide a sketch as a hint.
255  /// Defaults to `false`, which means no hint is provided unless explicitly requested.
256  pub type AttributePropertyDebug = AttributePropertySingletone< AttributePropertyDebugMarker >;
257
258  // ==
259
260  /// Marker type for attribute property to indicate whether a custom code should be generated.
261  /// Defaults to `false`, meaning no custom code is generated unless explicitly requested.
262  #[ derive( Debug, Default, Clone, Copy ) ]
263  pub struct AttributePropertyCustomMarker;
264
265  impl AttributePropertyComponent for AttributePropertyCustomMarker
266  {
267    const KEYWORD : & 'static str = "custom";
268  }
269
270  /// Indicates whether a custom code should be generated.
271  /// Defaults to `false`, meaning no custom code is generated unless explicitly requested.
272  pub type AttributePropertyCustom = AttributePropertyBoolean< AttributePropertyCustomMarker >;
273
274  // == test code
275
276  // Parse an attribute and construct a `ItemAttributes` instance.
277  let input : syn::Attribute = syn::parse_quote!( #[ mutator( custom = true ) ] );
278  let attrs : ItemAttributes = ItemAttributes::from_attrs( std::iter::once( & input ) ).unwrap();
279  println!( "{:?}", attrs );
280
281  // Test `AttributePropertyBoolean` functionality.
282  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = AttributePropertyBoolean::default();
283  assert_eq!( attr.internal(), false );
284  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = true.into();
285  assert_eq!( attr.internal(), true );
286  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = false.into();
287  assert_eq!( attr.internal(), false );
288
289}