feat: added file loader

This commit is contained in:
darwincereska
2026-06-08 12:38:41 -04:00
parent 56339890e5
commit 36c74e0c25
8 changed files with 359 additions and 87 deletions
+138 -40
View File
@@ -1,76 +1,174 @@
# envkit
`envkit` is a small Rust crate for reading environment variables and converting them into typed values.
`envkit` is a small Rust crate for reading environment variables as typed values.
It keeps the API minimal:
It provides a minimal loader API for required values, optional values, defaults,
prefixed variable names, and simple `.env`-style files.
- `EnvLoader::get` reads a required variable.
- `EnvLoader::get_or` reads a variable with a fallback.
- `EnvLoader::get_opt` returns `Option<T>`.
- `with_prefix` lets you build loaders for namespaced variables like `APP_PORT` or `DB_HOST`.
## Install
## Supported types
Add `envkit` to your `Cargo.toml`:
`envkit` currently supports:
```toml
[dependencies]
envkit = "0.2.1"
```
## Quick Start
```rust
use envkit::EnvLoader;
fn main() -> Result<(), envkit::error::EnvError> {
let env = EnvLoader::new();
let port: u16 = env.get("PORT")?;
let debug: bool = env.get_or("DEBUG", false);
let app_name: String = env
.get_opt("APP_NAME")
.unwrap_or_else(|| "envkit".to_string());
println!("{app_name} listening on port {port}; debug={debug}");
Ok(())
}
```
## API
### `EnvLoader::get`
Reads a required environment variable and parses it into the requested type.
```rust
let env = envkit::EnvLoader::new();
let port: u16 = env.get("PORT")?;
```
Returns `EnvError::MissingVar` when the variable is not set and
`EnvError::Invalid` when parsing fails.
### `EnvLoader::get_or`
Reads an environment variable and returns a fallback when the variable is
missing or invalid.
```rust
let debug: bool = env.get_or("DEBUG", false);
```
### `EnvLoader::get_opt`
Reads an environment variable and returns `None` when the variable is missing or
invalid.
```rust
let name: Option<String> = env.get_opt("APP_NAME");
```
### `EnvLoader::with_prefix`
Creates a loader that prepends a prefix to every key.
```rust
let app = env.with_prefix("APP_");
let host: String = app.get("HOST")?; // reads APP_HOST
let port: u16 = app.get_or("PORT", 8080); // reads APP_PORT
```
### `EnvLoader::load_file`
Loads variables from a file with one `KEY=VALUE` pair per line.
```rust
let env = envkit::EnvLoader::new();
env.load_file(".env")?;
```
Blank lines and lines starting with `#` are ignored. Keys and values are
trimmed before being written into the process environment.
Example file:
```text
# .env
PORT=8080
DEBUG=true
APP_NAME=envkit
```
## Supported Types
Built-in parsing supports:
- `String`
- `bool`
- numeric types: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `f32`, `f64`
- Vectors with any of the supported types
- signed integers: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`
- unsigned integers: `u8`, `u16`, `u32`, `u64`, `u128`, `usize`
- floats: `f32`, `f64`
- `Vec<T>` where `T` also implements `FromEnv`
Boolean parsing accepts:
Boolean values accept:
- `true` / `false`
- `1` / `0`
- `t` / `f`
- `yes` / `no`
## Quick start
Lists are comma-separated and each item is trimmed before parsing:
```rust
use envkit::EnvLoader;
fn main() -> Result<(), envkit::error::EnvError> {
let loader = EnvLoader;
let port: u16 = loader.get("PORT")?;
let debug: bool = loader.get_or("DEBUG", false);
let app_name: String = loader.get_opt("APP_NAME").unwrap_or_else(|| "envkit".to_string());
println!("port={port}, debug={debug}, app_name={app_name}");
Ok(())
}
let env = envkit::EnvLoader::new();
let ports: Vec<u16> = env.get("PORTS")?;
let flags: Vec<bool> = env.get("FEATURE_FLAGS")?;
```
## Prefixed variables
## Custom Types
Implement `FromEnv` for your own types when you want domain-specific parsing.
```rust
use envkit::EnvLoader;
use envkit::{error::EnvError, FromEnv};
fn main() -> Result<(), envkit::error::EnvError> {
let loader = EnvLoader;
let app = loader.with_prefix("APP_");
enum LogFormat {
Json,
Pretty,
}
let host: String = app.get("HOST")?;
let port: u16 = app.get_or("PORT", 8080);
println!("host={host}, port={port}");
Ok(())
impl FromEnv for LogFormat {
fn from_env(value: &str) -> Result<Self, EnvError> {
match value.trim().to_lowercase().as_str() {
"json" => Ok(Self::Json),
"pretty" => Ok(Self::Pretty),
other => Err(EnvError::invalid(
"LogFormat",
other,
"expected `json` or `pretty`",
)),
}
}
}
```
If you use the prefixed loader above, it reads `APP_HOST` and `APP_PORT`.
## Errors
`envkit` returns `EnvError` for two cases:
`envkit` returns `EnvError` values:
- `MissingVar` when the environment variable is not present
- `Invalid` when parsing fails
- `EnvError::MissingVar(key)` when a required environment variable is not set.
- `EnvError::Invalid { key, value, message }` when a value cannot be parsed.
- `EnvError::FileError { path, message }` when a file cannot be read or contains
an invalid line.
## Examples
See the `examples/` folder for runnable usage samples.
Run the included examples with:
```bash
cargo run --example basic
cargo run --example prefixed
```
The examples seed their own environment variables so they can be run directly.
## Development