4个稳定版本
| 1.0.3 | 2022年5月5日 |
|---|---|
| 1.0.2 | 2022年5月2日 |
| 1.0.1 | 2021年1月25日 |
| 1.0.0 | 2020年8月16日 |
#14 在 #string-pattern
33 每月下载量
用于 2 crates
45KB
1K SLoC
tia; 特征, 实现, 访问器 | 自动
这是针对特征、实现访问器模式的语法糖proc-macro包。 tia为任何结构体|枚举|联合体生成对应的访问器实现。
功能
tia可以自动生成实现代码。- 目标类型:
结构体|枚举|联合体。 - 设置级别:{所有字段} | {每个字段}。
特征支持:可以与多个特征一起生成。(参见下面的示例-3。)- 生成访问器:类似于获取器的 {move,
Copy,&,&mut},类似于设置器的 {move,Copy,Clone,Into}.(另见示例-1,2和参考/tia指令部分。) - 有用的+功能:{
print,file-pretty,include,disable}.(另见参考/功能部分。) - 命名模式:{ 前缀,后缀,全名 }.(另见参考/tia指令部分。)
示例
示例-1;tia的介绍
这是一个最小化、非常简单的版本。没有trait复杂。
use tia::Tia; // 1. use
#[derive(Tia)] // 2. derive
#[tia(rg)] // 3. tia directives
struct MyStruct
{
foo: i32,
bar: String
}
fn main()
{
let mys = MyStruct{ foo: 123, bar: "Hello".into() };
let foo = mys.get_foo(); // <-- 4. !! generated by tia automatically !!
let bar = mys.get_bar(); // <-- 5. !! generated by tia automatically !!
println!("foo={} bar={}", foo, bar );
}
cargo run,然后你会得到以下输出
foo=123 bar=Hello
- (1,2)正在准备。
- (3)是结构级别的tia指令。
- (4,5)是
tia自动生成的访问器。
自动生成的代码是
impl MyStruct
{
pub fn get_foo(&self) -> &i32
{
&self.foo
}
pub fn get_bar(&self) -> &String
{
&self.bar
}
}
如果使用file-pretty功能在Cargo.toml中,则可以输出到src/.tia/MyStruct
[dependencies]
tia={ version="*", features=["file-pretty"] }
示例-2;一点复杂/实用的用法
use tia::Tia; // use
#[derive(Tia, Debug, Default)] // derive
#[tia(rg, s)] // <-- tia directives, for all fields
struct MyStruct
{
#[tia(rmg)] // <-- #[tia(rg, s)] + #[tia(rmg)] => #[tia(rmg, s)]
foo: i32,
#[tia(rsi)] // <-- #[tia(rg, s)] + #[tia(rsi)] => #[tia(rg, rsi)]
bar: String,
baz: f64, // <-- #[tia(rg, s)]
#[tia(g)] // <-- #[tia(rg, s)] + #[tia(g)] => #[tia(g, s)] !! NOTE: Could be use for Copy-ables such as u8, but g pattern could not be use non-Copy-ables such as Vec<u8>
brabrabra: u8,
#[tia(gm)] // <-- #[tia(rg, s)] + #[tia(g)] => #[tia(gm, s)] !! WARNING: Could be move any types, but gm pattern will drop self
hogefuga: Vec<u8>
}
fn main()
{
let mut mys = MyStruct::default();
// rmg; reference-mut-getter
// with per-field level directive overwriting.
{
let foo = mys.get_foo(); // <-- &mut i32
*foo = 42;
dbg!(&foo);
dbg!(&mys);
}
// rsi: reference-setter-into
// with per-field level directive overwriting.
{
let a: &str = "Hello, ";
let b: String = String::from("tia.");
let c: &String = &b;
mys.set_bar(a); // &str
println!("a: mys.bar = {}", mys.get_bar());
mys.set_bar(b.clone()); // String; This effect move, thus the example is a -> c -> b
println!("b: mys.bar = {}", mys.get_bar());
mys.set_bar(c); // &String
println!("c: mys.bar = {}", mys.get_bar());
}
let x = mys.get_brabrabra(); // it will be Copy, mys will live
dbg!(x, &mys);
let y = mys.get_hogefuga(); // gm, get-move accessor will be drop mys
dbg!(y);
// mys was dropped, it could not be compile.
//dbg!(mys)
}
cargorun:
[src\main.rs:30] &foo = 42
[src\main.rs:31] &mys = MyStruct {
foo: 42,
bar: "",
baz: 0.0,
brabrabra: 0,
hogefuga: [],
}
a: mys.bar = Hello,
b: mys.bar = tia.
c: mys.bar = tia.
[src\main.rs:52] x = 0
[src\main.rs:52] &mys = MyStruct {
foo: 42,
bar: "tia.",
baz: 0.0,
brabrabra: 0,
hogefuga: [],
}
[src\main.rs:55] y = []
示例-3;trait用法
use tia::Tia;
trait FooGettable<T>{ fn get_foo(&self) -> T; }
trait Fruit{ fn get_bar(&self) -> &String; }
trait Sushi{ fn tuna(&self) -> u8; fn avocado(&mut self, v: u8); }
//include!(".tia/MyStruct.rs");
#[derive(Tia, Debug, Default)] // derive
struct MyStruct
{
#[tia(s, "FooGettable<i32>", g)]
foo: i32,
#[tia("Fruit",rg,"",rsi)]
bar: String,
#[tia("Sushi",g*="tuna",s*="avocado")] // <- `g` and `s`: Sushi trait
baz: u8
}
fn main()
{
let mut mys = MyStruct::default();
mys.set_foo(123);
mys.set_bar("meow");
let foo_gettable = &mys as &dyn FooGettable<i32>;
let fruit = &mys as &dyn Fruit;
println!("{}, {}", foo_gettable.get_foo(), fruit.get_bar() );
let sushi = &mut mys as &mut dyn Sushi;
sushi.avocado(32);
println!("{}", sushi.tuna());
}
然后运行cargo run
123, meow
32
带有print、file或file-pretty功能的生成代码
impl FooGettable<i32> for MyStruct
{
fn get_foo(&self) -> i32
{
self.foo
}
}
impl MyStruct
{
pub fn set_bar<T: Into<String>>(&mut self, v: T)
{
self.bar = v.into();
}
pub fn set_foo(&mut self, v: i32)
{
self.foo = v;
}
}
impl Fruit for MyStruct
{
fn get_bar(&self) -> &String
{
&self.bar
}
}
impl Sushi for MyStruct
{
fn avocado(&mut self, v: u8)
{
self.baz = v;
}
fn tuna(&self) -> u8
{
self.baz
}
}
参考
用法
- 准备中,在项目
Cargo.toml文件的[dependencies]部分中添加tia="*"。(或者我喜欢通过cargo-edit运行cargo add tia)- 注意:
file-pretty功能是一个很好的调试工具。如果您想了解,请阅读底部部分“功能”。
- 注意:
- 使用
- 在您的结构体|枚举|联合的顶部编写
#[derive(Tia)]proc-macro。 - 在
#[derive(Tia)](结构体|枚举|联合级别的设置)下面或特定字段的顶部编写#[tia(...)]proc-macro。...在下一节“tia指令”中解释。
- 在您的结构体|枚举|联合的顶部编写
tia指令
tia的proc-macro可以解析以下模式
#[tia( $tia_directive_0, $tia_directive_1,$tia_directive_2, ... )]
以及$tia_directive模式
- 访问器指令
- 访问器
- 类似于获取器的访问器
gm=> ([g]et [m]ove) ⚠ 移动 ⚠ 模式,不建议随意使用;例如fn (self) -> i32 { self.value }g=> ([g]et) 对于可复制的值,用于原始类型,如u8、f32或实现了impl Copy的类型;例如return &self.value。rg=> ([r]eference [g]et) 返回引用&模式。在大多数情况下可以随意使用。rmg=> ([r][m]ut [g]et) 返回可变引用&mut。有时很有用,但有时也很复杂和困难。
- 设置器加速
s=> ([s]et) 原始值移动模式。rs=> ([r]eference [s]et) 引用&模式,用于可复制的类型。rsc=> ([r]eference [s]et [c]lone)Clone模式,用于可复制的类型,如String。此模式需要相同的输入类型。rsi=> ([r]eference [s]et [i]nto)Into模式,用于可转换为String的类型,如String。此模式可以进行类型转换。例如,&str|String|&String等更多类型通过此模式输入到String。
- 类似于获取器的访问器
- 命名策略
- 默认(例如
g、rgrgi)=> 获取器与“get”前缀相同,设置器与“set”前缀相同。 g="my_awesome_prefix"=> 使用专用的前缀字符串模式作为前缀。它将为fn my_awesome_prefix_xxxx生成xxxx字段符号。g+="my_awesome_suffix"=> 使用专用的后缀字符串模式作为后缀。它将为fn xxxx_my_awesome_suffix生成xxxx字段符号。g*="my_awesome_fullname"=> 全名模式。它将为一个字段生成fn my_awesome_fullname。
- 默认(例如
- 访问器
- 特性指令
- 默认(没有特性指令)=> 将为字段生成
impl for MyStruct代码。 "TraitSymbol"=> 将为出现在此指令之后的字段生成impl TraitSymbol for MyStruct代码。""=> 将为出现在此指令之后的字段生成impl for MyStruct代码。
- 默认(没有特性指令)=> 将为字段生成
特性
禁用
使用示例
[dependencies]
tia={ version="*", features=["disable"] }
tia将不输出任何内容。- 但是
tia并没有被移除,因此它允许#[derive(Tia)]和#[tia(...)]proc-macros 不产生任何效果。
打印
使用示例
[dependencies]
tia={ version="*", features=["print"] }
tia将将生成的代码输出到 STDERR。- 但是它对人类眼睛来说很难阅读,因此
file-pretty对人类眼睛来说更好。
- 但是它对人类眼睛来说很难阅读,因此
file|file-pretty
使用示例
[dependencies]
tia={ version="*", features=["file-pretty"] }
tia将将生成的代码输出/更新到文件,例如src/.tia/MyStruct.rs。- 此文件不用于构建,但如果您想用眼睛检查生成的代码,那么它很有帮助。
file 和 file-pretty 之间的区别是什么
file将输出原始生成的代码。它非常紧凑。file-pretty将输出原始生成的代码,然后自动应用rustfmt。- 注意:此特性需要在您的开发环境中安装
rustfmt命令。(它不是 lib crate 依赖。)
- 注意:此特性需要在您的开发环境中安装
include|include-pretty|include-force
使用示例
[dependencies]
tia={ version="*", features=["include-pretty"] }
tia 将会是
- 如果不存在则生成代码。
- 生成如下宏
include!(...),例如include!("src/.tia/MyStruct")。
include、include-pretty 和 include-force 的区别是什么?
include如果生成的代码未找到,将生成 (=file) 代码。include-pretty如果生成的代码未找到,将生成并格式化 (=file-pretty)。include-force如果生成的代码未找到,将不会生成,可能会因为错误而停止构建。
注意
tia 提供了一个有用的语法糖,如果你需要实现许多类似接口的规范,这会很有帮助。例如,主要为面向对象范式的语言设计的内容,如 C#、Java、C++,或者基于 UML 的复杂数据定义,如 XMLSchema。但,它只是一个语法糖。请不要过度使用 tia。
许可证
贡献者
谢谢!😍
作者
- USAGI.NETWORK / Usagi Ito https://github.com/usagi/
依赖项
~1.5MB
~36K SLoC