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 #[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}