onetaskgraph_core/config/
effective.rs1use schemars::JsonSchema;
9use serde::Serialize;
10use serde_json::Value;
11
12use crate::secrets::SecretsReport;
13
14use super::Config;
15use super::layer::{Merged, Origin, Setting, SettingPath};
16
17#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
19pub struct EffectiveConfig {
20 pub settings: Vec<Setting>,
22 pub secrets: SecretsReport,
28}
29
30impl EffectiveConfig {
31 #[must_use]
37 pub fn new(merged: &Merged, config: &Config, secrets: SecretsReport) -> Self {
38 let mut settings: Vec<Setting> = merged.values().cloned().collect();
39
40 for (key, value) in [
41 ("page_size", Value::from(config.page_size().get())),
42 (
43 "output",
44 serde_json::to_value(config.output()).expect("an output format renders as JSON"),
45 ),
46 (
47 "default_sources",
48 serde_json::to_value(config.selected_sources())
49 .expect("source names render as JSON"),
50 ),
51 ] {
52 let key = SettingPath::parse(key).expect("a literal path with no empty segment");
53 if !settings.iter().any(|setting| setting.key == key) {
54 settings.push(Setting {
55 key,
56 value,
57 origin: Origin::Default,
58 });
59 }
60 }
61
62 settings.sort_by(|left, right| left.key.cmp(&right.key));
63 Self { settings, secrets }
64 }
65
66 #[must_use]
73 pub fn render_text(&self) -> String {
74 let key_width = self
75 .settings
76 .iter()
77 .map(|setting| setting.key.to_string().chars().count())
78 .max()
79 .unwrap_or(0);
80 let values: Vec<String> = self
81 .settings
82 .iter()
83 .map(|setting| render_value(&setting.value))
84 .collect();
85 let value_width = values
89 .iter()
90 .map(|value| value.chars().count())
91 .filter(|width| *width <= VALUE_COLUMN_CAP)
92 .max()
93 .unwrap_or(0);
94
95 let mut rendered = String::new();
96 for (setting, value) in self.settings.iter().zip(values) {
97 rendered.push_str(&format!(
98 "{:key_width$} {:value_width$} {}\n",
99 setting.key.to_string(),
100 value,
101 setting.origin
102 ));
103 }
104
105 rendered.push_str(&match self.secrets.path.as_ref() {
106 Some(path) => format!("\nsecrets file {}\n", path.display()),
107 None => "\nsecrets file none — neither XDG_CONFIG_HOME nor HOME is set\n".to_owned(),
108 });
109 if self.secrets.variables.is_empty() {
110 rendered.push_str(" (it defines no variables, or is not there)\n");
111 }
112 let name_width = self
113 .secrets
114 .variables
115 .iter()
116 .map(|credential| credential.variable.as_str().chars().count())
117 .max()
118 .unwrap_or(0);
119 for credential in &self.secrets.variables {
120 rendered.push_str(&format!(
121 " {:name_width$} resolved from the {}\n",
122 credential.variable, credential.resolved_from
123 ));
124 }
125 rendered
126 }
127}
128
129const VALUE_COLUMN_CAP: usize = 44;
131
132fn render_value(value: &Value) -> String {
134 serde_json::to_string(value).expect("a value that was deserialized from JSON re-renders")
135}