Skip to main content

state_engine/load/
mod.rs

1// Load - 自動ロード実装
2//
3// _load 設定に従って各種ソースからデータをロードする。
4
5use crate::ports::required::{
6    DBClient, ENVClient, KVSClient,
7    InMemoryClient,
8};
9use serde_json::Value;
10use std::collections::HashMap;
11
12/// Load - 自動ロード専用
13///
14/// _load メタデータに従って各種clientからデータを取得する。
15/// 再帰制御は Resolver が担当。
16pub 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    /// 新しいLoadインスタンスを作成
25    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    /// DBClientを設定
35    pub fn with_db_client(mut self, client: &'a dyn DBClient) -> Self {
36        self.db_client = Some(client);
37        self
38    }
39
40    /// KVSClientを設定
41    pub fn with_kvs_client(mut self, client: &'a dyn KVSClient) -> Self {
42        self.kvs_client = Some(client);
43        self
44    }
45
46    /// InMemoryClientを設定
47    pub fn with_in_memory(mut self, client: &'a dyn InMemoryClient) -> Self {
48        self.in_memory = Some(client);
49        self
50    }
51
52    /// ENVClientを設定
53    pub fn with_env_client(mut self, client: &'a dyn ENVClient) -> Self {
54        self.env_client = Some(client);
55        self
56    }
57
58    /// placeholder 解決済みの config でデータをロード
59    ///
60    /// # Arguments
61    /// * `config` - 解決済み _load メタデータ
62    ///
63    /// # Returns
64    /// * `Ok(Value)` - ロード成功
65    /// * `Err(String)` - ロード失敗
66    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    /// 環境変数から読み込み
82    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    /// InMemoryから読み込み
109    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        // placeholder はすでに resolved_config で解決済み
123        in_memory
124            .get(key)
125            .ok_or_else(|| format!("Load::load_from_in_memory: key '{}' not found", key))
126    }
127
128    /// KVSから読み込み
129    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        // placeholder はすでに resolved_config で解決済み
143        let value_str = kvs_client
144            .get(key)
145            .ok_or_else(|| format!("Load::load_from_kvs: key '{}' not found", key))?;
146
147        // deserialize処理
148        // 全ての値はJSON形式で保存されている(型情報保持)
149        serde_json::from_str(&value_str)
150            .map_err(|e| format!("Load::load_from_kvs: JSON parse error: {}", e))
151    }
152
153    /// DBから読み込み
154    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        // connection 値を取得(String でも Object でも OK)
175        let connection = config
176            .get("connection")
177            .ok_or("Load::load_from_db: 'connection' not specified")?;
178
179        // map から SELECT カラムを抽出
180        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        // DB から取得(常に Vec で返る)
190        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        // 空チェック
195        if rows.is_empty() {
196            return Err(format!("Load::load_from_db: no data found in table '{}'", table));
197        }
198
199        // 1件目を取得(現状はレコード形式のみ対応)
200        let row = &rows[0];
201
202        // mapに従ってフィールドをマッピング
203        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    // feature function: load with API Cleint
216    // fn load_from_api(
217    //     &self,
218    //     config: &HashMap<String, Value>,
219    // ) -> Result<Value, String> {
220    //     let api_client = self
221    //         .api_client
222    //         .ok_or("Load::load_from_api: APIClient not configured")?;
223
224    //     let url = config
225    //         .get("url")
226    //         .and_then(|v| v.as_str())
227    //         .ok_or("Load::load_from_api: 'url' not found")?;
228
229    //     // placeholder はすでに resolved_config で解決済み
230
231    //     // headers処理(optional)
232    //     let headers = config.get("headers").and_then(|v| v.as_object()).map(|h| {
233    //         h.iter()
234    //             .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
235    //             .collect::<HashMap<String, String>>()
236    //     });
237
238    //     api_client.get(url, headers.as_ref())
239    // }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    // Mock ENVClient
247    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    // 再帰深度テストは State が管理するため削除
278    // Load は単純なデータ取得のみを担当
279}