feat: added support for lists

This commit is contained in:
darwincereska
2026-06-08 05:39:49 -04:00
parent 849eb9d62a
commit 5743c0b490
6 changed files with 60 additions and 14 deletions
Generated
+1 -1
View File
@@ -4,4 +4,4 @@ version = 4
[[package]] [[package]]
name = "envkit" name = "envkit"
version = "0.1.1" version = "0.2.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "envkit" name = "envkit"
version = "0.1.1" version = "0.2.0"
edition = "2024" edition = "2024"
authors = ["Darwin Cereska <discorddurr@gmail.com>"] authors = ["Darwin Cereska <discorddurr@gmail.com>"]
description = "A dead-simple env loader" description = "A dead-simple env loader"
+3 -3
View File
@@ -12,9 +12,9 @@ impl EnvError {
/// Creates a new `EnvError::Invalid` with the given key, value, and message. /// Creates a new `EnvError::Invalid` with the given key, value, and message.
pub fn invalid<K, V, M>(key: K, value: V, message: M) -> Self pub fn invalid<K, V, M>(key: K, value: V, message: M) -> Self
where where
K: Into<String>, K: Into<String>, // The key of the environment variable that was invalid.
V: Into<String>, V: Into<String>, // The value of the environment variable that was invalid.
M: Into<String>, M: Into<String>, // A message describing why the value was invalid.
{ {
Self::Invalid { Self::Invalid {
key: key.into(), key: key.into(),
+10
View File
@@ -28,6 +28,16 @@ impl<T: Number> FromEnv for T {
} }
} }
/// Adds support for loading lists of values from environment variables.
impl<T: FromEnv> FromEnv for Vec<T> {
fn from_env(value: &str) -> Result<Self, EnvError> {
match parser::parse_list(value) {
Ok(value) => Ok(value),
Err(e) => Err(e)
}
}
}
/// Adds support for loading strings from environment variables. /// Adds support for loading strings from environment variables.
impl FromEnv for String { impl FromEnv for String {
fn from_env(value: &str) -> Result<String, EnvError> { fn from_env(value: &str) -> Result<String, EnvError> {
+30 -9
View File
@@ -1,5 +1,6 @@
use std::fmt::Display; use std::fmt::Display;
use std::any::type_name; use std::any::type_name;
use crate::FromEnv;
use crate::error::EnvError; use crate::error::EnvError;
use std::str::FromStr; use std::str::FromStr;
@@ -21,18 +22,24 @@ impl Number for usize {}
impl Number for f32 {} impl Number for f32 {}
impl Number for f64 {} impl Number for f64 {}
/// Normalizes a string by trimming whitespace and converting to lowercase.
fn normalize(input: &str) -> String {
input.trim().to_lowercase().to_string()
}
/// Parses a string as a boolean value. /// Parses a string as a boolean value.
pub fn parse_bool(value: &str) -> Result<bool, EnvError> { pub fn parse_bool(value: &str) -> Result<bool, EnvError> {
if value.to_lowercase() == "t" { return Ok(true) } let normalized = normalize(value);
if value.to_lowercase() == "f" { return Ok(false) } if normalized == "t" { return Ok(true) }
if value == "1" { return Ok(true) } if normalized == "f" { return Ok(false) }
if value == "0" { return Ok(false) } if normalized == "1" { return Ok(true) }
if value == "yes" { return Ok(true) } if normalized == "0" { return Ok(false) }
if value == "no" { return Ok(false) } if normalized == "yes" { return Ok(true) }
if normalized == "no" { return Ok(false) }
match value.parse::<bool>() { match normalized.parse::<bool>() {
Ok(value) => Ok(value), Ok(normalized) => Ok(normalized),
Err(e) => Err(EnvError::invalid("bool", value, &e.to_string())) Err(e) => Err(EnvError::invalid("bool", normalized, &e.to_string()))
} }
} }
@@ -43,3 +50,17 @@ pub fn parse_number<T: Number>(value: &str) -> Result<T, EnvError> {
Err(e) => Err(EnvError::invalid(type_name::<T>(), value, &e.to_string())) Err(e) => Err(EnvError::invalid(type_name::<T>(), value, &e.to_string()))
} }
} }
/// Parses a comma-separated list of values of the specified type.
pub fn parse_list<T: FromEnv>(value: &str) -> Result<Vec<T>, EnvError> {
let items = value.split(',').map(|item| item.trim());
let mut result = Vec::new();
for item in items {
match T::from_env(item) {
Ok(value) => result.push(value),
Err(e) => return Err(e)
}
}
Ok(result)
}
+15
View File
@@ -2,6 +2,7 @@
mod test_parser { mod test_parser {
use envkit::parser::parse_bool; use envkit::parser::parse_bool;
use envkit::parser::parse_number; use envkit::parser::parse_number;
use envkit::parser::parse_list;
#[test] #[test]
/// Tests for the `parse_bool` function. /// Tests for the `parse_bool` function.
@@ -34,4 +35,18 @@ mod test_parser {
assert_eq!(parse_number::<f32>("42"), Ok(42_f32)); assert_eq!(parse_number::<f32>("42"), Ok(42_f32));
assert_eq!(parse_number::<f64>("42"), Ok(42_f64)); assert_eq!(parse_number::<f64>("42"), Ok(42_f64));
} }
#[test]
/// Tests for the `parse_list` function.
fn test_parse_list() {
let numbers = "1,2,3,4";
let names = "Alice, Bob, Charlie";
let bools = "true, false, yes, no";
let floats = "3.14, 2.718, 1.618";
assert_eq!(parse_list::<u8>(numbers), Ok(Vec::from([1,2,3,4])));
assert_eq!(parse_list::<String>(names), Ok(Vec::from(["Alice".to_string(), "Bob".to_string(), "Charlie".to_string()])));
assert_eq!(parse_list::<bool>(bools), Ok(Vec::from([true, false, true, false])));
assert_eq!(parse_list::<f64>(floats), Ok(Vec::from([3.14, 2.718, 1.618])));
}
} }