1use std::env;
2use thiserror::Error;
3
4#[doc(hidden)]
5pub mod fallback {
6 pub fn load_or_default<T>(_env_path: &std::path::Path) -> Result<T, crate::CfgError>
7 where
8 T: Default,
9 {
10 Ok(T::default())
17 }
18}
19
20#[derive(Debug, Error)]
21pub enum CfgError {
22 #[error("missing required env: {0}")]
23 MissingEnv(&'static str),
24
25 #[error("failed to parse env {key} value `{value}` into {ty}: {source}")]
26 ParseError {
27 key: &'static str,
28 value: String,
29 ty: &'static str,
30 #[source]
31 source: Box<dyn std::error::Error + Send + Sync>,
32 },
33
34 #[error("failed to load env: {msg}: {source}")]
35 LoadError {
36 msg: &'static str,
37 #[source]
38 source: Box<dyn std::error::Error + Send + Sync>,
39 },
40}
41
42pub trait FromEnv: Sized {
43 fn load(env_path: &std::path::Path) -> Result<Self, CfgError>;
44}
45
46pub fn get_env(key: &'static str) -> Option<String> {
48 env::var(key).ok()
49}
50
51pub fn load_env_file(env_path: &std::path::Path) -> Result<(), CfgError> {
53 match dotenvy::from_path(env_path) {
55 Ok(_) => Ok(()),
56 Err(dotenvy::Error::Io(e)) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
57 Err(e) => Err(CfgError::LoadError {
58 msg: "failed to load .env file",
59 source: Box::new(e),
60 }),
61 }
62}
63
64pub fn parse_scalar<T: std::str::FromStr>(key: &'static str, raw: String) -> Result<T, CfgError>
66where
67 <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
68{
69 raw.parse::<T>().map_err(|e| CfgError::ParseError {
70 key,
71 value: raw,
72 ty: std::any::type_name::<T>(),
73 source: Box::new(e),
74 })
75}
76
77pub fn parse_vec<T: std::str::FromStr>(
79 key: &'static str,
80 raw: String,
81 sep: &'static str,
82) -> Result<Vec<T>, CfgError>
83where
84 <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
85{
86 if sep.is_empty() {
87 return Ok(Vec::new());
88 }
89 let mut out = Vec::new();
90 for part in raw.split(sep) {
91 let s = part.trim().to_string();
92 if s.is_empty() {
93 continue;
94 }
95 out.push(parse_scalar::<T>(key, s)?);
96 }
97 Ok(out)
98}