oboron_cli_core/
config.rs1use anyhow::{anyhow, bail, Context, Result};
14use serde_json::Value;
15use std::fs;
16use std::path::PathBuf;
17
18use crate::paths::config_path;
19
20#[derive(Debug, Default, Clone, PartialEq, Eq)]
21pub struct Config {
22 pub profile: Option<String>,
24 pub scheme: Option<String>,
26 pub encoding: Option<String>,
28}
29
30pub fn load_config() -> Result<Option<Config>> {
32 let path = config_path()?;
33 if !path.exists() {
34 return Ok(None);
35 }
36 let v = read_json(&path)?;
37 let obj = v
38 .as_object()
39 .ok_or_else(|| anyhow!("{} is not a JSON object", path.display()))?;
40 Ok(Some(Config {
41 profile: obj.get("profile").and_then(Value::as_str).map(str::to_string),
42 scheme: obj.get("scheme").and_then(Value::as_str).map(str::to_string),
43 encoding: obj.get("encoding").and_then(Value::as_str).map(str::to_string),
44 }))
45}
46
47pub fn save_config(cfg: &Config) -> Result<()> {
49 let path = config_path()?;
50 let mut v = read_json_or_empty(&path)?;
51 let obj = v
52 .as_object_mut()
53 .ok_or_else(|| anyhow!("{} is not a JSON object", path.display()))?;
54 if let Some(p) = &cfg.profile {
55 obj.insert("profile".into(), Value::String(p.clone()));
56 }
57 if let Some(s) = &cfg.scheme {
58 obj.insert("scheme".into(), Value::String(s.clone()));
59 }
60 if let Some(e) = &cfg.encoding {
61 obj.insert("encoding".into(), Value::String(e.clone()));
62 }
63 write_json(&path, &v)
64}
65
66pub(crate) fn read_json(path: &PathBuf) -> Result<Value> {
71 let body = fs::read_to_string(path)
72 .with_context(|| format!("read {}", path.display()))?;
73 let v: Value = serde_json::from_str(&body)
74 .with_context(|| format!("parse {}", path.display()))?;
75 if !v.is_object() {
76 bail!("{} is not a JSON object", path.display());
77 }
78 Ok(v)
79}
80
81pub(crate) fn read_json_or_empty(path: &PathBuf) -> Result<Value> {
82 if !path.exists() {
83 return Ok(Value::Object(serde_json::Map::new()));
84 }
85 read_json(path)
86}
87
88pub(crate) fn write_json(path: &PathBuf, value: &Value) -> Result<()> {
89 if let Some(parent) = path.parent() {
90 fs::create_dir_all(parent).context("create parent directory")?;
91 }
92 let pretty = serde_json::to_string_pretty(value).context("serialize JSON")?;
93 fs::write(path, pretty).with_context(|| format!("write {}", path.display()))?;
94 #[cfg(unix)]
95 {
96 use std::os::unix::fs::PermissionsExt;
97 let mut perms = fs::metadata(path)?.permissions();
98 perms.set_mode(0o600);
99 fs::set_permissions(path, perms)?;
100 }
101 Ok(())
102}