use schemars::JsonSchema;
use serde::Serialize;
use serde_json::Value;
use crate::secrets::SecretsReport;
use super::Config;
use super::layer::{Merged, Origin, Setting, SettingPath};
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
pub struct EffectiveConfig {
pub settings: Vec<Setting>,
pub secrets: SecretsReport,
}
impl EffectiveConfig {
#[must_use]
pub fn new(merged: &Merged, config: &Config, secrets: SecretsReport) -> Self {
let mut settings: Vec<Setting> = merged.values().cloned().collect();
for (key, value) in [
("page_size", Value::from(config.page_size().get())),
(
"output",
serde_json::to_value(config.output()).expect("an output format renders as JSON"),
),
(
"default_sources",
serde_json::to_value(config.selected_sources())
.expect("source names render as JSON"),
),
] {
let key = SettingPath::parse(key).expect("a literal path with no empty segment");
if !settings.iter().any(|setting| setting.key == key) {
settings.push(Setting {
key,
value,
origin: Origin::Default,
});
}
}
settings.sort_by(|left, right| left.key.cmp(&right.key));
Self { settings, secrets }
}
#[must_use]
pub fn render_text(&self) -> String {
let key_width = self
.settings
.iter()
.map(|setting| setting.key.to_string().chars().count())
.max()
.unwrap_or(0);
let values: Vec<String> = self
.settings
.iter()
.map(|setting| render_value(&setting.value))
.collect();
let value_width = values
.iter()
.map(|value| value.chars().count())
.filter(|width| *width <= VALUE_COLUMN_CAP)
.max()
.unwrap_or(0);
let mut rendered = String::new();
for (setting, value) in self.settings.iter().zip(values) {
rendered.push_str(&format!(
"{:key_width$} {:value_width$} {}\n",
setting.key.to_string(),
value,
setting.origin
));
}
rendered.push_str(&match self.secrets.path.as_ref() {
Some(path) => format!("\nsecrets file {}\n", path.display()),
None => "\nsecrets file none — neither XDG_CONFIG_HOME nor HOME is set\n".to_owned(),
});
if self.secrets.variables.is_empty() {
rendered.push_str(" (it defines no variables, or is not there)\n");
}
let name_width = self
.secrets
.variables
.iter()
.map(|credential| credential.variable.as_str().chars().count())
.max()
.unwrap_or(0);
for credential in &self.secrets.variables {
rendered.push_str(&format!(
" {:name_width$} resolved from the {}\n",
credential.variable, credential.resolved_from
));
}
rendered
}
}
const VALUE_COLUMN_CAP: usize = 44;
fn render_value(value: &Value) -> String {
serde_json::to_string(value).expect("a value that was deserialized from JSON re-renders")
}