greentic_dev/
config.rs

1use std::collections::HashMap;
2use std::fs;
3use std::path::PathBuf;
4
5use anyhow::{Context, Result};
6use serde::Deserialize;
7
8#[derive(Debug, Default, Deserialize)]
9pub struct GreenticConfig {
10    #[serde(default)]
11    pub tools: ToolsSection,
12    #[allow(dead_code)]
13    #[serde(default)]
14    pub defaults: DefaultsSection,
15    #[allow(dead_code)]
16    #[serde(default)]
17    pub distributor: DistributorSection,
18}
19
20#[derive(Debug, Default, Deserialize)]
21pub struct ToolsSection {
22    #[serde(rename = "greentic-component", default)]
23    pub greentic_component: ToolEntry,
24    #[serde(rename = "packc", default)]
25    pub packc: ToolEntry,
26}
27
28#[derive(Debug, Default, Deserialize)]
29pub struct ToolEntry {
30    pub path: Option<PathBuf>,
31}
32
33#[allow(dead_code)]
34#[derive(Debug, Default, Deserialize)]
35pub struct DefaultsSection {
36    #[serde(default)]
37    pub component: ComponentDefaults,
38}
39
40#[allow(dead_code)]
41#[derive(Debug, Default, Deserialize)]
42pub struct ComponentDefaults {
43    pub org: Option<String>,
44    pub template: Option<String>,
45}
46
47#[derive(Debug, Default, Deserialize)]
48pub struct DistributorSection {
49    /// Map of profile name -> profile configuration.
50    #[allow(dead_code)]
51    #[serde(default, flatten)]
52    pub profiles: HashMap<String, DistributorProfileConfig>,
53}
54
55#[derive(Debug, Clone, Deserialize)]
56pub struct DistributorProfileConfig {
57    #[allow(dead_code)]
58    pub url: String,
59    #[allow(dead_code)]
60    #[serde(default)]
61    pub token: Option<String>,
62}
63
64pub fn load() -> Result<GreenticConfig> {
65    let path_override = std::env::var("GREENTIC_CONFIG").ok();
66    load_from(path_override.as_deref())
67}
68
69pub fn load_from(path_override: Option<&str>) -> Result<GreenticConfig> {
70    let Some(path) = config_path_override(path_override) else {
71        return Ok(GreenticConfig::default());
72    };
73
74    if !path.exists() {
75        return Ok(GreenticConfig::default());
76    }
77
78    let raw = fs::read_to_string(&path)
79        .with_context(|| format!("failed to read config at {}", path.display()))?;
80    let config: GreenticConfig = toml::from_str(&raw)
81        .with_context(|| format!("failed to parse config at {}", path.display()))?;
82    Ok(config)
83}
84
85fn config_path_override(path_override: Option<&str>) -> Option<PathBuf> {
86    if let Some(raw) = path_override {
87        return Some(PathBuf::from(raw));
88    }
89    config_path()
90}
91
92pub fn config_path() -> Option<PathBuf> {
93    dirs::home_dir().map(|mut home| {
94        home.push(".greentic");
95        home.push("config.toml");
96        home
97    })
98}