1use super::client::DiscourseClient;
2use super::error::http_error;
3use anyhow::{anyhow, Context, Result};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SiteSettingDetail {
11 pub setting: String,
12 #[serde(default)]
13 pub value: Value,
14 #[serde(default)]
15 pub default: Value,
16 #[serde(default)]
17 pub description: String,
18 #[serde(default)]
19 pub category: String,
20 #[serde(rename = "type", default)]
22 pub setting_type: String,
23}
24
25impl DiscourseClient {
26 pub fn update_site_setting(&self, setting: &str, value: &str) -> Result<()> {
28 let setting = setting.trim();
29 if setting.is_empty() {
30 return Err(anyhow!("missing site setting name for site setting update"));
31 }
32 if setting.chars().any(|ch| ch.is_whitespace() || ch == '/') {
33 return Err(anyhow!(
34 "site setting name contains invalid characters: {}",
35 setting
36 ));
37 }
38 let path = format!("/admin/site_settings/{}.json", setting);
39 let payload = [("value", value)];
40 let response = self.send_retrying(|| Ok(self.put(&path)?.form(&payload)))?;
41 let status = response.status();
42 let text = response
43 .text()
44 .context("reading site setting update response")?;
45 if !status.is_success() {
46 return Err(http_error("update site setting request", status, &text));
47 }
48 Ok(())
49 }
50
51 pub fn list_site_settings(&self) -> Result<Value> {
53 let response = self.get("/admin/site_settings.json")?;
54 let status = response.status();
55 let text = response
56 .text()
57 .context("reading site settings list response")?;
58 if !status.is_success() {
59 return Err(http_error("list site settings request", status, &text));
60 }
61 let value: Value =
62 serde_json::from_str(&text).context("parsing site settings list response")?;
63 Ok(value)
64 }
65
66 pub fn list_site_settings_detailed(&self) -> Result<Vec<SiteSettingDetail>> {
70 let raw = self.list_site_settings()?;
71 let arr = raw
72 .get("site_settings")
73 .and_then(|v| v.as_array())
74 .ok_or_else(|| anyhow!("site_settings response missing 'site_settings' array"))?;
75 let mut out = Vec::with_capacity(arr.len());
76 for entry in arr {
77 let detail: SiteSettingDetail = serde_json::from_value(entry.clone())
78 .with_context(|| {
79 format!(
80 "parsing site setting entry: {}",
81 entry.get("setting").and_then(|v| v.as_str()).unwrap_or("?")
82 )
83 })?;
84 out.push(detail);
85 }
86 Ok(out)
87 }
88
89 pub fn fetch_site_setting(&self, setting: &str) -> Result<String> {
92 let setting = setting.trim();
93 if setting.is_empty() {
94 return Err(anyhow!("missing site setting name"));
95 }
96 let all = self.list_site_settings()?;
98 let settings = all
100 .get("site_settings")
101 .and_then(|v| v.as_array())
102 .cloned()
103 .unwrap_or_default();
104 for entry in &settings {
105 let name = entry.get("setting").and_then(|v| v.as_str()).unwrap_or("");
106 if name == setting {
107 let value = entry.get("value").cloned().unwrap_or(Value::Null);
108 let display = match &value {
109 Value::String(s) => s.clone(),
110 Value::Null => String::new(),
111 other => other.to_string(),
112 };
113 return Ok(display);
114 }
115 }
116 Err(anyhow!("setting not found: {}", setting))
117 }
118}