4 个版本

0.1.3 2023 年 12 月 29 日
0.1.2 2023 年 12 月 28 日
0.1.1 2023 年 12 月 27 日
0.1.0 2023 年 12 月 27 日

#passed 中排名 26

Download history 71/week @ 2024-04-21 147/week @ 2024-04-28 42/week @ 2024-05-05 40/week @ 2024-05-12 19/week @ 2024-05-19 26/week @ 2024-05-26 21/week @ 2024-06-02 23/week @ 2024-06-09 55/week @ 2024-06-16 56/week @ 2024-06-23 121/week @ 2024-06-30 71/week @ 2024-07-07 13/week @ 2024-07-14 17/week @ 2024-07-21

每月下载 222
用于 yacm

Apache-2.0 协议

25KB
336 行代码(不含注释)

yacm

Yacm 是又一个配置宏。

为什么?

鉴于“另一个”项目的悠久历史,甚至连它的名字都不是原创的。然而,我在配置宏中找不到我想要的东西,而且我已经厌倦了重写相同的无聊代码来加载结构,然后将其传递给需要配置的东西。

项目目标

  1. 简化配置代码
  2. 能够从异步源(例如 AWS SSM 参数)加载配置
  3. 配置错误时快速失败
  4. 使配置加载方式高度可配置

总体思路

#[derive(Yacm)]
#[yacm(prefix = "derived", name_provider = yacm::name_provider::screaming_snake_case, default_loader = yacm::env::read_env)]
struct Test {
    #[yacm(
        name = "TEST_TEST_ONE",
        default = 142u32,
        validator = less_than_100
    )]
    pub test_1: u32,
    #[yacm(
        name = Test::load_test_1().await?,
        loader = yacm::env::read_env,
    )]
    pub test_2: Option<String>,
    pub test_3: Option<String>,
}


fn less_than_100(value: &u32) -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
    if *value < 100 {
        Ok(())
    } else {
        Err("should be less than 100".into())
    }
}

这将生成类似以下内容:

impl Test {         
    pub async fn load_test_1() -> std::result::Result<u32,::yacm::Error> {             
        let name = "TEST_TEST_ONE";             
        let mut value = yacm::env::read_env(&name).await;             
        if let Ok(None) = value { value = Ok(Some(142u32.into())) }             
        if let Ok(v) = value.as_ref() {                 
            if let Err(e) = less_than_100(v) { 
                return Err(::yacm::Error::ValidationError(name.to_string(), e)); 
            }             
        };             
        match value {                 
            Ok(Some(v)) => Ok(v),                 
            Ok(None) => Err(::yacm::Error::NotFound(name.to_string())),                 
            Err(e) => Err(e),             
        }         
    }         
    pub async fn load_test_2() -> std::result::Result<Option<String>, ::yacm::Error> {             
        let name = Test::load_test_1().await?;             
        let mut value = yacm::env::read_env(&name).await;             
        value         
    }         
    pub async fn load_test_3() -> std::result::Result<Option<String>, ::yacm::Error> {             
        let name = yacm::name_provider::screaming_snake_case("test_3", Some("derived")).await
            .map_err(|e| ::yacm::Error::Read("test_3".to_string(), e))?;             
        let mut value = yacm::env::read_env(&name).await;             
        value         
    }         
    pub async fn load() -> Result<Self, ::yacm::Error> { 
        Ok(Self { 
            test_1: Self::load_test_1().await?, 
            test_2: Self::load_test_2().await?, 
            test_3: Self::load_test_3().await? 
        }) 
    }     
}

使用 Yacm

yacm 为结构体的单个字段和整个结构体生成代码来加载,基于每个字段指定的或默认的加载器。加载函数应具有以下签名:

pubasyncfn foo_loader<T>(name: &str) -> Result<Option<T>, yacm::Error>

返回的 yacm::Error 应该是 yacm::Error::Readyacm::Error::Parse

传递给加载器的名称来自 name_provider 或特定字段的名称。可以为单个字段或整个结构体指定 name_providers,其中 yacm 的默认值是 ::yacm::env::screaming_snake_case

Name_providers 应具有以下签名:

pubasyncfn foo(field: &str, prefix: Option<&str>) -> Result<String, Box<dynstd::error::Error+ Sync + Send>>

名称是类型为 &str 的表达式,可以可选地返回 Err(Box<dyn std::error::Error + Sync + Send>)

例如,名称可以是一个字面量 &str,例如 "FOO_BAR_SAMPLE",或者可以更复杂一些,例如 &format!("{}.sample",Config::load_env().await?)

结构体级别的属性

#[yacm(prefix= "foo",name_provider=path::custom_name_convention,default_loader=path::custom_loader)]

  • prefix:一个可选的 String,表示配置结构体的前缀,当生成用于加载配置的名称时,将传递给任何名称提供者。例如,可以指定 #[yacm(prefix = "sample")],以便字段 foobar 可以从名为 SAMPLE_FOOSAMPLE_BAR 的环境变量中加载。

  • name_provider:一个可选的路径,用作结构体中每个字段的默认 name_provider。

  • default_loader:一个可选的路径,覆盖 yacm 的默认值 ::yacm::env::read_env。

字段级别的属性

#[yacm(name= "bar",name_provider= ..,loader=path::custom_loader,default= 42,validator=path::custom_validator)]

  • name:一个可选的表达式,表示要使用的确切 &str 名称

  • name_provider:一个可选的路径,用作结构体中每个字段的默认 name_provider。

  • loader:一个可选的路径,覆盖结构体默认值或 yacm 默认值 ::yacm::env::read_env。

  • default:一个可选的默认值,其类型应与字段类型匹配

  • validator:一个可选的验证器路径,该验证器应具有类似 fn custom_val(value: &FieldType) -> Result<(), Box<dyn std::error::Error + Sync + Send>>

路线图

  1. 在几个自己的项目中使用它,直到我对接口有一些信心
  2. 添加测试
  3. 文档
  4. 也许可以将其作为异步功能,而不是默认(我不需要非异步,但有些人可能需要)

为什么你现在不应该使用它

它刚刚推出,我甚至还没有在所有打算使用的地方使用它。也就是说,接口预期将非常灵活。

欢迎反馈(是的,即使在这个早期阶段)

虽然我编程了 40 年,但我对 Rust 还是新手,我对 Rust 中的元编程更是新手。任何从渐进式改进的建议到链接到我应该使用的而不是浪费时间在另一个配置宏上的 crate 都受欢迎。

依赖项

~3.5MB
~64K SLoC