#env-var #macro #utility

mkenv

轻量但实用的宏,用于在启动时捕获环境上下文

9 个版本

0.1.8 2024 年 7 月 18 日
0.1.7 2024 年 7 月 18 日
0.1.6 2024 年 4 月 24 日

#95 in 配置

Download history 382/week @ 2024-04-19 75/week @ 2024-04-26 4/week @ 2024-05-17 2/week @ 2024-05-24 4/week @ 2024-06-28 31/week @ 2024-07-05 125/week @ 2024-07-12 100/week @ 2024-07-19 23/week @ 2024-07-26 12/week @ 2024-08-02

260 每月下载次数

MIT 许可证

33KB
529

mkenv

mkenv 是一个轻量级的 Rust crate,它提供了一个用于生成包含所有必要环境上下文的结构的宏 make_env!。这允许在程序开始时捕获所有变量,从而消除获取不存在的环境变量时的运行时错误。它旨在在环境初始化失败时抛出一个关于应用程序使用的所有变量的明确错误。

使用示例

对于每个环境变量声明,您至少需要提供以下字段

  • id:关联结构的标识符。这通常是结构字段相同的标识符,但以驼峰式命名。
  • kind:如何检索环境变量。
  • var:环境变量的名称,作为字符串字面量。
  • desc:作为字符串字面量的简短描述。

请看下面的示例

mkenv::make_env! {AppEnv:
  db_url: {
    id: DbUrl(String),
    kind: normal,
    var: "DB_URL",
    desc: "The URL to the database",
  }
}

上面的示例将生成以下定义的结构

struct AppEnv {
  db_url: String,
}

此结构实现了 Env 特性,允许通过 get 方法实例化。此方法将根据宏调用中设置的配置填充此结构的字段。

如果您希望在结构构造期间跟踪捕获的变量,您还可以使用其他方法。

想法是使用此结构的输出实例初始化一个静态变量,并使用后者在代码的任何位置获取必要的变量。基本示例(使用 once_cell crate)

static ENV: Lazy<AppEnv> = Lazy::new(AppEnv::get);

fn env() -> &'static AppEnv {
  &ENV
}

这样,您就可以在代码的任何位置使用 crate::::env.().db_url 访问 db_url 字段。

如果构建失败,即无法检索所需的变量,则显示此错误

Cannot initialize environment:
Got 0 valid variable
Got 1 incorrect variable
- Missing `DB_URL`
Note: full required environment description:
- `DB_URL`: The URL to the database

特性

获取环境变量的多种方式

通常

该宏显然支持以通常方式检索环境变量,即作为包含变量值的 String。这是通过 normal 类型实现的。

通过解析

该宏支持使用目标类型的 FromStr 特征实现来解析。为此,您需要将 parse 类别提供到环境变量的声明中。

通过读取文件内容

您可以将 file 类别提供到声明中,这将读取环境变量提供的路径上的文件。目前,无法混合使用 fileparse 类别。file 类别的输出是一个 String,就像 normal 类别一样。

包装类型

该宏支持包装类型,或可以从字符串或可以解析自字符串的单个值构建的类型。为此,您需要提供一个参数到类别字段,即用于构建包装类型的方法。请参见下面的示例

mkenv::make_env! {AppEnv:
  timeout: {
    id: Timeout(std::time::Duration),
    kind: parse(from_secs),
    var: "TIMEOUT",
    desc: "The duration of the timeout (in seconds)",
  }
}

默认值

只要您提供默认值,该宏就支持可选变量。这是通过 default 字段完成的

const DEFAULT_PORT: u16 = 3000;

mkenv::make_env! {AppEnv:
  port: {
    id: Port(u16),
    kind: parse,
    var: "PORT",
    desc: "The port used by the application",
    default: DEFAULT_PORT,
  }
}

请注意,默认值必须是一个标识符,这意味着它应该在预先定义为一个常量。类型必须实现 Debug,因为它在出现错误时会被打印。

还请注意,如果环境变量存在但解析失败,即使它有默认值,它也会引发错误。

条件编译

该宏支持环境变量声明的条件编译属性。然而,它目前还不支持声明在其上的任何其他属性。

例如

mkenv::make_env! {DbEnv:
  #[cfg(debug_assertions)]
  db_url: {
    // ... in debug mode
  },
  #[cfg(not(debug_assertions))]
  db_url: {
    // ... in release mode
  },
}

可组合声明

该宏支持可组合声明,这意味着将其他环境类型的声明包含在其他声明中。请参见下面的示例

mkenv::make_env! {DbEnv:
  db_url: {
    // ...
  }
}

mkenv::make_env! {AppEnv includes [DbEnv as db_env]:
  port: {
    // ...
  }
}

结构体声明大致如下

struct DbEnv {
  db_url: ...,
}

struct AppEnv {
  db_env: DbEnv,
  port: ...,
}

安全性

出于安全原因,您很可能不想在内存中保留环境变量的值。在大多数情况下,您希望它在初始化后一次使用后就被丢弃。

为了实现这一点,您需要通过在单独的宏调用中声明您需要丢弃的环境变量来完成上面的可组合模式

mkenv::make_env! {UsedOnce:
  sess_key: {
    // ...
  }
}

mkenv::make_env! {AppEnv includes [UsedOnce as used_once]:
  // ...
}

然后,在构建结构体时,您可以从 EnvSplitIncluded 特征中调用 split 方法。这将给出一个包含

  • 一个包含在 includes [...] 子句中的字段的结构体
  • 一个包含所有其他字段的结构体,可以安全地在运行时保留在内存中

通过将结构体分成两部分,您将所有权分成两部分:您可以在初始化过程完成后丢弃第一部分,并且可以用第二部分初始化一个静态变量

fn init_my_env() {
  let env = AppEnv::get();
  let (used_once, rest) = env.split();
  // do things with `used_once`, will be dropped at the end
  MY_STATIC_VAR.set(rest).unwrap_or_else(|_| panic!("wtf?"));
}

但静态变量的类型是什么?

您可以像这样声明您的静态变量(以使用 once_cell 包为例)

static MY_STATIC_VAR: OnceCell<mkenv::init_env!(AppEnv)> = OnceCell::new();

轻量级

该库非常轻量,它没有 0 依赖!

没有运行时依赖