1use std::collections::BTreeMap;
2use std::path::Path;
3
4use diode::{Extract, StdError};
5use serde::{Deserialize, Serialize, de::DeserializeOwned};
6
7#[derive(Default, Serialize, Deserialize)]
8pub struct Config {
9 #[serde(flatten)]
10 pub(crate) configs: BTreeMap<String, serde_json::Value>,
11}
12
13pub trait ConfigSection: DeserializeOwned {
14 fn key() -> &'static str;
15}
16
17impl Config {
18 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn get<T>(&self, name: impl AsRef<str>) -> Result<T, StdError>
23 where
24 T: DeserializeOwned,
25 {
26 Ok(serde_json::from_value(
27 self.configs
28 .get(name.as_ref())
29 .cloned()
30 .unwrap_or(serde_json::Value::Null),
31 )?)
32 }
33
34 pub fn set<T>(&mut self, name: impl Into<String>, value: T) -> Result<(), StdError>
35 where
36 T: Serialize,
37 {
38 self.configs
39 .insert(name.into(), serde_json::to_value(value)?);
40 Ok(())
41 }
42
43 pub fn with<T>(mut self, name: impl Into<String>, value: T) -> Self
44 where
45 T: Serialize,
46 {
47 self.configs
48 .insert(name.into(), serde_json::to_value(value).unwrap());
49 self
50 }
51
52 pub fn merge_from(&mut self, other: Self) -> Result<(), StdError> {
53 for (key, value) in other.configs {
54 let entry = self.configs.entry(key);
55 merge_json_from(entry.or_insert(serde_json::Value::Null), value)?;
56 }
57 Ok(())
58 }
59
60 pub fn parse<T>(text: T) -> Result<Self, StdError>
61 where
62 T: AsRef<str>,
63 {
64 Ok(serde_json::from_str(text.as_ref())?)
65 }
66
67 pub async fn parse_file(path: impl AsRef<Path>) -> Result<Self, StdError> {
68 let text = tokio::fs::read_to_string(path).await?;
69 Self::parse(text)
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.configs.is_empty()
75 }
76
77 pub fn len(&self) -> usize {
79 self.configs.len()
80 }
81}
82
83impl<T> Extract<T> for Config
84where
85 T: ConfigSection,
86{
87 fn extract(ctx: &diode::AppContext) -> Result<T, diode::AppError> {
88 Ok(ctx
89 .get_component_ref::<Config>()
90 .unwrap()
91 .get::<T>(T::key())
92 .unwrap())
93 }
94}
95
96fn merge_json_from(lhs: &mut serde_json::Value, rhs: serde_json::Value) -> Result<(), StdError> {
97 match lhs {
98 serde_json::Value::Object(l) => match rhs {
99 serde_json::Value::Object(r) => {
100 for (key, value) in r {
101 let entry = l.entry(key);
102 merge_json_from(entry.or_insert(serde_json::Value::Null), value)?;
103 }
104 }
105 _ => *lhs = rhs,
106 },
107 serde_json::Value::Array(l) => match rhs {
108 serde_json::Value::Array(r) => {
109 l.extend(r);
110 }
111 _ => *lhs = rhs,
112 },
113 _ => *lhs = rhs,
114 }
115 Ok(())
116}