1use crate::ports::required::{
6 DBClient, ENVClient, KVSClient,
7 InMemoryClient,
8};
9use serde_json::Value;
10use std::collections::HashMap;
11
12pub struct Load<'a> {
17 db_client: Option<&'a dyn DBClient>,
18 kvs_client: Option<&'a dyn KVSClient>,
19 in_memory: Option<&'a dyn InMemoryClient>,
20 env_client: Option<&'a dyn ENVClient>,
21}
22
23impl<'a> Load<'a> {
24 pub fn new() -> Self {
26 Self {
27 db_client: None,
28 kvs_client: None,
29 in_memory: None,
30 env_client: None,
31 }
32 }
33
34 pub fn with_db_client(mut self, client: &'a dyn DBClient) -> Self {
36 self.db_client = Some(client);
37 self
38 }
39
40 pub fn with_kvs_client(mut self, client: &'a dyn KVSClient) -> Self {
42 self.kvs_client = Some(client);
43 self
44 }
45
46 pub fn with_in_memory(mut self, client: &'a dyn InMemoryClient) -> Self {
48 self.in_memory = Some(client);
49 self
50 }
51
52 pub fn with_env_client(mut self, client: &'a dyn ENVClient) -> Self {
54 self.env_client = Some(client);
55 self
56 }
57
58 pub fn handle(&self, config: &HashMap<String, Value>) -> Result<Value, String> {
67 let client = config
68 .get("client")
69 .and_then(|v| v.as_str())
70 .ok_or("Load::handle: 'client' not found in _load config")?;
71
72 match client {
73 "Env" | "ENV" => self.load_from_env(config),
74 "InMemory" => self.load_from_in_memory(config),
75 "KVS" => self.load_from_kvs(config),
76 "DB" => self.load_from_db(config),
77 _ => Err(format!("Load::handle: unsupported client '{}'", client)),
78 }
79 }
80
81 fn load_from_env(
83 &self,
84 config: &HashMap<String, Value>,
85 ) -> Result<Value, String> {
86 let env_client = self
87 .env_client
88 .ok_or("Load::load_from_env: ENVClient not configured")?;
89
90 let map = config
91 .get("map")
92 .and_then(|v| v.as_object())
93 .ok_or("Load::load_from_env: 'map' not found")?;
94
95 let mut result = serde_json::Map::new();
96
97 for (config_key, env_key_value) in map {
98 if let Some(env_key) = env_key_value.as_str() {
99 if let Some(value) = env_client.get(env_key) {
100 result.insert(config_key.clone(), Value::String(value));
101 }
102 }
103 }
104
105 Ok(Value::Object(result))
106 }
107
108 fn load_from_in_memory(
110 &self,
111 config: &HashMap<String, Value>,
112 ) -> Result<Value, String> {
113 let in_memory = self
114 .in_memory
115 .ok_or("Load::load_from_in_memory: InMemoryClient not configured")?;
116
117 let key = config
118 .get("key")
119 .and_then(|v| v.as_str())
120 .ok_or("Load::load_from_in_memory: 'key' not found")?;
121
122 in_memory
124 .get(key)
125 .ok_or_else(|| format!("Load::load_from_in_memory: key '{}' not found", key))
126 }
127
128 fn load_from_kvs(
130 &self,
131 config: &HashMap<String, Value>,
132 ) -> Result<Value, String> {
133 let kvs_client = self
134 .kvs_client
135 .ok_or("Load::load_from_kvs: KVSClient not configured")?;
136
137 let key = config
138 .get("key")
139 .and_then(|v| v.as_str())
140 .ok_or("Load::load_from_kvs: 'key' not found")?;
141
142 let value_str = kvs_client
144 .get(key)
145 .ok_or_else(|| format!("Load::load_from_kvs: key '{}' not found", key))?;
146
147 serde_json::from_str(&value_str)
150 .map_err(|e| format!("Load::load_from_kvs: JSON parse error: {}", e))
151 }
152
153 fn load_from_db(
155 &self,
156 config: &HashMap<String, Value>,
157 ) -> Result<Value, String> {
158 let db_client = self
159 .db_client
160 .ok_or("Load::load_from_db: DBClient not configured")?;
161
162 let table = config
163 .get("table")
164 .and_then(|v| v.as_str())
165 .ok_or("Load::load_from_db: 'table' not found")?;
166
167 let where_clause = config.get("where").and_then(|v| v.as_str());
168
169 let map = config
170 .get("map")
171 .and_then(|v| v.as_object())
172 .ok_or("Load::load_from_db: 'map' not found")?;
173
174 let connection = config
176 .get("connection")
177 .ok_or("Load::load_from_db: 'connection' not specified")?;
178
179 let columns: Vec<&str> = map
181 .values()
182 .filter_map(|v| v.as_str())
183 .collect();
184
185 if columns.is_empty() {
186 return Err("Load::load_from_db: no columns specified in map".to_string());
187 }
188
189 let rows = db_client
191 .fetch(connection, table, &columns, where_clause)
192 .ok_or_else(|| format!("Load::load_from_db: fetch failed for table '{}'", table))?;
193
194 if rows.is_empty() {
196 return Err(format!("Load::load_from_db: no data found in table '{}'", table));
197 }
198
199 let row = &rows[0];
201
202 let mut result = serde_json::Map::new();
204 for (config_key, db_column_value) in map {
205 if let Some(db_column) = db_column_value.as_str() {
206 if let Some(value) = row.get(db_column) {
207 result.insert(config_key.clone(), value.clone());
208 }
209 }
210 }
211
212 Ok(Value::Object(result))
213 }
214
215 }
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 struct MockENVClient;
248 impl ENVClient for MockENVClient {
249 fn get(&self, key: &str) -> Option<String> {
250 match key {
251 "DB_HOST" => Some("localhost".to_string()),
252 "DB_PORT" => Some("5432".to_string()),
253 _ => None,
254 }
255 }
256 }
257
258 #[test]
259 fn test_load_from_env() {
260 let env_client = MockENVClient;
261 let load = Load::new().with_env_client(&env_client);
262
263 let mut config = HashMap::new();
264 config.insert("client".to_string(), Value::String("Env".to_string()));
265
266 let mut map = serde_json::Map::new();
267 map.insert("host".to_string(), Value::String("DB_HOST".to_string()));
268 map.insert("port".to_string(), Value::String("DB_PORT".to_string()));
269 config.insert("map".to_string(), Value::Object(map));
270
271 let result = load.handle(&config).unwrap();
272
273 assert_eq!(result.get("host"), Some(&Value::String("localhost".to_string())));
274 assert_eq!(result.get("port"), Some(&Value::String("5432".to_string())));
275 }
276
277 }