use std::fmt;
use crate::settings::{Entry, Scope, SettingDef, Value};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ConfigListParams {
#[serde(default)]
pub scope: Option<Scope>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigGetParams {
pub key: String,
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct ConfigEntry {
pub key: String,
pub value: Value,
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct ConfigSetParams {
#[serde(default)]
pub scope: Option<Scope>,
pub entries: Vec<ConfigEntry>,
#[serde(default)]
pub dry_run: bool,
}
impl fmt::Debug for ConfigEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConfigEntry")
.field("key", &self.key)
.field("value", &format_args!("<secret>"))
.finish()
}
}
impl fmt::Debug for ConfigSetParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConfigSetParams")
.field("scope", &self.scope)
.field("entries", &self.entries)
.field("dry_run", &self.dry_run)
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigFieldError {
pub key: String,
pub message: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigFieldErrors {
pub fields: Vec<ConfigFieldError>,
}
impl ConfigFieldErrors {
pub fn from_data(data: Option<&serde_json::Value>) -> Option<Self> {
let errors: Self = serde_json::from_value(data?.clone()).ok()?;
(!errors.fields.is_empty()).then_some(errors)
}
pub fn to_data(&self) -> serde_json::Value {
serde_json::to_value(self).expect("field errors are plain strings")
}
pub fn lines(&self) -> impl Iterator<Item = String> + '_ {
self.fields
.iter()
.map(|f| format!("{}: {}", f.key, f.message))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigUnsetParams {
#[serde(default)]
pub scope: Option<Scope>,
pub keys: Vec<String>,
#[serde(default)]
pub dry_run: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ConfigSchemaResult {
pub settings: Vec<SettingDef>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ConfigListResult {
pub entries: Vec<Entry>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigWriteResult {
pub changed: Vec<String>,
pub pending_restart: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_set_params_never_print_values() {
let params = ConfigSetParams {
scope: None,
entries: vec![ConfigEntry {
key: "embed.remote.key".into(),
value: Value::String("sk-hunter2".into()),
}],
dry_run: false,
};
let shown = format!("{params:?}");
assert!(!shown.contains("hunter2"), "{shown}");
assert!(shown.contains("<secret>") && shown.contains("embed.remote.key"));
}
#[test]
fn set_params_round_trip() {
let params = ConfigSetParams {
scope: Some(Scope::Device),
entries: vec![ConfigEntry {
key: "net.discovery".into(),
value: Value::Bool(false),
}],
dry_run: false,
};
let json = serde_json::to_string(¶ms).unwrap();
assert_eq!(
serde_json::from_str::<ConfigSetParams>(&json).unwrap(),
params
);
}
#[test]
fn dry_run_defaults_to_false() {
let set: ConfigSetParams = serde_json::from_str(r#"{"entries":[]}"#).unwrap();
assert!(!set.dry_run);
let unset: ConfigUnsetParams = serde_json::from_str(r#"{"keys":[]}"#).unwrap();
assert!(!unset.dry_run);
}
}