use super::client::DiscourseClient;
use super::error::http_error;
use anyhow::{anyhow, Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteSettingDetail {
pub setting: String,
#[serde(default)]
pub value: Value,
#[serde(default)]
pub default: Value,
#[serde(default)]
pub description: String,
#[serde(default)]
pub category: String,
#[serde(rename = "type", default)]
pub setting_type: String,
}
impl DiscourseClient {
pub fn update_site_setting(&self, setting: &str, value: &str) -> Result<()> {
let setting = setting.trim();
if setting.is_empty() {
return Err(anyhow!("missing site setting name for site setting update"));
}
if setting.chars().any(|ch| ch.is_whitespace() || ch == '/') {
return Err(anyhow!(
"site setting name contains invalid characters: {}",
setting
));
}
let path = format!("/admin/site_settings/{}.json", setting);
let payload = [("value", value)];
let response = self.send_retrying(|| Ok(self.put(&path)?.form(&payload)))?;
let status = response.status();
let text = response
.text()
.context("reading site setting update response")?;
if !status.is_success() {
return Err(http_error("update site setting request", status, &text));
}
Ok(())
}
pub fn list_site_settings(&self) -> Result<Value> {
let response = self.get("/admin/site_settings.json")?;
let status = response.status();
let text = response
.text()
.context("reading site settings list response")?;
if !status.is_success() {
return Err(http_error("list site settings request", status, &text));
}
let value: Value =
serde_json::from_str(&text).context("parsing site settings list response")?;
Ok(value)
}
pub fn list_site_settings_detailed(&self) -> Result<Vec<SiteSettingDetail>> {
let raw = self.list_site_settings()?;
let arr = raw
.get("site_settings")
.and_then(|v| v.as_array())
.ok_or_else(|| anyhow!("site_settings response missing 'site_settings' array"))?;
let mut out = Vec::with_capacity(arr.len());
for entry in arr {
let detail: SiteSettingDetail = serde_json::from_value(entry.clone())
.with_context(|| {
format!(
"parsing site setting entry: {}",
entry.get("setting").and_then(|v| v.as_str()).unwrap_or("?")
)
})?;
out.push(detail);
}
Ok(out)
}
pub fn fetch_site_setting(&self, setting: &str) -> Result<String> {
let setting = setting.trim();
if setting.is_empty() {
return Err(anyhow!("missing site setting name"));
}
let all = self.list_site_settings()?;
let settings = all
.get("site_settings")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
for entry in &settings {
let name = entry.get("setting").and_then(|v| v.as_str()).unwrap_or("");
if name == setting {
let value = entry.get("value").cloned().unwrap_or(Value::Null);
let display = match &value {
Value::String(s) => s.clone(),
Value::Null => String::new(),
other => other.to_string(),
};
return Ok(display);
}
}
Err(anyhow!("setting not found: {}", setting))
}
}