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 在 开发工具 中
3,072 每月下载次数
在 63 个crate中(直接使用7个)
340KB
6K SLoC
模块 :: proc_macro_tools
过程宏编写工具。
示例:简单示例
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
解析布尔属性以及 AttributePropertyComponent 和
AttributeComponent
特性的作用。还使用了 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