33 个重大版本

新版本 0.39.0 2024年8月16日
0.38.0 2024年7月13日
0.37.0 2024年7月13日
0.23.0 2024年3月22日
0.1.1 2022年1月15日

#188开发工具

Download history 112/week @ 2024-04-26 135/week @ 2024-05-03 3164/week @ 2024-05-10 1565/week @ 2024-05-17 1422/week @ 2024-05-24 3564/week @ 2024-05-31 1179/week @ 2024-06-07 463/week @ 2024-06-14 294/week @ 2024-06-21 1346/week @ 2024-06-28 351/week @ 2024-07-05 1512/week @ 2024-07-12 646/week @ 2024-07-19 919/week @ 2024-07-26 283/week @ 2024-08-02 854/week @ 2024-08-09

3,072 每月下载次数
63crate中(直接使用7个)

MIT 许可证

340KB
6K SLoC

模块 :: proc_macro_tools

experimental rust-status docs.rs Open in Gitpod discord

过程宏编写工具。

示例:简单示例

typ::type_parameters 的目的是从一个给定的 Rust 类型中提取类型参数。在这个示例中,我们生成一个类型 core::option::Option<i8, i16, i32, i64> 并提取其类型参数。

#[ cfg( not( all( feature = "enabled", feature = "typ" ) ) ) ]
fn main(){}
#[ cfg( all( feature = "enabled", feature = "typ" ) ) ]
fn main()
{
  // Import necessary macros and modules from the `macro_tools` crate.
  use macro_tools::{ typ, qt };

  // Generate a token stream representing the type `core::option::Option<i8, i16, i32, i64>`.
  let code = qt!( core::option::Option< i8, i16, i32, i64 > );

  // Parse the generated token stream into a `syn::Type` object.
  // `syn::Type` is a syntax tree node representing a Rust type.
  let tree_type = syn::parse2::< syn::Type >( code ).unwrap();

  // Extract type parameters from the parsed type.
  // `typ::type_parameters` takes a reference to a `syn::Type` and a range.
  // It returns a vector of type parameters within the specified range.
  // Here, `0..=2` specifies that we are interested in the first three type parameters.
  let got = typ::type_parameters( &tree_type, 0..=2 );

  // Iterate over the extracted type parameters and print each one.
  // The `qt!` macro is used to convert the type parameter back to a token stream for printing.
  got.iter().for_each( | e | println!( "{}", qt!( #e ) ) );

  /* Expected output:
     i8
     i16
     i32
  */
}

尝试运行 cargo run --example macro_tools_trivial
查看代码.

示例:属性属性

此示例演示了解析属性及其属性的方法。属性被收集到一个结构体中,该结构体将它们聚合在一起,并且使用库中的可重用组件解析属性属性。示例展示了如何使用 AttributePropertyBoolean 解析布尔属性以及 AttributePropertyComponentAttributeComponent 特性的作用。还使用了 Assign 特性来简化字段分配的逻辑。

属性被收集到一个 ItemAttributes 结构体中,属性属性使用可重用组件如 AttributePropertyBoolean 进行解析。

  • AttributeComponent:一个特性,定义了如何从 syn::Attribute 中解析属性。
  • AttributePropertyComponent:一个特性,定义了属性属性的标记。
  • Assign:一个简化将字段分配给结构的逻辑的特质。采用基于组件的方法要求每个字段具有唯一的类型,这与强类型语言的优点相符。此方法确保将值分配给字段的逻辑封装在字段本身中,从而促进模块化和可重用性。

来自库的可重用属性组件具有参数,可以区分同一类型的不同属性。例如,当属性有多个布尔属性时,这很有用。这种做法有助于避免限制,即总是可以定义针对自定义类型的特质,而其他crate中定义的类型可能不行。


#[ cfg( not( all( feature = "enabled", feature = "attr_prop", debug_assertions ) )  ) ]
fn main(){}
#[ cfg( all( feature = "enabled", feature = "attr_prop", debug_assertions )  ) ]
fn main()
{

  use macro_tools::
  {
    attr,
    ct,
    syn_err,
    return_syn_err,
    qt,
    Result,
    AttributeComponent,
    AttributePropertyComponent,
    AttributePropertyBoolean,
    AttributePropertySingletone,
    Assign,
  };

  /// Represents the attributes of a struct. Aggregates all its attributes.
  #[ derive( Debug, Default ) ]
  pub struct ItemAttributes
  {
    /// Attribute for customizing the mutation process.
    pub mutator : AttributeMutator,
  }

  impl ItemAttributes
  {
    /// Constructs a `ItemAttributes` instance from an iterator of attributes.
    ///
    /// This function parses the provided attributes and assigns them to the
    /// appropriate fields in the `ItemAttributes` struct.
    pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self >
    {
      let mut result = Self::default();

      // Closure to generate an error message for unknown attributes.
      let error = | attr : & syn::Attribute | -> syn::Error
      {
        let known_attributes = ct::str::format!
        (
          "Known attributes are: {}, {}.",
          "debug",
          AttributeMutator::KEYWORD,
        );
        syn_err!
        (
          attr,
          "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n  {known_attributes}\n  But got: '{}'",
          qt! { #attr }
        )
      };

      for attr in attrs
      {
        let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?;
        let key_str = format!( "{}", key_ident );
        match key_str.as_ref()
        {
          AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ),
          "debug" => {},
          _ => {},
        }
      }

      Ok( result )
    }
  }

  /// Represents attributes for customizing the mutation process in a forming operation.
  ///
  /// ## Example of code
  ///
  /// ```ignore
  /// #[ mutator( custom = true, debug = true ) ]
  /// ```
  #[ derive( Debug, Default ) ]
  pub struct AttributeMutator
  {
    /// Indicates whether a custom mutator should be generated.
    /// Defaults to `false`, meaning no custom mutator is generated unless explicitly requested.
    pub custom : AttributePropertyCustom,
    /// Specifies whether to print code generated for the field.
    /// Defaults to `false`, which means no hint is provided unless explicitly requested.
    pub debug : AttributePropertyDebug,
  }

  impl AttributeComponent for AttributeMutator
  {
    const KEYWORD : & 'static str = "mutator";

    /// Parses a `syn::Attribute` into an `AttributeMutator`.
    fn from_meta( attr : & syn::Attribute ) -> Result< Self >
    {
      match attr.meta
      {
        syn::Meta::List( ref meta_list ) =>
        {
          return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() );
        },
        syn::Meta::Path( ref _path ) =>
        {
          return Ok( Default::default() )
        },
        _ => return_syn_err!
        (
          attr,
          "Expects an attribute of format `#[ mutator( custom = true ) ]`. \nGot: {}",
          qt! { #attr }
        ),
      }
    }
  }

  // Implement `Assign` trait to allow assigning `AttributeMutator` to `ItemAttributes`.
  impl< IntoT > Assign< AttributeMutator, IntoT > for ItemAttributes
  where
    IntoT : Into< AttributeMutator >,
  {
    #[ inline( always ) ]
    fn assign( & mut self, component : IntoT )
    {
      self.mutator = component.into();
    }
  }

  // Implement `Assign` trait to allow assigning `AttributePropertyDebug` to `AttributeMutator`.
  impl< IntoT > Assign< AttributePropertyDebug, IntoT > for AttributeMutator
  where
    IntoT : Into< AttributePropertyDebug >,
  {
    #[ inline( always ) ]
    fn assign( & mut self, component : IntoT )
    {
      self.debug = component.into();
    }
  }

  // Implement `Assign` trait to allow assigning `AttributePropertyCustom` to `AttributeMutator`.
  impl< IntoT > Assign< AttributePropertyCustom, IntoT > for AttributeMutator
  where
    IntoT : Into< AttributePropertyCustom >,
  {
    #[ inline( always ) ]
    fn assign( & mut self, component : IntoT )
    {
      self.custom = component.into();
    }
  }

  impl syn::parse::Parse for AttributeMutator
  {
    fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self >
    {
      let mut result = Self::default();

      let error = | ident : & syn::Ident | -> syn::Error
      {
        let known = ct::str::format!
        (
          "Known entries of attribute {} are: {}, {}.",
          AttributeMutator::KEYWORD,
          AttributePropertyCustom::KEYWORD,
          AttributePropertyDebug::KEYWORD,
        );
        syn_err!
        (
          ident,
          r#"Expects an attribute of format '#[ mutator( custom = false ) ]'
    {known}
    But got: '{}'
  "#,
          qt! { #ident }
        )
      };

      while !input.is_empty()
      {
        let lookahead = input.lookahead1();
        if lookahead.peek( syn::Ident )
        {
          let ident : syn::Ident = input.parse()?;

          match ident.to_string().as_str()
          {
            AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::parse( input )? ),
            AttributePropertyDebug::KEYWORD => result.assign( AttributePropertyDebug::from( true ) ),
            _ => return Err( error( & ident ) ),
          }
        }
        else
        {
          return Err( lookahead.error() );
        }

        // Optional comma handling
        if input.peek( syn::Token![,] )
        {
          input.parse::< syn::Token![,] >()?;
        }
      }

      Ok( result )
    }
  }

  // == Attribute properties

  /// Marker type for attribute property to specify whether to provide a sketch as a hint.
  /// Defaults to `false`, which means no hint is provided unless explicitly requested.
  #[ derive( Debug, Default, Clone, Copy ) ]
  pub struct AttributePropertyDebugMarker;

  impl AttributePropertyComponent for AttributePropertyDebugMarker
  {
    const KEYWORD : & 'static str = "debug";
  }

  /// Specifies whether to provide a sketch as a hint.
  /// Defaults to `false`, which means no hint is provided unless explicitly requested.
  pub type AttributePropertyDebug = AttributePropertySingletone< AttributePropertyDebugMarker >;

  // ==

  /// Marker type for attribute property to indicate whether a custom code should be generated.
  /// Defaults to `false`, meaning no custom code is generated unless explicitly requested.
  #[ derive( Debug, Default, Clone, Copy ) ]
  pub struct AttributePropertyCustomMarker;

  impl AttributePropertyComponent for AttributePropertyCustomMarker
  {
    const KEYWORD : & 'static str = "custom";
  }

  /// Indicates whether a custom code should be generated.
  /// Defaults to `false`, meaning no custom code is generated unless explicitly requested.
  pub type AttributePropertyCustom = AttributePropertyBoolean< AttributePropertyCustomMarker >;

  // == test code

  // Parse an attribute and construct a `ItemAttributes` instance.
  let input : syn::Attribute = syn::parse_quote!( #[ mutator( custom = true ) ] );
  let attrs : ItemAttributes = ItemAttributes::from_attrs( std::iter::once( & input ) ).unwrap();
  println!( "{:?}", attrs );

  // Test `AttributePropertyBoolean` functionality.
  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = AttributePropertyBoolean::default();
  assert_eq!( attr.internal(), false );
  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = true.into();
  assert_eq!( attr.internal(), true );
  let attr : AttributePropertyBoolean< AttributePropertyDebugMarker > = false.into();
  assert_eq!( attr.internal(), false );

}

尝试运行 cargo run --example macro_tools_attr_prop
查看代码.

将内容添加到您的项目中

cargo add proc_macro_tools

从仓库中尝试

git clone https://github.com/Wandalen/wTools
cd wTools
cd examples/macro_tools_trivial
cargo run

依赖项

~1.2–1.7MB
~33K SLoC