voa_config/config/
base.rs1use std::{collections::BTreeMap, fmt::Display};
4
5use libc::geteuid;
6use log::{debug, error, trace};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 TechnologySettings,
11 config::technology::TechnologySettingsDefaults,
12 core::{Context, Os, Purpose},
13 file::{ConfigLoader, LoadMode},
14};
15
16#[derive(Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
18pub struct OsPurposeContext {
19 os: Os,
20
21 purpose: Purpose,
23
24 context: Context,
26}
27
28impl OsPurposeContext {
29 pub fn new(os: Os, purpose: Purpose, context: Context) -> Self {
31 Self {
32 os,
33 purpose,
34 context,
35 }
36 }
37}
38
39impl Display for OsPurposeContext {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "{}/{}/{}", self.os, self.purpose, self.context)
42 }
43}
44
45#[derive(Debug)]
49pub struct VoaConfig {
50 defaults: TechnologySettings,
52
53 oses: BTreeMap<Os, TechnologySettings>,
55
56 contexts: BTreeMap<OsPurposeContext, TechnologySettings>,
58}
59
60impl VoaConfig {
61 pub fn load() -> Self {
70 let euid = unsafe { geteuid() };
71 trace!("Config::load called with process user id {euid}");
72
73 let loader = if euid < 1000 {
74 debug!("⤷ Using system mode configuration file locations");
75 ConfigLoader::load(LoadMode::System)
76 } else {
77 debug!("⤷ Using user mode configuration file locations");
78 ConfigLoader::load(LoadMode::User)
79 };
80
81 Self::from(loader)
82 }
83
84 pub fn settings_for_os_or_default(&self, os: &Os) -> &TechnologySettings {
86 self.oses.get(os).unwrap_or(&self.defaults)
87 }
88
89 pub fn settings_for_context_or_default(
91 &self,
92 os: &Os,
93 purpose: &Purpose,
94 context: &Context,
95 ) -> &TechnologySettings {
96 self.contexts
97 .get(&OsPurposeContext::new(
98 os.clone(),
99 purpose.clone(),
100 context.clone(),
101 ))
102 .unwrap_or(self.oses.get(os).unwrap_or(&self.defaults))
103 }
104
105 pub fn oses(&self) -> &BTreeMap<Os, TechnologySettings> {
107 &self.oses
108 }
109
110 pub fn contexts(&self) -> &BTreeMap<OsPurposeContext, TechnologySettings> {
112 &self.contexts
113 }
114}
115
116impl From<ConfigLoader> for VoaConfig {
117 fn from(value: ConfigLoader) -> Self {
118 let defaults = TechnologySettings::default();
119 let mut oses: BTreeMap<Os, TechnologySettings> = BTreeMap::new();
120 let mut contexts: BTreeMap<OsPurposeContext, TechnologySettings> = BTreeMap::new();
121
122 for (os, config_file) in value.defaults() {
124 if let Some(config_technology_settings) = config_file.default_technology_settings() {
125 match TechnologySettings::from_config_with_defaults(
126 config_technology_settings,
127 TechnologySettingsDefaults {
128 built_in_defaults: &defaults,
129 os_defaults: None,
130 },
131 ) {
132 Ok(technology_settings) => {
133 oses.insert(os.clone(), technology_settings);
134 }
135 Err(error) => {
136 error!(
137 "Unable to create default technology settings for OS {os} due to error: {error}"
138 );
139 }
140 }
141 }
142 }
143 for (os, config_files) in value.drop_ins() {
150 for config_file in config_files.iter() {
151 if let Some(config_technology_settings) = config_file.default_technology_settings()
152 {
153 match TechnologySettings::from_config_with_defaults(
154 config_technology_settings,
155 TechnologySettingsDefaults {
156 built_in_defaults: &defaults,
157 os_defaults: oses.get(os),
158 },
159 ) {
160 Ok(technology_settings) => {
161 oses.insert(os.clone(), technology_settings);
162 }
163 Err(error) => {
164 error!(
165 "Unable to create default technology settings for OS {os} due to error: {error}"
166 );
167 }
168 }
169 }
170 }
171 }
172
173 for (os, config_file) in value.defaults() {
175 for context_settings in config_file.contexts().iter() {
177 let os_purpose_context = OsPurposeContext::new(
178 os.clone(),
179 context_settings.purpose().clone(),
180 context_settings.context().clone(),
181 );
182
183 let technology_settings = match TechnologySettings::from_config_with_defaults(
184 context_settings.config_technology_settings(),
185 TechnologySettingsDefaults {
186 built_in_defaults: &defaults,
187 os_defaults: oses.get(os),
188 },
189 ) {
190 Ok(settings) => settings,
191 Err(error) => {
192 error!(
193 "Unable to create technology settings for OS/purpose/context {os_purpose_context} due to an error: {error}"
194 );
195 continue;
196 }
197 };
198
199 contexts.insert(os_purpose_context, technology_settings);
200 }
201 }
202
203 for (os, config_files) in value.drop_ins() {
210 for config_file in config_files.iter() {
211 for context_settings in config_file.contexts().iter() {
213 let os_purpose_context = OsPurposeContext::new(
214 os.clone(),
215 context_settings.purpose().clone(),
216 context_settings.context().clone(),
217 );
218
219 let technology_settings = match TechnologySettings::from_config_with_defaults(
220 context_settings.config_technology_settings(),
221 TechnologySettingsDefaults {
222 built_in_defaults: &defaults,
223 os_defaults: oses.get(os),
224 },
225 ) {
226 Ok(technology_settings) => technology_settings,
227 Err(error) => {
228 error!(
229 "Unable to create technology settings for OS/purpose/context {os_purpose_context} due to an error: {error}"
230 );
231 continue;
232 }
233 };
234
235 contexts.insert(os_purpose_context, technology_settings);
236 }
237 }
238 }
239
240 Self {
241 defaults,
242 oses,
243 contexts,
244 }
245 }
246}