1use crate::ports::required::{
2 DbClient, EnvClient, KVSClient,
3 InMemoryClient,
4};
5use crate::common::bit;
6use serde_json::Value;
7use std::collections::HashMap;
8
9pub struct Load<'a> {
10 db_client: Option<&'a dyn DbClient>,
11 kvs_client: Option<&'a dyn KVSClient>,
12 in_memory: Option<&'a dyn InMemoryClient>,
13 env_client: Option<&'a dyn EnvClient>,
14}
15
16impl<'a> Load<'a> {
17 pub fn new() -> Self {
18 Self {
19 db_client: None,
20 kvs_client: None,
21 in_memory: None,
22 env_client: None,
23 }
24 }
25
26 pub fn with_db_client(mut self, client: &'a dyn DbClient) -> Self {
27 self.db_client = Some(client);
28 self
29 }
30
31 pub fn with_kvs_client(mut self, client: &'a dyn KVSClient) -> Self {
32 self.kvs_client = Some(client);
33 self
34 }
35
36 pub fn with_in_memory(mut self, client: &'a dyn InMemoryClient) -> Self {
37 self.in_memory = Some(client);
38 self
39 }
40
41 pub fn with_env_client(mut self, client: &'a dyn EnvClient) -> Self {
42 self.env_client = Some(client);
43 self
44 }
45
46 pub fn handle(&self, config: &HashMap<String, Value>) -> Result<Value, String> {
47 let client = config
48 .get("client")
49 .and_then(|v| v.as_u64())
50 .ok_or("Load::handle: 'client' not found in _load config")?;
51
52 match client {
53 bit::CLIENT_ENV => self.load_from_env(config),
54 bit::CLIENT_IN_MEMORY => self.load_from_in_memory(config),
55 bit::CLIENT_KVS => self.load_from_kvs(config),
56 bit::CLIENT_DB => self.load_from_db(config),
57 _ => Err(format!("Load::handle: unsupported client '{}'", client)),
58 }
59 }
60
61 fn load_from_env(
62 &self,
63 config: &HashMap<String, Value>,
64 ) -> Result<Value, String> {
65 let env_client = self
66 .env_client
67 .ok_or("Load::load_from_env: EnvClient not configured")?;
68
69 let map = config
70 .get("map")
71 .and_then(|v| v.as_object())
72 .ok_or("Load::load_from_env: 'map' not found")?;
73
74 let mut result = serde_json::Map::new();
75
76 for (config_key, env_key_value) in map {
77 if let Some(env_key) = env_key_value.as_str() {
78 if let Some(value) = env_client.get(env_key) {
79 result.insert(config_key.clone(), Value::String(value));
80 }
81 }
82 }
83
84 Ok(Value::Object(result))
85 }
86
87 fn load_from_in_memory(
88 &self,
89 config: &HashMap<String, Value>,
90 ) -> Result<Value, String> {
91 let in_memory = self
92 .in_memory
93 .ok_or("Load::load_from_in_memory: InMemoryClient not configured")?;
94
95 let key = config
96 .get("key")
97 .and_then(|v| v.as_str())
98 .ok_or("Load::load_from_in_memory: 'key' not found")?;
99
100 in_memory
101 .get(key)
102 .ok_or_else(|| format!("Load::load_from_in_memory: key '{}' not found", key))
103 }
104
105 fn load_from_kvs(
106 &self,
107 config: &HashMap<String, Value>,
108 ) -> Result<Value, String> {
109 let kvs_client = self
110 .kvs_client
111 .ok_or("Load::load_from_kvs: KVSClient not configured")?;
112
113 let key = config
114 .get("key")
115 .and_then(|v| v.as_str())
116 .ok_or("Load::load_from_kvs: 'key' not found")?;
117
118 let value_str = kvs_client
119 .get(key)
120 .ok_or_else(|| format!("Load::load_from_kvs: key '{}' not found", key))?;
121
122 serde_json::from_str(&value_str)
123 .map_err(|e| format!("Load::load_from_kvs: JSON parse error: {}", e))
124 }
125
126 fn load_from_db(
127 &self,
128 config: &HashMap<String, Value>,
129 ) -> Result<Value, String> {
130 let db_client = self
131 .db_client
132 .ok_or("Load::load_from_db: DbClient not configured")?;
133
134 let table = config
135 .get("table")
136 .and_then(|v| v.as_str())
137 .ok_or("Load::load_from_db: 'table' not found")?;
138
139 let where_clause = config.get("where").and_then(|v| v.as_str());
140
141 let map = config
142 .get("map")
143 .and_then(|v| v.as_object())
144 .ok_or("Load::load_from_db: 'map' not found")?;
145
146 let connection = config
147 .get("connection")
148 .ok_or("Load::load_from_db: 'connection' not specified")?;
149
150 let columns: Vec<&str> = map
151 .values()
152 .filter_map(|v| v.as_str())
153 .collect();
154
155 if columns.is_empty() {
156 return Err("Load::load_from_db: no columns specified in map".to_string());
157 }
158
159 let rows = db_client
160 .fetch(connection, table, &columns, where_clause)
161 .ok_or_else(|| format!("Load::load_from_db: fetch failed for table '{}'", table))?;
162
163 if rows.is_empty() {
164 return Err(format!("Load::load_from_db: no data found in table '{}'", table));
165 }
166
167 let row = &rows[0];
168
169 let mut result = serde_json::Map::new();
170 for (config_key, db_column_value) in map {
171 if let Some(db_column) = db_column_value.as_str() {
172 if let Some(value) = row.get(db_column) {
173 result.insert(config_key.clone(), value.clone());
174 }
175 }
176 }
177
178 Ok(Value::Object(result))
179 }
180
181 }
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 struct MockEnvClient;
213 impl EnvClient for MockEnvClient {
214 fn get(&self, key: &str) -> Option<String> {
215 match key {
216 "Db_HOST" => Some("localhost".to_string()),
217 "Db_PORT" => Some("5432".to_string()),
218 _ => None,
219 }
220 }
221 }
222
223 #[test]
224 fn test_load_from_env() {
225 let env_client = MockEnvClient;
226 let load = Load::new().with_env_client(&env_client);
227
228 let mut config = HashMap::new();
229 config.insert("client".to_string(), Value::Number(bit::CLIENT_ENV.into()));
230
231 let mut map = serde_json::Map::new();
232 map.insert("host".to_string(), Value::String("Db_HOST".to_string()));
233 map.insert("port".to_string(), Value::String("Db_PORT".to_string()));
234 config.insert("map".to_string(), Value::Object(map));
235
236 let result = load.handle(&config).unwrap();
237
238 assert_eq!(result.get("host"), Some(&Value::String("localhost".to_string())));
239 assert_eq!(result.get("port"), Some(&Value::String("5432".to_string())));
240 }
241
242}