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
、rg
rgi
)=> 获取器与“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