Skip to main content

config_get/
format.rs

1use std::path::Path;
2
3/// Supported configuration file formats.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum Format {
6    /// Shell-style `KEY=VALUE` / dotenv files (`.env`)
7    Env,
8    /// Windows-style `.ini` files with `[sections]`
9    Ini,
10    /// TOML files
11    Toml,
12    /// JSON files
13    Json,
14    /// YAML files (`.yml` / `.yaml`)
15    Yaml,
16}
17
18impl Format {
19    /// Detect format from file extension.
20    ///
21    /// Returns `None` if the extension is unrecognised.
22    #[must_use]
23    pub fn from_path(path: &Path) -> Option<Self> {
24        let name = path.file_name()?.to_string_lossy();
25
26        if name == ".env" || name.starts_with(".env.") || name.ends_with(".env") {
27            return Some(Self::Env);
28        }
29
30        match path.extension()?.to_string_lossy().as_ref() {
31            "ini" => Some(Self::Ini),
32            "toml" => Some(Self::Toml),
33            "json" => Some(Self::Json),
34            "yml" | "yaml" => Some(Self::Yaml),
35            _ => None,
36        }
37    }
38
39    /// Human-readable label for this format.
40    #[must_use]
41    pub fn as_str(self) -> &'static str {
42        match self {
43            Self::Env => "env",
44            Self::Ini => "ini",
45            Self::Toml => "toml",
46            Self::Json => "json",
47            Self::Yaml => "yaml",
48        }
49    }
50
51    /// Returns the canonical file names checked for each format, in priority order.
52    #[must_use]
53    pub fn candidates(stem: &str) -> Vec<String> {
54        vec![
55            ".env".to_string(),
56            format!("{stem}.ini"),
57            format!("{stem}.toml"),
58            format!("{stem}.json"),
59            format!("{stem}.yml"),
60            format!("{stem}.yaml"),
61        ]
62    }
63}
64
65impl std::fmt::Display for Format {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        f.write_str(self.as_str())
68    }
69}