#config #serde #serialization #database #struct-fields #field-name

perstruct

用于为结构体添加持久后端的实用宏

1 个不稳定版本

0.1.0 2024年1月31日

#959编码

MIT 许可证

8KB

Perstruct

perstruct 是一个crate,它提供了一个宏,将Rust结构体转换为设置结构体。它简化了将结构体字段作为键值对加载和保存的过程。这对于应用程序设置或配置非常有用,其中每个设置都可以单独更新和持久化。

这种方法相对于整个结构体的序列化和反序列化具有显著优势,特别是在应用程序配置或用户偏好需要灵活性和对变化的弹性时。

字段值以json格式序列化。未来可能会支持其他格式。

以下是一个示例,演示如何使用 perstruct 宏来管理应用程序中的用户引用

use perstruct::perstruct;

// Define a struct for user preferences.
// Apply the `perstruct` macro to enable key/value storage capabilities.
#[perstruct]
struct UserPreferences {
    // Define user preferences with custom keys for storage.
    pub ui_theme: UiTheme, // key is the same as the field name by default

    #[perstruct(key = "notifications_enabled")] // key can be overridden
    #[perstruct(default = true)] // default value can be specified
    pub enable_notifications: bool,

    // Use `perstruct(default_fn = "default_language")` to specify a function that returns  default value.
    #[perstruct(default_fn = "default_language")]
    pub language: String,

    // Use `perstruct(skip)` to exclude fields from being persisted.
    #[perstruct(skip)]
    pub cache: std::collections::HashMap<String, String>
}

#[derive(serde_derive::Serialize, serde_derive::Deserialize, Default, Debug, PartialEq)]
pub enum UiTheme {
    #[default]
    Dark,
    Light
}

fn default_language() -> String { "en".to_string() }

// Simulate loading preferences from a key-value store (like a database or config file).
// These might be values previously saved by the user.
let kv_store_simulation = vec![
    ("ui_theme", "\"Dark\""),
    ("notifications_enabled", "true"),
].into_iter().collect();
let result = UserPreferences::from_map(&kv_store_simulation);

// Access the loaded preferences, handle deserialization errors or unknown fields.
let mut preferences = result.value;
assert_eq!(preferences.ui_theme(), &UiTheme::Dark);
assert_eq!(preferences.enable_notifications(), true);
assert_eq!(preferences.language(), "en");
assert_eq!(result.deserialization_errors, vec![]);
assert!(result.unknown_fields.is_empty());

// Modify preferences using the auto-generated setters.
preferences.set_ui_theme(UiTheme::Light);
preferences.set_enable_notifications(false);

// Retrieve changes (dirty fields) to persist them.
let mut changes = preferences.perstruct_get_changes().unwrap();
changes.sort_by_key(|(k, _)| *k);
assert_eq!(
    changes,
    vec![
        ("language", "\"en\"".to_string()), // language was not loaded but defaulted to "en", so it should be included in the changes
        ("notifications_enabled", "false".to_string()),
        ("ui_theme", "\"Light\"".to_string()),
    ]
);

// Simulate saving the changes to the kv store.
// ...

// Mark changes as saved using `perstruct_saved`.
preferences.perstruct_saved();

// Verify that there are no unsaved changes.
assert_eq!(preferences.perstruct_get_changes().unwrap(), vec![]);

限制

perstruct 宏只能应用于满足以下要求的结构体

  • 所有非跳过的字段类型都必须实现 serde::Serializeserde::Deserialize
  • 所有非跳过的字段都必须实现 Default 或使用 perstruct(default = ...) 或 #[perstruct(default_fn = "...")] 指定默认值。

依赖项

~0.7–1.6MB
~34K SLoC