V0.1.0 initial

This commit is contained in:
2025-05-19 08:03:46 +02:00
parent 89d94358f4
commit 2e35997243
9 changed files with 304 additions and 7 deletions

2
Cargo.lock generated
View File

@@ -3,5 +3,5 @@
version = 4 version = 4
[[package]] [[package]]
name = "no-man-sky" name = "merlin_env_helper"
version = "0.1.0" version = "0.1.0"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "no-man-sky" name = "merlin_env_helper"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]

View File

@@ -0,0 +1 @@
my_secret

253
src/config/env.rs Normal file
View File

@@ -0,0 +1,253 @@
//! Module for reading configuration values from environment variables or secure files.
//!
//! This module provides utilities to retrieve configuration values from environment variables,
//! with support for fallback values and secure file-based secrets. It defines error types for
//! configuration loading and exposes a unified `Result` type for error handling.
//!
//! # Types
//!
//! - [`ConfigError`]: Represents errors that can occur when reading configuration values.
//! - [`ConfigErrorKind`]: Enum describing the kind of configuration error (I/O, file not found, or not found).
//! - [`ConfigErrorMessage`]: Simple error message wrapper.
//!
//! # Functions
//!
//! - [`get_env_value`]: Retrieves a configuration value from the environment or a secure file, with optional fallback.
//!
//! # Usage
//!
//! Use [`get_env_value`] with an [`EnvKey`] to retrieve configuration values, handling errors as needed.
//!
//! # Examples
//!
//! ```rust
//! use no_man_sky::config::EnvKey;
//! use no_man_sky::config::get_env_value;
//! let env_key = EnvKey::key("MY_CONFIG_KEY");
//! match get_env_value(&env_key) {
//! Ok(value) => println!("Config value: {}", value),
//! Err(e) => eprintln!("Error: {}", e),
//! }
//! ```
use core::fmt;
use std::{error::Error, fmt::{Display, Formatter}, io::{self, Read}, path::Path};
use crate::config::types::EnvKey;
pub type Result<T, E = ConfigError> = std::result::Result<T, E>;
#[derive(Debug)]
#[non_exhaustive]
pub struct ConfigError {
pub key: String,
pub kind: ConfigErrorKind,
}
impl Display for ConfigError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "error reading configuration key `{}`", self.key)
}
}
impl Error for ConfigError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.kind {
ConfigErrorKind::IO(e) => Some(e),
ConfigErrorKind::FileNotFound(e) => Some(e),
ConfigErrorKind::NotFound => None,
}
}
}
/// The kind of error that can occur when reading a configuration key.
#[derive(Debug)]
pub enum ConfigErrorKind {
IO(io::Error),
FileNotFound(ConfigErrorMessage),
NotFound,
}
/// A simple message that can be used to describe an error.
#[derive(Debug)]
#[non_exhaustive]
pub struct ConfigErrorMessage {
pub msg: String,
}
impl Display for ConfigErrorMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl Error for ConfigErrorMessage {
}
pub fn get_env_value(env_key : &EnvKey) -> Result<String> {
if let Some(sec_key) = &env_key.sec_key {
let key = std::env::var(&sec_key);
if let Ok(path_file) = key {
return read_secure_value(&env_key.key, &path_file);
}
}
read_key_value(&env_key.key, &env_key.fallback)
}
fn read_key_value(key: &str, fallback: &Option<String>) -> Result<String> {
let env_value = std::env::var(key);
println!("env_value: {:?}", env_value);
if let Ok(value) = env_value {
return Ok(value);
}
if let Some(fallback) = fallback {
return Ok(fallback.to_string());
}
Err(
ConfigError {
key: key.to_string(),
kind: ConfigErrorKind::NotFound,
}
)
}
fn read_secure_value(key: &str, path_file: &str) -> Result<String> {
let path = Path::new(path_file);
let exists = path.try_exists().map_err(|e| -> ConfigError {
println!("path.try_exists(): {:?}", e);
ConfigError {
key: format!("{}: {} File not found.", key.to_string(), path_file),
kind: ConfigErrorKind::IO(e),
}
})?;
if !exists {
return Err(
ConfigError {
key: format!("{}: {} File not found.", key.to_string(), path_file),
kind: ConfigErrorKind::FileNotFound(ConfigErrorMessage {
msg: format!("File not found: {}", path_file),
}),
}
);
}
let file = std::fs::File::open(path).map_err(|e| -> ConfigError {
println!("std::fs::File::open(path_file): {:?}", e);
ConfigError {
key: key.to_string(),
kind: ConfigErrorKind::IO(e),
}
})?;
let mut reader = std::io::BufReader::new(file);
let mut content = String::new();
reader.read_to_string(&mut content).map_err(|e| -> ConfigError {
ConfigError {
key: key.to_string(),
kind: ConfigErrorKind::IO(e),
}
})?;
Ok(content)
}
#[cfg(test)]
mod tests {
use super::*;
use std::{panic, path::PathBuf};
fn clean_up(reset_value: &Option<String>) {
if let Some(value) = reset_value {
unsafe {std::env::set_var("TEST_KEY", value)};
} else {
unsafe {std::env::remove_var("TEST_KEY")};
}
}
fn run_test<T, U>(env_key : &str, test: T, clean_up: U) -> ()
where T: FnOnce() -> () + std::panic::UnwindSafe
, U: FnOnce(&Option<String>) -> ()
{
let old_value = std::env::var(env_key);
let mut reset_value= None;
if let Ok(value) = old_value {
reset_value = Some(value.clone());
}
let result = panic::catch_unwind( || {
test();
});
clean_up(&reset_value);
assert!(!result.is_err(), "Test failed");
}
#[test]
fn test_get_env_value() {
run_test("TEST_KEY", || {
let key = "TEST_KEY";
let value = "test_value";
unsafe {std::env::set_var(key, value)};
let env_key = EnvKey::key(key);
let result = get_env_value(&env_key);
assert_eq!(result.is_ok(), true);
assert_eq!(result.unwrap(), value);
},
clean_up
);
}
#[test]
fn test_get_env_value_with_fallback() {
run_test("TEST_KEY", || {
let key = "TEST_KEY";
let fallback_value = "fallback_value";
let env_key = EnvKey::key_with_fallback(key, fallback_value);
let result = get_env_value(&env_key);
assert_eq!(result.is_ok(), true);
assert_eq!(result.unwrap(), fallback_value);
},
clean_up);
}
#[test]
fn test_get_env_value_with_secure_key() {
run_test("TEST_KEY", || {
let key = "TEST_KEY";
let value = "test_value";
let secure_key = "TEST_KEY_FILE";
let secure_value = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/test_secret.txt");
unsafe {std::env::set_var(key, value)};
unsafe {std::env::set_var(secure_key, &secure_value)};
assert_eq!(secure_value.try_exists().unwrap(), true);
let env_key = EnvKey::secure_key(key, secure_key);
let result = get_env_value(&env_key);
assert_eq!(result.is_ok(), true);
assert_eq!(result.unwrap(), "my_secret");
},
clean_up);
}
}

5
src/config/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod env;
pub mod types;
pub use types::*;
pub use env::*;

40
src/config/types.rs Normal file
View File

@@ -0,0 +1,40 @@
#[derive(Debug, PartialEq, Eq)]
pub struct EnvKey {
pub key: String,
pub sec_key: Option<String>,
pub fallback: Option<String>,
}
impl EnvKey {
pub fn key(key: &str) -> Self {
Self {
key: key.to_string(),
sec_key: None,
fallback: None,
}
}
pub fn key_with_fallback(key: &str, fallback: &str) -> Self {
Self {
key: key.to_string(),
sec_key: None,
fallback: Some(fallback.to_string()),
}
}
pub fn secure_key(key: &str, sec_key: &str) -> Self {
Self {
key: key.to_string(),
sec_key: Some(sec_key.to_string()),
fallback: None,
}
}
pub fn secure_with_fallback(key: &str, sec_key: &str, fallback: &str) -> Self {
Self {
key: key.to_string(),
sec_key: Some(sec_key.to_string()),
fallback: Some(fallback.to_string()),
}
}
}

1
src/lib.rs Normal file
View File

@@ -0,0 +1 @@
pub mod config;

View File

@@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}