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 ("interactive", Value::Bool(config.interactive())),
47 (
48 "default_sources",
49 serde_json::to_value(config.selected_sources())
50 .expect("source names render as JSON"),
51 ),
52 ] {
53 let key = SettingPath::parse(key).expect("a literal path with no empty segment");
54 if !settings.iter().any(|setting| setting.key == key) {
55 settings.push(Setting {
56 key,
57 value,
58 origin: Origin::Default,
59 });
60 }
61 }
62
63 settings.sort_by(|left, right| left.key.cmp(&right.key));
64 Self { settings, secrets }
65 }
66
67 #[must_use]
74 pub fn render_text(&self) -> String {
75 let key_width = self
76 .settings
77 .iter()
78 .map(|setting| setting.key.to_string().chars().count())
79 .max()
80 .unwrap_or(0);
81 let values: Vec<String> = self
82 .settings
83 .iter()
84 .map(|setting| render_value(&setting.value))
85 .collect();
86 let value_width = values
90 .iter()
91 .map(|value| value.chars().count())
92 .filter(|width| *width <= VALUE_COLUMN_CAP)
93 .max()
94 .unwrap_or(0);
95
96 let mut rendered = String::new();
97 for (setting, value) in self.settings.iter().zip(values) {
98 rendered.push_str(&format!(
99 "{:key_width$} {:value_width$} {}\n",
100 setting.key.to_string(),
101 value,
102 setting.origin
103 ));
104 }
105
106 rendered.push_str(&match self.secrets.path.as_ref() {
107 Some(path) => format!("\nsecrets file {}\n", path.display()),
108 None => "\nsecrets file none — neither XDG_CONFIG_HOME nor HOME is set\n".to_owned(),
109 });
110 if self.secrets.variables.is_empty() {
111 rendered.push_str(" (it defines no variables, or is not there)\n");
112 }
113 let name_width = self
114 .secrets
115 .variables
116 .iter()
117 .map(|credential| credential.variable.as_str().chars().count())
118 .max()
119 .unwrap_or(0);
120 for credential in &self.secrets.variables {
121 rendered.push_str(&format!(
122 " {:name_width$} resolved from the {}\n",
123 credential.variable, credential.resolved_from
124 ));
125 }
126 rendered
127 }
128}
129
130const VALUE_COLUMN_CAP: usize = 44;
132
133fn render_value(value: &Value) -> String {
135 serde_json::to_string(value).expect("a value that was deserialized from JSON re-renders")
136}