17 个版本 (2 个稳定版)

1.0.1 2022 年 6 月 20 日
1.0.0 2022 年 5 月 26 日
0.3.0-alpha.12022 年 5 月 18 日
0.2.0-alpha.32022 年 5 月 18 日
0.1.0-alpha.172022 年 5 月 17 日

1720Rust 模式

Download history 13/week @ 2024-03-14 19/week @ 2024-03-21 13/week @ 2024-03-28 13/week @ 2024-04-04 20/week @ 2024-04-11 8/week @ 2024-04-18 1/week @ 2024-04-25 3/week @ 2024-05-16 2/week @ 2024-05-23 22/week @ 2024-05-30 22/week @ 2024-06-06 14/week @ 2024-06-13 8/week @ 2024-06-20 1/week @ 2024-06-27

每月 52 次下载
platz-sdk 中使用

MIT 许可证

22KB
243

kv-derive

使用 ToStringFromStr 从和到字符串键值向量派生结构体转换。

Crates.io Last commit GitHub Workflow Status License: MIT

示例

任何实现了 std::string::ToString 和/或 std::str::FromStr 支持的字段类型

#[派生(IntoVec)]

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Foo {
    bar: i32,
    qux: String,
}

let foo = Foo { bar: 42, qux: "qux".into() };
assert_eq!(foo.into_vec(), vec![
    ("bar".into(), "42".into()),
    ("qux".into(), "qux".into()),
]);

#[派生(FromIter)]

use kv_derive::prelude::*;
use kv_derive::FromIter;

#[derive(FromIter, Debug, PartialEq)]
struct Foo {
    #[kv(default())]
    bar: i32,
    
    #[kv(default())]
    qux: String,
}

let actual = Foo::from_iter(vec![("bar", "42"), ("qux", "quuux")]).unwrap();
let expected = Foo { bar: 42, qux: "quuux".into() };
assert_eq!(actual, expected);

#[derive(FromIter)] 需要您在每个字段上指定 #[kv(default())] 属性,因为它需要知道当输入中缺少键时该怎么做。

#[派生(FromMapping)]

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Foo {
    bar: i32,
    qux: String,
}

let mapping = HashMap::from([("bar", "42"), ("qux", "quuux")]);
let actual = Foo::from_mapping(&mapping).unwrap();
let expected = Foo { bar: 42, qux: "quuux".into() };
assert_eq!(actual, expected);

在这里 #[kv(default())] 不是必需的,缺少键将引发错误

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Foo {
    bar: i32,
    qux: String,
}

let mapping = HashMap::from([("bar", "42")]);
let actual = Foo::from_mapping(&mapping);
assert_eq!(actual, Err(kv_derive::error::Error::MissingKey("qux")));

自定义字段

可选字段

使用 #[kv(optional)] 宏期望字段被包裹在 std::option::Option 中,并跳过 None

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Foo {
    #[kv(optional)]
    bar: Option<i32>,
    
    #[kv(optional)]
    qux: Option<i32>,
}

let foo = Foo { bar: Some(42), qux: None };
assert_eq!(foo.into_vec(), vec![("bar".into(), "42".into())]);

请注意,这里需要同时使用 #[kv(optional)]std::option::Option 类型,因为技术上你可以省略 #[kv(optional)] 并在自定义的 Option<T> 上实现 std::string::ToString 来手动处理 None 值。

对于 #[derive(FromIter)] 这也确保了对 T 而不是对 Option<T> 调用了 std::str::FromStr

use kv_derive::prelude::*;
use kv_derive::FromIter;

#[derive(FromIter, Debug, PartialEq)]
struct Foo {
    #[kv(default(), optional)]
    bar: Option<i32>,
    
    #[kv(default(), optional)]
    qux: Option<i32>,
    
    #[kv(default(value = "Some(42)"), optional)]
    quux: Option<i32>,
}

let actual = Foo::from_iter(vec![("bar", "42")]).unwrap();
let expected = Foo { bar: Some(42), qux: None, quux: Some(42) };
assert_eq!(actual, expected);

默认值

#[kv(default())] 表明类型实现了 std::default::Default。但你也可以使用 #[kv(default(value = ""))] 指定一个自定义的默认值

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Foo {
    #[kv(default())]
    bar: i32,
    
    #[kv(default(value = "42"))]
    qux: i32,
    
    #[kv(default(), optional)]
    quux: Option<i32>,
    
    #[kv(default(value = "Some(100500)"), optional)]
    quuux: Option<i32>,
}

let foo = Foo::from_mapping(&HashMap::<String, String>::new()).unwrap();
assert_eq!(foo, Foo { bar: 0, qux: 42, quux: None, quuux: Some(100500) });

