slave-framework 0.7.0

Runtime for slave apps (a Laravel-inspired Rust framework for MCP-first apps): server lifecycle, config, logging, Turso Cloud database and migrations, and OpenID Connect sign-in
Documentation
//! Reading configuration from the process environment and `.env` files.

use std::fmt::Display;
use std::path::PathBuf;
use std::str::FromStr;

use crate::{Error, Result};

/// Loads variables from a `.env` file in the current directory or its parents.
///
/// Variables already set in the process environment take precedence. A missing
/// file is not an error; `Ok(None)` is returned instead.
pub fn load_dotenv() -> Result<Option<PathBuf>> {
    match dotenvy::dotenv() {
        Ok(path) => Ok(Some(path)),
        Err(err) if err.not_found() => Ok(None),
        Err(err) => Err(err.into()),
    }
}

/// Returns the value of `key`, or `default` if it is unset or not valid Unicode.
pub fn var_or(key: &str, default: &str) -> String {
    std::env::var(key).unwrap_or_else(|_| default.to_owned())
}

/// Parses the value of `key`, or returns `default` if it is unset.
pub fn parse_or<T>(key: &str, default: T) -> Result<T>
where
    T: FromStr,
    T::Err: Display,
{
    match std::env::var(key) {
        Ok(value) => parse_value(key, &value),
        Err(_) => Ok(default),
    }
}

fn parse_value<T>(key: &str, value: &str) -> Result<T>
where
    T: FromStr,
    T::Err: Display,
{
    value
        .trim()
        .parse()
        .map_err(|err: T::Err| Error::InvalidEnv {
            key: key.to_owned(),
            value: value.to_owned(),
            reason: err.to_string(),
        })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_valid_values() {
        assert_eq!(parse_value::<u16>("PORT", " 8080 ").unwrap(), 8080);
    }

    #[test]
    fn reports_invalid_values() {
        let err = parse_value::<u16>("PORT", "eighty").unwrap_err();
        assert!(err.to_string().contains("PORT"), "{err}");
    }

    #[test]
    fn falls_back_to_default_when_unset() {
        assert_eq!(parse_or("SLAVE_TEST_SURELY_UNSET_VAR", 42u16).unwrap(), 42);
        assert_eq!(var_or("SLAVE_TEST_SURELY_UNSET_VAR", "x"), "x");
    }
}