#merge #struct #proc-macro #library

废弃 struct-merge

为各种结构体合并策略自动生成代码

2个版本

0.1.1 2022年4月7日
0.1.0 2021年12月30日

#158 in #merge

每月 28 次下载
用于 zero4rs

MIT 许可证

15KB
104

Struct-Merge

GitHub Actions Workflow Crates.io License: MIT Downloads

!!! 已废弃 !!!

此项目已被 inter-struct 取代。

请考虑切换到该库,因为这个库不会得到任何更新。

!!! 已废弃 !!!

生成合并各种结构的代码。

此crate提供两个proc-macros来生成两个非常相似的特质。

/// Merge another struct into Self whilst consuming it.
/// 
/// The other trait is named `StructMergeRef` and merges other structs by reference.
pub trait StructMerge<Src> {
    /// Merge the given struct into self.
    fn merge(&mut self, src: Src);

    /// Nearly the same as `merge`, but any `Self::Option<T>` fields will only get merged, if the
    /// value of the field is `None`.
    fn merge_soft(&mut self, src: Src);
}

在使用此crate之前,请务必阅读 已知注意事项 部分!

示例

此示例展示了 struct_merge 宏的简单用法。

structs.rs:此示例中使用的结构体。

use struct_merge::struct_merge;

/// The target struct we'll merge into.
pub struct Target {
    pub normal: String,
    pub optional: Option<String>,
    /// This field won't be touched as the macro cannot find a
    /// respective `ignored` field in the `Mixed` struct.
    pub ignored: Option<String>,
}

/// A struct with both an identical and an optional field type.
/// Note that the path to `Target` must always a fully qualifying path.
#[struct_merge(crate::structs::Target)]
pub struct Mixed {
    pub normal: String,
    pub optional: Option<Option<String>>,
}

lib.rs

use struct_merge::prelude::*;

mod structs;
use structs::{Target, Mixed};

fn main() {
    let mut target = Target {
        normal: "target".to_string(),
        optional: Some("target".to_string()),
        ignored: "target".to_string(),
    };

    let mixed = Mixed {
        /// Has the same type as Target::normal
        normal: "mixed".to_string(),
        /// Wraps Target::optional in an Option
        optional: Some(Some("mixed".to_string())),
    };

    // Merge the `Mixed` struct into target.
    target.merge(mixed);
    // You can also call this:
    // mixed.merge_into(target);
    assert_eq!(target.normal, "mixed".to_string());
    assert_eq!(target.optional, Some("mixed".to_string()));
    assert_eq!(target.ignored, "target".to_string());
}

合并行为

以下将解释目标结构体单个字段的合并行为。目标字段的名称为 test

mergemerge_ref

相同类型

struct Src {
    test: T
}
struct Target {
    test: T
}

这会将 src.test 合并到 target.test
目标.test=src.test

目标为可选

struct Src {
    test: T
}
struct Target {
    test: Option<T>
}

这会将 src.test 包装成 Option 并合并到 target.test
目标.test= Some(src.test);

源为可选

struct Src {
    test: Option<T>
}
struct Target {
    test: T
}

如果 src.testSome,则只将 src.test 合并到 target.test

if let Some(value) = src.test {
    target.test = value;
}

merge_softmerge_ref_soft

如果 target.test 不是可选的

只要目标字段不是可选的,就不会被修改!

目标为可选

struct Src {
    test: T
}
struct Target {
    test: Option<T>
}

这会将 src.test 包装成 Option 并合并到 target.test,但只有当 target.testNone

if target.test.is_none() {
    target.test = Some(src.test);
}

两者都是可选的

struct Src {
    test: Option<T>
}
struct Target {
    test: Option<T>
}

此功能只会将 src.test 合并到 target.test,前提是 target.testNone: \

if target.test.is_none() {
    target.test = src.test;
}

已知问题

模块/类型解析

在过程宏阶段,没有简单的方法来进行路径或类型解析。
因此,这个软件包可能无法与您项目的结构兼容。

然而,由于我们创建的是安全和有效的Rust代码,编译器将在出现任何问题时抛出错误。

在使用正常的 merge 函数时,最坏的情况是这个软件包无法编译。

然而,当使用类型别名等隐藏类型时,merge_soft 函数无法正确检测 Option,即使值已经是 Some 也可能会合并值!

尚未解决的问题

这些问题可能可以解决,但它们不是显而易见的。

  • 位于软件包根目录的Struct。例如 lib.rs
  • 位于集成测试中的Struct。
  • 位于文件中(可能嵌套或交替的) mod {} 块中的Struct。
  • 源根目录不是 src。我们可能需要检查环境并可能解析 Cargo.toml
  • 使用不同标记但具有相同类型的不同泛型别名。例如,Box<dyn T>Box<dyn S>,但 ST 都具有 Clone 特性限制。
  • 非公共Struct。即不完全内部可见的Struct。这将导致编译器错误,但在运行此宏时不会被捕获。这可能是不切实际的?

无法解决的问题

这些问题要么无法解决,要么非常不切实际。例如,解析所有文件并对给定软件包进行完全类型解析可能是不切实际的。这将是编译器在后续阶段的工作。

  • 由其他宏修改或生成的Struct。
  • 类型别名。例如,type test = Option<String> 不会被检测为Option。当前对 Option 字段的检查是对 Option 标记的逐字检查。

依赖关系

~1.5MB
~37K SLoC