使用 #[kv(rename =)] 重新命名字段

使用指定的键而不是标识符

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Foo {
    #[kv(rename = "qux")]
    bar: i32,
}

let foo = Foo { bar: 42 };
assert_eq!(foo.into_vec(), vec![("qux".into(), "42".into())]);

转换到和从其他表示形式

以下是一个示例,说明如何使用 i32 表示布尔值

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::{IntoVec, FromIter, FromMapping};

#[derive(IntoVec, FromIter, FromMapping, PartialEq, Debug)]
struct Foo {
    #[kv(
        default(),
        collection,
        into_repr_with = "|value| value as i32",
        from_repr_with = "|value: i32| kv_derive::result::Result::Ok(value != 0)",
    )]
    bar: Vec<bool>,
}

assert_eq!(
    Foo { bar: vec![false, true] }.into_vec(),
    vec![("bar".into(), "0".into()), ("bar".into(), "1".into())],
);
assert_eq!(
    Foo::from_iter(vec![("bar".into(), "0".into()), ("bar".into(), "1".into())]).unwrap(), 
    Foo { bar: vec![false, true] },
);
assert_eq!(
    Foo::from_mapping(HashMap::from([("bar", "1")])).unwrap(),
    Foo { bar: vec![true] },
);

在这种情况下,std::string::ToStringstd::str::FromStri32 操作,而不是对 bool 操作。

集合字段

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Foo {
    #[kv(collection)]
    bar: Vec<i32>,
}

let foo = Foo { bar: vec![42, 100500] };
assert_eq!(foo.into_vec(), vec![
    ("bar".into(), "42".into()),
    ("bar".into(), "100500".into()),
]);
use kv_derive::prelude::*;
use kv_derive::FromIter;

#[derive(FromIter, Debug, PartialEq)]
struct Foo {
    #[kv(collection, default())]
    bar: Vec<i32>,
}

let actual = Foo::from_iter(vec![("bar", "42".into()), ("bar", "100500".into())]).unwrap();
let expected = Foo { bar: vec![42, 100500] };
assert_eq!(actual, expected);

关于 #[derive(FromMapping)]

HashMapBTreeMap 不能包含重复键。然而,为了保持一致性,单个值被正确转换为 std::vec::Vec

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Foo {
    #[kv(collection)]
    bar: Vec<i32>,
}

let map = HashMap::from([("bar", "42")]);
let actual = Foo::from_mapping(&map).unwrap();
let expected = Foo { bar: vec![42] };
assert_eq!(actual, expected);

扁平化

简单扁平化

可以“扁平化”内部结构

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Bar {
    qux: i32,
}

#[derive(IntoVec)]
struct Foo {
    #[kv(flatten())]
    bar: Bar,
}

let foo = Foo { bar: Bar { qux: 42 } };
assert_eq!(foo.into_vec(), vec![("qux".into(), "42".into())]);

注意,该宏不会检查外部和内部结构中可能存在的重复键。

对于具有扁平化字段的结构,无法推导出 FromIter。但是,对于 #[derive(FromMapping)] 是有效的。

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Inner {
    bar: i32,
}

#[derive(FromMapping, Debug, PartialEq)]
struct Outer {
    #[kv(flatten())]
    inner: Inner,
}

let map = HashMap::from([("bar", "42")]);
let actual = Outer::from_mapping(&map).unwrap();
let expected = Outer { inner: Inner { bar: 42 } };
assert_eq!(actual, expected);

前缀扁平化

也可以为所有内部字段添加相同的前缀

use kv_derive::prelude::*;
use kv_derive::IntoVec;

#[derive(IntoVec)]
struct Bar {
    qux: i32,
}

#[derive(IntoVec)]
struct Foo {
    #[kv(flatten(prefix = "bar::"))]
    bar: Bar,
}

let foo = Foo { bar: Bar { qux: 42 } };
assert_eq!(foo.into_vec(), vec![("bar::qux".into(), "42".into())]);

然后返回

use std::collections::HashMap;

use kv_derive::prelude::*;
use kv_derive::FromMapping;

#[derive(FromMapping, Debug, PartialEq)]
struct Inner {
    bar: i32,
}

#[derive(FromMapping, Debug, PartialEq)]
struct Outer {
    #[kv(flatten(prefix = "inner::"))]
    inner: Inner,
}

let map = HashMap::from([("inner::bar", "42")]);
let actual = Outer::from_mapping(&map).unwrap();
let expected = Outer { inner: Inner { bar: 42 } };
assert_eq!(actual, expected);

依赖关系

~0.7–1.1MB
~26K SLoC