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

MIT 许可证

45KB
1K SLoC

githubcrates-iodocs-rs

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

带有printfilefile-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
 }
}

参考

用法

  1. 准备中,在项目Cargo.toml文件的[dependencies]部分中添加tia="*"。(或者我喜欢通过cargo-edit运行cargo add tia
    • 注意:file-pretty功能是一个很好的调试工具。如果您想了解,请阅读底部部分“功能”。
  2. 使用
    1. 在您的结构体|枚举|联合的顶部编写#[derive(Tia)] proc-macro。
    2. #[derive(Tia)](结构体|枚举|联合级别的设置)下面或特定字段的顶部编写#[tia(...)] proc-macro。
      • ...在下一节“tia指令”中解释。

tia指令

tia的proc-macro可以解析以下模式

#[tia( $tia_directive_0, $tia_directive_1,$tia_directive_2, ... )]

以及$tia_directive模式

  1. 访问器指令
    • 访问器
      • 类似于获取器的访问器
        • gm => ([g]et [m]ove) ⚠ 移动 ⚠ 模式,不建议随意使用;例如 fn (self) -> i32 { self.value }
        • g => ([g]et) 对于可复制的值,用于原始类型,如 u8f32 或实现了 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
    • 命名策略
      • 默认(例如 grg 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
  2. 特性指令
    • 默认(没有特性指令)=> 将为字段生成 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
  • 此文件不用于构建,但如果您想用眼睛检查生成的代码,那么它很有帮助。

filefile-pretty 之间的区别是什么

  • file 将输出原始生成的代码。它非常紧凑。
  • file-pretty 将输出原始生成的代码,然后自动应用 rustfmt
    • 注意:此特性需要在您的开发环境中安装 rustfmt 命令。(它不是 lib crate 依赖。)

include|include-pretty|include-force

使用示例

[dependencies]
tia={ version="*", features=["include-pretty"] }

tia 将会是

  1. 如果不存在则生成代码。
  2. 生成如下宏 include!(...),例如 include!("src/.tia/MyStruct")

includeinclude-prettyinclude-force 的区别是什么?

  • include 如果生成的代码未找到,将生成 (=file) 代码。
  • include-pretty 如果生成的代码未找到,将生成并格式化 (=file-pretty)。
  • include-force 如果生成的代码未找到,将不会生成,可能会因为错误而停止构建。

注意

tia 提供了一个有用的语法糖,如果你需要实现许多类似接口的规范,这会很有帮助。例如,主要为面向对象范式的语言设计的内容,如 C#、Java、C++,或者基于 UML 的复杂数据定义,如 XMLSchema。但,它只是一个语法糖。请不要过度使用 tia

许可证

贡献者

谢谢!😍

作者

依赖项

~1.5MB
~36K SLoC