1use serde_json::Value;
2use std::collections::{HashMap, HashSet};
3use std::path::PathBuf;
4use crate::core::fixed_bits;
5use crate::core::manifest::{Manifest, ConfigValue};
6use crate::core::parser::{Value as ParseValue, parse};
7use crate::ports::provided::{ManifestError, StateError};
8use crate::ports::required::FileClient;
9use crate::store::Store;
10use crate::load::Load;
11
12use std::sync::Arc;
13
14pub struct State {
15 manifest_dir: PathBuf,
16 manifest_file: Box<dyn FileClient>,
17 manifest: Manifest,
18 state_keys: Vec<u16>,
19 state_vals: Vec<Value>,
20 store: Store,
21 load: Load,
22 max_recursion: usize,
23 called_keys: HashSet<String>,
24}
25
26impl State {
27 pub fn new(manifest_dir: &str) -> Self {
37 Self {
38 manifest_dir: PathBuf::from(manifest_dir),
39 manifest_file: Box::new(crate::ports::default::DefaultFileClient),
40 manifest: Manifest::new(),
41 state_keys: vec![0],
42 state_vals: vec![Value::Null],
43 store: Store::new(),
44 load: Load::new(),
45 max_recursion: 20,
46 called_keys: HashSet::new(),
47 }
48 }
49
50 pub fn with_in_memory(mut self, client: Arc<dyn crate::ports::required::InMemoryClient>) -> Self {
51 self.store = self.store.with_in_memory(Arc::clone(&client));
52 self.load = self.load.with_in_memory(client);
53 self
54 }
55
56 pub fn with_kvs(mut self, client: Arc<dyn crate::ports::required::KVSClient>) -> Self {
57 self.store = self.store.with_kvs(Arc::clone(&client));
58 self.load = self.load.with_kvs(client);
59 self
60 }
61
62 pub fn with_db(mut self, client: Arc<dyn crate::ports::required::DbClient>) -> Self {
63 self.load = self.load.with_db(client);
64 self
65 }
66
67 pub fn with_env(mut self, client: Arc<dyn crate::ports::required::EnvClient>) -> Self {
68 self.load = self.load.with_env(client);
69 self
70 }
71
72 pub fn with_http(mut self, client: Arc<dyn crate::ports::required::HttpClient>) -> Self {
73 self.store = self.store.with_http(Arc::clone(&client));
74 self.load = self.load.with_http(client);
75 self
76 }
77
78 pub fn with_file(mut self, client: Arc<dyn crate::ports::required::FileClient>) -> Self {
79 self.store = self.store.with_file(Arc::clone(&client));
80 self.load = self.load.with_file(client);
81 self
82 }
83
84 pub fn with_manifest_file(mut self, client: impl FileClient + 'static) -> Self {
85 self.manifest_file = Box::new(client);
86 self
87 }
88
89 fn load_manifest(&mut self, file: &str) -> Result<(), ManifestError> {
90 crate::fn_log!("State", "load_manifest", file);
91 if self.manifest.is_loaded(file) {
92 return Ok(());
93 }
94
95 let yml_path = self.manifest_dir.join(format!("{}.yml", file));
96 let yaml_path = self.manifest_dir.join(format!("{}.yaml", file));
97 let yml_key = yml_path.to_string_lossy();
98 let yaml_key = yaml_path.to_string_lossy();
99 let yml_content = self.manifest_file.get(&yml_key);
100 let yaml_content = self.manifest_file.get(&yaml_key);
101
102 let content = match (yml_content, yaml_content) {
103 (Some(_), Some(_)) => return Err(ManifestError::AmbiguousFile(
104 format!("both '{}.yml' and '{}.yaml' exist.", file, file)
105 )),
106 (Some(c), None) => c,
107 (None, Some(c)) => c,
108 (None, None) => return Err(ManifestError::FileNotFound(
109 format!("'{}.yml' or '{}.yaml'", file, file)
110 )),
111 };
112
113 let yaml_root: serde_yaml_ng::Value = serde_yaml_ng::from_str(&content)
114 .map_err(|e| ManifestError::ParseError(format!("YAML parse error: {}", e)))?;
115
116 let pm = parse(
117 file,
118 yaml_to_parse_value(yaml_root),
119 &mut self.manifest.dynamic,
120 &mut self.manifest.keys,
121 &mut self.manifest.values,
122 &mut self.manifest.path_map,
123 &mut self.manifest.children_map,
124 ).map_err(|e| ManifestError::ParseError(e))?;
125
126 self.manifest.insert(file.to_string(), pm);
127 Ok(())
128 }
129
130 fn split_key<'k>(key: &'k str) -> (&'k str, &'k str) {
131 match key.find('.') {
132 Some(pos) => (&key[..pos], &key[pos + 1..]),
133 None => (key, ""),
134 }
135 }
136
137 fn find_state_value(&self, key_idx: u16) -> Option<usize> {
138 self.state_keys.iter().skip(1).position(|&k| k == key_idx).map(|p| p + 1)
139 }
140
141 fn resolve_template(&mut self, template: &str) -> Result<Option<String>, StateError> {
143 let mut result = String::new();
144 let mut remaining = template;
145 while let Some(start) = remaining.find("${") {
146 result.push_str(&remaining[..start]);
147 remaining = &remaining[start + 2..];
148 let end = match remaining.find('}') {
149 Some(e) => e,
150 None => return Ok(None),
151 };
152 let path = &remaining[..end];
153 remaining = &remaining[end + 1..];
154 let resolved = match self.get(path)? {
155 Some(Value::String(s)) => s,
156 Some(Value::Number(n)) => n.to_string(),
157 Some(Value::Bool(b)) => b.to_string(),
158 _ => return Ok(None),
159 };
160 result.push_str(&resolved);
161 }
162 result.push_str(remaining);
163 Ok(Some(result))
164 }
165
166 fn resolve_config_value(&mut self, cv: ConfigValue) -> Result<Option<Value>, StateError> {
171 match cv {
172 ConfigValue::Client(c) => Ok(Some(Value::Number(c.into()))),
173 ConfigValue::Placeholder(path) => self.get(&path),
174 ConfigValue::Str(s) if s.contains("${") => {
175 Ok(self.resolve_template(&s)?.map(Value::String))
176 }
177 ConfigValue::Str(s) => Ok(Some(Value::String(s))),
178 ConfigValue::Map(pairs) => {
179 let mut map = serde_json::Map::new();
180 for (k, v) in pairs {
181 map.insert(k, Value::String(v));
182 }
183 Ok(Some(Value::Object(map)))
184 }
185 }
186 }
187
188 fn resolve_config(&mut self, meta_idx: u16) -> Result<Option<HashMap<String, Value>>, StateError> {
190 let entries = match self.manifest.build_config(meta_idx) {
191 Some(e) => e,
192 None => return Ok(None),
193 };
194
195 let mut config = HashMap::new();
196 for (key, cv) in entries {
197 if let Some(v) = self.resolve_config_value(cv)? {
198 config.insert(key, v);
199 }
200 }
201 Ok(Some(config))
202 }
203
204 pub fn get(&mut self, key: &str) -> Result<Option<Value>, StateError> {
230 crate::fn_log!("State", "get", key);
231 if self.called_keys.len() >= self.max_recursion {
232 return Err(StateError::RecursionLimitExceeded);
233 }
234 if self.called_keys.contains(&key.to_string()) {
235 return Err(StateError::RecursionLimitExceeded);
236 }
237
238 self.called_keys.insert(key.to_string());
239
240 let (file, path) = Self::split_key(key);
241 let file = file.to_string();
242 let path = path.to_string();
243
244 if let Err(e) = self.load_manifest(&file) {
245 self.called_keys.remove(key);
246 return Err(StateError::ManifestLoadFailed(e.to_string()));
247 }
248
249 let key_idx = match self.manifest.find(&file, &path) {
250 Some(idx) => idx,
251 None => {
252 self.called_keys.remove(key);
253 return Err(StateError::KeyNotFound(key.to_string()));
254 }
255 };
256
257 if let Some(sv_idx) = self.find_state_value(key_idx) {
258 let val = self.state_vals.get(sv_idx).cloned();
259 self.called_keys.remove(key);
260 return Ok(val);
261 }
262
263 let meta = self.manifest.get_meta(&file, &path);
264
265 let has_state_client = meta.load
266 .map(|load_idx| self.manifest.get_client(load_idx) == fixed_bits::CLIENT_STATE)
267 .unwrap_or(false);
268
269 if !has_state_client {
270 if let Some(store_idx) = meta.store {
271 match self.resolve_config(store_idx) {
272 Ok(Some(config)) => {
273 if let Some(value) = self.store.get(&config) {
274 self.state_keys.push(key_idx);
275 self.state_vals.push(value.clone());
276 self.called_keys.remove(key);
277 return Ok(Some(value));
278 }
279 }
280 Ok(None) => {}
281 Err(e) => {
282 self.called_keys.remove(key);
283 return Err(e);
284 }
285 }
286 }
287 }
288
289 if has_state_client {
291 if let Some(load_idx) = meta.load {
292 let state_key = self.manifest.build_config(load_idx)
293 .and_then(|entries| entries.into_iter().find(|(k, _)| k == "key"))
294 .and_then(|(_, cv)| match cv {
295 ConfigValue::Placeholder(p) => Some(p),
296 ConfigValue::Str(s) => Some(s),
297 _ => None,
298 });
299 let result = match state_key {
300 Some(k) => self.get(&k),
301 None => Ok(None),
302 };
303 self.called_keys.remove(key);
304 return result;
305 }
306 }
307
308 let result = if let Some(load_idx) = meta.load {
309 match self.resolve_config(load_idx) {
310 Ok(Some(mut config)) => {
311 if !config.contains_key("client") {
312 self.called_keys.remove(key);
313 return Ok(None);
314 }
315
316 if let Some(Value::Object(map_obj)) = config.get("map").cloned() {
318 let mut unqualified = serde_json::Map::new();
319 for (qk, v) in map_obj {
320 let field = qk.rfind('.').map_or(qk.as_str(), |p| &qk[p+1..]);
321 unqualified.insert(field.to_string(), v);
322 }
323 config.insert("map".to_string(), Value::Object(unqualified));
324 }
325
326 match self.load.handle(&config) {
327 Ok(loaded) => {
328 if let Some(store_idx) = meta.store {
329 match self.resolve_config(store_idx) {
330 Ok(Some(store_config)) => {
331 if self.store.set(&store_config, loaded.clone(), None).unwrap_or(false) {
332 self.state_keys.push(key_idx);
333 self.state_vals.push(loaded.clone());
334 }
335 }
336 Ok(None) => {
337 self.state_keys.push(key_idx);
338 self.state_vals.push(loaded.clone());
339 }
340 Err(_) => {} }
342 } else {
343 self.state_keys.push(key_idx);
344 self.state_vals.push(loaded.clone());
345 }
346 Ok(Some(loaded))
347 }
348 Err(e) => Err(StateError::LoadFailed(e)),
349 }
350 }
351 Ok(None) => Ok(None),
352 Err(e) => Err(e),
353 }
354 } else { Ok(None) };
355
356 self.called_keys.remove(key);
357 result
358 }
359
360 pub fn set(&mut self, key: &str, value: Value, ttl: Option<u64>) -> Result<bool, StateError> {
382 crate::fn_log!("State", "set", key);
383 let (file, path) = Self::split_key(key);
384 let file = file.to_string();
385 let path = path.to_string();
386
387 if let Err(e) = self.load_manifest(&file) {
388 return Err(StateError::ManifestLoadFailed(e.to_string()));
389 }
390
391 let key_idx = match self.manifest.find(&file, &path) {
392 Some(idx) => idx,
393 None => return Err(StateError::KeyNotFound(key.to_string())),
394 };
395
396 let meta = self.manifest.get_meta(&file, &path);
397
398 if let Some(store_idx) = meta.store {
399 match self.resolve_config(store_idx)? {
400 Some(config) => {
401 return match self.store.set(&config, value.clone(), ttl) {
402 Ok(ok) => {
403 if ok {
404 if let Some(sv_idx) = self.find_state_value(key_idx) {
405 self.state_vals[sv_idx] = value;
406 } else {
407 self.state_keys.push(key_idx);
408 self.state_vals.push(value);
409 }
410 }
411 Ok(ok)
412 }
413 Err(e) => Err(StateError::StoreFailed(e)),
414 };
415 }
416 None => {}
417 }
418 }
419 Ok(false)
420 }
421
422 pub fn delete(&mut self, key: &str) -> Result<bool, StateError> {
447 crate::fn_log!("State", "delete", key);
448 let (file, path) = Self::split_key(key);
449 let file = file.to_string();
450 let path = path.to_string();
451
452 if let Err(e) = self.load_manifest(&file) {
453 return Err(StateError::ManifestLoadFailed(e.to_string()));
454 }
455
456 let key_idx = match self.manifest.find(&file, &path) {
457 Some(idx) => idx,
458 None => return Err(StateError::KeyNotFound(key.to_string())),
459 };
460
461 let meta = self.manifest.get_meta(&file, &path);
462
463 if let Some(store_idx) = meta.store {
464 match self.resolve_config(store_idx)? {
465 Some(config) => {
466 return match self.store.delete(&config) {
467 Ok(ok) => {
468 if ok {
469 if let Some(sv_idx) = self.find_state_value(key_idx) {
470 self.state_keys[sv_idx] = 0;
471 self.state_vals[sv_idx] = Value::Null;
472 }
473 }
474 Ok(ok)
475 }
476 Err(e) => Err(StateError::StoreFailed(e)),
477 };
478 }
479 None => {}
480 }
481 }
482 Ok(false)
483 }
484
485 pub fn exists(&mut self, key: &str) -> Result<bool, StateError> {
510 crate::fn_log!("State", "exists", key);
511 let (file, path) = Self::split_key(key);
512 let file = file.to_string();
513 let path = path.to_string();
514
515 if let Err(e) = self.load_manifest(&file) {
516 return Err(StateError::ManifestLoadFailed(e.to_string()));
517 }
518
519 let key_idx = match self.manifest.find(&file, &path) {
520 Some(idx) => idx,
521 None => return Err(StateError::KeyNotFound(key.to_string())),
522 };
523
524 if let Some(sv_idx) = self.find_state_value(key_idx) {
525 return Ok(!self.state_vals.get(sv_idx).map_or(true, |v| v.is_null()));
526 }
527
528 let meta = self.manifest.get_meta(&file, &path);
529 if let Some(store_idx) = meta.store {
530 if let Some(config) = self.resolve_config(store_idx)? {
531 return Ok(self.store.get(&config).is_some());
532 }
533 }
534 Ok(false)
535 }
536}
537
538fn yaml_to_parse_value(v: serde_yaml_ng::Value) -> ParseValue {
539 match v {
540 serde_yaml_ng::Value::Mapping(m) => ParseValue::Mapping(
541 m.into_iter()
542 .filter_map(|(k, v)| {
543 let key = match k {
544 serde_yaml_ng::Value::String(s) => s,
545 _ => return None,
546 };
547 Some((key, yaml_to_parse_value(v)))
548 })
549 .collect(),
550 ),
551 serde_yaml_ng::Value::String(s) => ParseValue::Scalar(s),
552 serde_yaml_ng::Value::Number(n) => ParseValue::Scalar(n.to_string()),
553 serde_yaml_ng::Value::Bool(b) => ParseValue::Scalar(b.to_string()),
554 serde_yaml_ng::Value::Null => ParseValue::Null,
555 _ => ParseValue::Null,
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562 use crate::ports::required::{KVSClient, DbClient, EnvClient, FileClient};
563 use serde_json::Value;
564 use std::collections::HashMap;
565 use std::sync::Arc;
566
567 struct StubKVS;
568 impl KVSClient for StubKVS {
569 fn get(&self, _: &str) -> Option<String> { None }
570 fn set(&self, _: &str, _: String, _: Option<u64>) -> bool { false }
571 fn delete(&self, _: &str) -> bool { false }
572 }
573
574 struct StubDb;
575 impl DbClient for StubDb {
576 fn get(&self, _: &Value, _: &str, _: &[&str], _: Option<&str>) -> Option<Vec<HashMap<String, Value>>> { None }
577 fn set(&self, _: &Value, _: &str, _: &HashMap<String, Value>, _: Option<&str>) -> bool { false }
578 fn delete(&self, _: &Value, _: &str, _: Option<&str>) -> bool { false }
579 }
580
581 struct StubEnv;
582 impl EnvClient for StubEnv {
583 fn get(&self, _: &str) -> Option<String> { None }
584 fn set(&self, _: &str, _: String) -> bool { false }
585 fn delete(&self, _: &str) -> bool { false }
586 }
587
588 struct StubFile;
589 impl FileClient for StubFile {
590 fn get(&self, _: &str) -> Option<String> { None }
591 fn set(&self, _: &str, _: String) -> bool { false }
592 fn delete(&self, _: &str) -> bool { false }
593 }
594
595 struct StubHttp;
596 impl crate::ports::required::HttpClient for StubHttp {
597 fn get(&self, _: &str, _: Option<&HashMap<String, String>>) -> Option<Value> { None }
598 fn set(&self, _: &str, _: Value, _: Option<&HashMap<String, String>>) -> bool { false }
599 fn delete(&self, _: &str, _: Option<&HashMap<String, String>>) -> bool { false }
600 }
601
602 #[test]
603 fn test_with_clients_build() {
604 let _ = State::new("./examples/manifest").with_kvs(Arc::new(StubKVS));
605 let _ = State::new("./examples/manifest").with_db(Arc::new(StubDb));
606 let _ = State::new("./examples/manifest").with_env(Arc::new(StubEnv));
607 let _ = State::new("./examples/manifest").with_http(Arc::new(StubHttp));
608 let _ = State::new("./examples/manifest").with_file(Arc::new(StubFile));
609 }
610}