moduforge_rules_engine/loader/
filesystem.rs1use std::collections::HashMap;
2use std::fs::File;
3use std::future::Future;
4use std::io::BufReader;
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8use serde::{Deserialize, Serialize};
9use tokio::sync::RwLock;
10
11use crate::loader::{DecisionLoader, LoaderError, LoaderResponse};
12use crate::model::DecisionContent;
13
14#[derive(Debug)]
16pub struct FilesystemLoader {
17 root: String,
18 memory_refs: Option<RwLock<HashMap<String, Arc<DecisionContent>>>>,
19}
20
21#[derive(Serialize, Deserialize)]
22pub struct FilesystemLoaderOptions<R: Into<String>> {
23 pub root: R,
24 pub keep_in_memory: bool,
25}
26
27impl FilesystemLoader {
28 pub fn new<R>(options: FilesystemLoaderOptions<R>) -> Self
29 where
30 R: Into<String>,
31 {
32 let root = options.root.into();
33 let memory_refs = if options.keep_in_memory {
34 Some(Default::default())
35 } else {
36 None
37 };
38
39 Self { root, memory_refs }
40 }
41
42 fn key_to_path<K: AsRef<str>>(
43 &self,
44 key: K,
45 ) -> PathBuf {
46 Path::new(&self.root).join(key.as_ref())
47 }
48
49 async fn read_from_file<K>(
50 &self,
51 key: K,
52 ) -> LoaderResponse
53 where
54 K: AsRef<str>,
55 {
56 if let Some(memory_refs) = &self.memory_refs {
57 let mref = memory_refs.read().await;
58 if let Some(decision_content) = mref.get(key.as_ref()) {
59 return Ok(decision_content.clone());
60 }
61 }
62
63 let path = self.key_to_path(key.as_ref());
64 if !Path::exists(&path) {
65 return Err(
66 LoaderError::NotFound(String::from(key.as_ref())).into()
67 );
68 }
69
70 let file = File::open(path).map_err(|e| LoaderError::Internal {
71 key: String::from(key.as_ref()),
72 source: e.into(),
73 })?;
74
75 let reader = BufReader::new(file);
76 let result: DecisionContent =
77 serde_json::from_reader(reader).map_err(|e| {
78 LoaderError::Internal {
79 key: String::from(key.as_ref()),
80 source: e.into(),
81 }
82 })?;
83
84 let ptr = Arc::new(result);
85 if let Some(memory_refs) = &self.memory_refs {
86 let mut mref = memory_refs.write().await;
87 mref.insert(key.as_ref().to_string(), ptr.clone());
88 }
89
90 Ok(ptr)
91 }
92}
93
94impl DecisionLoader for FilesystemLoader {
95 fn load<'a>(
96 &'a self,
97 key: &'a str,
98 ) -> impl Future<Output = LoaderResponse> + 'a {
99 async move { self.read_from_file(key).await }
100 }
101}