1use anyhow::{Context, Result};
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8use tokio::fs;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Profile {
13 pub aad: Option<String>,
15
16 pub key_b64: Option<String>,
18
19 pub url: Option<String>,
21
22 pub url_rev: Option<String>,
24
25 pub url_b64: Option<String>,
27
28 pub output: Option<String>,
30}
31
32impl Default for Profile {
33 fn default() -> Self {
34 Self {
35 aad: None,
36 key_b64: None,
37 url: None,
38 url_rev: None,
39 url_b64: None,
40 output: None,
41 }
42 }
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct Config {
48 pub default: Option<Profile>,
50
51 #[serde(default)]
53 pub profile: std::collections::HashMap<String, Profile>,
54}
55
56impl Config {
57 pub async fn from_toml_file<P: AsRef<Path>>(path: P) -> Result<Self> {
59 let content = fs::read_to_string(path.as_ref())
60 .await
61 .context("failed to read config file")?;
62 toml::from_str(&content).context("failed to parse TOML config")
63 }
64
65 pub async fn from_json_file<P: AsRef<Path>>(path: P) -> Result<Self> {
67 let content = fs::read_to_string(path.as_ref())
68 .await
69 .context("failed to read config file")?;
70 serde_json::from_str(&content).context("failed to parse JSON config")
71 }
72
73 pub fn get_profile(&self, name: Option<&str>) -> Option<Profile> {
75 match name {
76 Some(profile_name) => self.profile.get(profile_name).cloned(),
77 None => self.default.clone(),
78 }
79 }
80
81 pub fn merge_with_cli(
83 mut profile: Profile,
84 key_b64: Option<String>,
85 url: Option<String>,
86 url_rev: Option<String>,
87 url_b64: Option<String>,
88 aad: Option<String>,
89 output: Option<String>,
90 ) -> Profile {
91 if let Some(k) = key_b64 {
92 profile.key_b64 = Some(k);
93 }
94 if let Some(u) = url {
95 profile.url = Some(u);
96 }
97 if let Some(ur) = url_rev {
98 profile.url_rev = Some(ur);
99 }
100 if let Some(ub) = url_b64 {
101 profile.url_b64 = Some(ub);
102 }
103 if let Some(a) = aad {
104 profile.aad = Some(a);
105 }
106 if let Some(o) = output {
107 profile.output = Some(o);
108 }
109 profile
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_profile_merge() {
119 let profile = Profile {
120 aad: Some("aad-1".to_string()),
121 key_b64: Some("key1".to_string()),
122 url: Some("https://example.com".to_string()),
123 url_rev: None,
124 url_b64: None,
125 output: None,
126 };
127
128 let merged = Config::merge_with_cli(
129 profile,
130 None,
131 Some("https://override.com".to_string()),
132 None,
133 None,
134 None,
135 None,
136 );
137
138 assert_eq!(merged.url.unwrap(), "https://override.com");
139 assert_eq!(merged.aad.unwrap(), "aad-1");
140 }
141}