#枚举 #哈希表 #关联 #数据 #获取器-设置器 #生成 #转换

enum2map

一个将具有关联数据的枚举转换为HashMap的小宏

2个版本

0.1.1 2023年9月3日
0.1.0 2023年8月30日

#877 in 过程宏

Apache-2.0

13KB
150

枚举到HashMap

github crates.io

这个crate提供了一种将具有关联数据的枚举转换为hashmap的方法。这个想法是在我开发我的UI库时产生的。

我希望以非常灵活的方式实现样式。本质上,除非它被定义,否则每个样式属性都是可选的。如果使用结构体来实现,这将非常麻烦,因为所有属性都必须是Option类型。相反,我只是简单地创建了一个带有关联数据的枚举,并将其转换为映射。我以后可以很容易地通过迭代器访问每个值,并按任何方式对其进行操作。

[dependencies]
enum2map = "0.1"

示例

使用这个crate的方式非常简单。定义一个带有关联数据的枚举并派生Enum2Map。

#[derive(Debug, PartialEq, Eq, Clone, Enum2Map)]
pub enum TestValue {
    Padding(usize),
    Margin(String),
}

然后您可以使用泛型获取器和设置器或为新生成的枚举中的每个属性使用获取器和设置器。

pub enum TestValueKey {
    Padding,
    Margin,
}

let mut map = TestValueMap::new();

map.get(TestValueKey::Margin);
map.get(TestValueKey::Padding);

map.get_or_default(TestValueKey::Padding);
map.get_or_default(TestValueKey::Padding);

map.set(TestValue::Padding(10));
map.set(TestValue::Margin("string test".to_string()));

以及您枚举中的每个值的获取器和设置器

map.set_padding(50);
map.set_margin("another test".to_string());

map.get_padding();
map.get_margin();

工作原理

派生过程将首先从提供的枚举中生成键

pub enum TestValueKey {
    Padding,
    Margin,
}

然后它将生成一个具有您在示例中看到的泛型获取器和设置器的结构体。它还将为您的枚举中的每个值生成获取器和设置器。API被编写得尽可能接近HashMap API。

pub struct TestValueMap {
    pub values: std::collections::HashMap<TestValueKey, TestValue>,
}
impl TestValueMap {
    pub fn new() -> Self {
        Self {
            values: std::collections::HashMap::new(),
        }
    }
    pub fn insert(&mut self, value: TestValue) -> Option<TestValue> {
        match value {
            TestValue::Padding(val) => {
                self.values.insert(TestValueKey::Padding, TestValue::Padding(val))
            }
            TestValue::Margin(val) => {
                self.values.insert(TestValueKey::Margin, TestValue::Margin(val))
            }
        }
    }
    pub fn get(&self, key: TestValueKey) -> Option<&TestValue> {
        self.values.get(&key)
    }
    pub fn get_or_default(&self, key: TestValueKey) -> TestValue {
        match self.values.get(&key) {
            Some(value) => value.clone(),
            None => {
                match key {
                    TestValueKey::Padding => TestValue::Padding(Default::default()),
                    TestValueKey::Margin => TestValue::Margin(Default::default()),
                }
            }
        }
    }
    pub fn set(&mut self, value: TestValue) -> Option<TestValue> {
        match value {
            TestValue::Padding(val) => {
                self.values.insert(TestValueKey::Padding, TestValue::Padding(val))
            }
            TestValue::Margin(val) => {
                self.values.insert(TestValueKey::Margin, TestValue::Margin(val))
            }
        }
    }
    pub fn get_padding(&self) -> usize {
        match self.values.get(&TestValueKey::Padding) {
            Some(TestValue::Padding(value)) => value.clone(),
            None => Default::default(),
            _ => {
                ::core::panicking::panic_fmt(
                    format_args!(
                        "Unexpected condition: Didn\'t find type {0} for {1}",
                        "usize",
                        "Padding",
                    ),
                );
            }
        }
    }
    pub fn get_margin(&self) -> String {
        match self.values.get(&TestValueKey::Margin) {
            Some(TestValue::Margin(value)) => value.clone(),
            None => Default::default(),
            _ => {
                ::core::panicking::panic_fmt(
                    format_args!(
                        "Unexpected condition: Didn\'t find type {0} for {1}",
                        "String",
                        "Margin",
                    ),
                );
            }
        }
    }
    pub fn set_padding(&mut self, val: usize) -> Option<usize> {
        if let Some(TestValue::Padding(old_value))
            = self.values.insert(TestValueKey::Padding, TestValue::Padding(val))
        {
            return Some(old_value);
        }
        None
    }
    pub fn set_margin(&mut self, val: String) -> Option<String> {
        if let Some(TestValue::Margin(old_value))
            = self.values.insert(TestValueKey::Margin, TestValue::Margin(val))
        {
            return Some(old_value);
        }
        None
    }
}

未来工作

  • 更好的错误处理。
  • 处理枚举没有关联数据的情况。
  • 更好的文档。

依赖项

~1.5MB
~36K SLoC