#mapper #model #entity #from #into

no-std model-mapper

派生宏,用于在类型之间映射

8个不稳定版本 (3个破坏性更新)

0.4.2 2024年7月29日
0.4.1 2024年7月28日
0.3.1 2024年7月13日
0.2.2 2024年7月3日
0.1.0 2023年10月12日

250Rust模式

Download history 3/week @ 2024-06-03 134/week @ 2024-07-01 187/week @ 2024-07-08 138/week @ 2024-07-15 107/week @ 2024-07-22 197/week @ 2024-07-29

每月634次下载

Apache-2.0

38KB
640

模型映射器

此库提供宏来实现类型之间的转换函数(枚举和结构体),无需样板代码。

它还提供了一个包含一些类型转换实用工具的 with 模块,这些工具不实现 Into 特性。

只要您不使用 with 模块(禁用默认功能)并且不派生 try_intotry_form,此库就可以在 #![no_std] 链接中使用。

示例

此库最常见的使用案例是在服务和面向外部的模型或DTO之间映射领域实体。

#[derive(Mapper)]
#[mapper(from, ty = Entity)]
pub struct Model {
    id: i64,
    name: String,
}

上面的宏展开将生成类似以下内容

impl From<Entity> for Model {
    fn from(Entity { id, name }: Entity) -> Self {
        Self {
            id: Into::into(id),
            name: Into::into(name),
        }
    }
}

因为类型并不总是完美匹配,您可以在运行时提供额外的字段,但这将牺牲使用 From 特性的能力

pub mod service {
    pub struct UpdateUserInput {
        pub user_id: i64,
        pub name: Option<String>,
        pub surname: Option<String>,
    }
}

#[derive(Mapper)]
#[mapper(
    into(custom = "into_update_user"),
    ty = service::UpdateUserInput,
    add(field = user_id, ty = i64),
    add(field = surname, default(value = None))
)]
pub struct UpdateProfileRequest {
    pub name: String,
}

将生成类似以下内容

impl UpdateProfileRequest {
    /// Builds a new [service::UpdateUserInput] from a [UpdateProfileRequest]
    pub fn into_update_user(self, user_id: i64) -> service::UpdateUserInput {
        let UpdateProfileRequest { name } = self;
        service::UpdateUserInput {
            user_id,
            surname: None,
            name: Into::into(name),
        }
    }
}

其他高级用例可以在 示例文件夹 中找到。

用法

在类型级别需要 mapper 属性,在字段或变体级别是可选的。

以下属性可用。

  • 类型级别属性

    • ty = PathType (必填):要派生转换的另一个类型
    • from (可选):是否为自身派生 From 的另一个类型
      • custom (可选):派生自定义函数而不是特型
      • custom = from_other (可选):派生自定义函数而不是特型,使用给定的名称
    • into (可选):是否为另一个类型派生 From 自身
      • custom (可选):派生自定义函数而不是特型
      • custom = from_other (可选):派生自定义函数而不是特型,使用给定的名称
    • try_from (可选):是否为自身派生 TryFrom 的另一个类型
      • custom (可选):派生自定义函数而不是特型
      • custom = from_other (可选):派生自定义函数而不是特型,使用给定的名称
    • try_into (可选):是否为其他类型派生 TryFrom self
      • custom (可选):派生自定义函数而不是特型
      • custom = from_other (可选):派生自定义函数而不是特型,使用给定的名称
    • add (可选,多个):其他类型有而本类型没有的额外字段(对于具有命名字段的 struct)或变体(对于 enum)¹
      • field = other_field (必填):字段或变体名称
      • ty = bool (可选):字段类型,对于没有提供默认值的 intotry_into 是必填的
      • default (可选):将使用 Default::default() 来填充字段或变体(对于 enum,无论是否有值都是必填的)
        • value = true (可选):将使用给定的表达式来填充字段或变体
    • ignore_extra (可选):是否忽略其他类型的所有额外字段(对于 struct)或变体(对于 enum)²
  • 变体级别的属性

    • rename = OtherVariant (可选):在另一个 enum 上重命名此变体
    • add (可选,多个):变体的额外字段,其他类型的变体有而本类型没有的 ¹
      • field = other_field (必填):字段名称
      • ty = bool (可选):字段类型,对于没有提供默认值的 intotry_into 是必填的
      • default (可选):将使用 Default::default() 来填充字段或变体
        • value = true (可选):将使用给定的表达式来填充字段或变体
    • skip (可选):是否跳过此变体,因为其他 enum 没有它
      • default (必填):将使用 Default::default() 来填充字段或变体
        • value = get_default_value() (可选):将使用给定的表达式来填充字段或变体
    • ignore_extra (可选):是否忽略其他变体的所有额外字段(仅对 fromtry_from 有效)²
  • 字段级别的属性

    • rename = other_name (可选):在另一个类型上重命名此字段
    • skip (可选):是否跳过此字段,因为其他类型没有它
      • default (可选):将使用 Default::default() 来填充字段或变体
        • value = get_default_value() (可选):将使用给定的表达式来填充字段或变体
    • with = mod::my_function (可选):如果字段类型没有实现 IntoTryInto 的其他类型,这个属性允许你通过提供转换函数来自定义行为
    • into_with = mod::my_function (可选):与上面相同,但仅适用于 intotry_into 派生
    • from_with = mod::my_function (可选):与上面相同,但仅适用于 fromtry_from 派生

¹ 当提供没有默认值的额外字段时,FromTryFrom 特性无法被推导,因此需要使用自定义函数。当推导 intotry_into 时,必须提供 ty

² 忽略字段或变体时,可能需要枚举或结构体实现 Default,以便正确填充它。

多重推导

当为单个类型推导转换时,可以直接设置属性

#[mapper(from, into, ty = OtherType, add(field = field_1, default), add(field = field_2, default))]

但也可以通过在 derive 属性上包装属性来为多个类型推导转换

#[mapper(derive(try_into, ty = OtherType, add(field = field_1, default)))]
#[mapper(derive(from, ty = YetAnotherType))]

如果涉及多个转换,也可以在 when 属性中包装变体级和字段级属性,并且必须设置它们所引用的 ty

#[mapper(when(ty = OtherType, with = TryIntoMapper::try_map_removing_option))]
#[mapper(when(ty = YetAnotherType, skip(default)))]

依赖关系

~0.7–1.7MB
~35K SLoC