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 #[serde(default)]
13 pub defaults: DefaultsSection,
14 #[serde(default)]
15 pub distributor: DistributorSection,
16}
17
18#[derive(Debug, Default, Deserialize)]
19pub struct ToolsSection {
20 #[serde(rename = "greentic-component", default)]
21 pub greentic_component: ToolEntry,
22 #[serde(rename = "packc", default)]
23 pub packc: ToolEntry,
24 #[serde(rename = "packc-path", default)]
25 pub packc_path: 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 #[serde(default, flatten)]
51 pub profiles: HashMap<String, DistributorProfileConfig>,
52}
53
54#[derive(Debug, Clone, Deserialize)]
55pub struct DistributorProfileConfig {
56 #[serde(default)]
58 pub base_url: Option<String>,
59 #[serde(default)]
61 pub url: Option<String>,
62 #[serde(default)]
64 pub token: Option<String>,
65 #[serde(default)]
67 pub tenant_id: Option<String>,
68 #[serde(default)]
70 pub environment_id: Option<String>,
71 #[serde(default)]
73 pub headers: Option<HashMap<String, String>>,
74}
75
76pub fn load() -> Result<GreenticConfig> {
77 let path_override = std::env::var("GREENTIC_CONFIG").ok();
78 load_from(path_override.as_deref())
79}
80
81pub fn load_from(path_override: Option<&str>) -> Result<GreenticConfig> {
82 let Some(path) = config_path_override(path_override) else {
83 return Ok(GreenticConfig::default());
84 };
85
86 if !path.exists() {
87 return Ok(GreenticConfig::default());
88 }
89
90 let raw = fs::read_to_string(&path)
91 .with_context(|| format!("failed to read config at {}", path.display()))?;
92 let config: GreenticConfig = toml::from_str(&raw)
93 .with_context(|| format!("failed to parse config at {}", path.display()))?;
94 Ok(config)
95}
96
97fn config_path_override(path_override: Option<&str>) -> Option<PathBuf> {
98 if let Some(raw) = path_override {
99 return Some(PathBuf::from(raw));
100 }
101 config_path()
102}
103
104pub fn config_path() -> Option<PathBuf> {
105 if let Some(mut dir) = dirs::config_dir() {
107 dir.push("greentic-dev");
108 dir.push("config.toml");
109 if dir.exists() {
110 return Some(dir);
111 }
112 }
113 dirs::home_dir().map(|mut home| {
114 home.push(".greentic");
115 home.push("config.toml");
116 home
117 })
118}