zen_engine/loader/
filesystem.rs

1use 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/// Loads decisions based on filesystem root
15#[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>>(&self, key: K) -> PathBuf {
43        Path::new(&self.root).join(key.as_ref())
44    }
45
46    async fn read_from_file<K>(&self, key: K) -> LoaderResponse
47    where
48        K: AsRef<str>,
49    {
50        if let Some(memory_refs) = &self.memory_refs {
51            let mref = memory_refs.read().await;
52            if let Some(decision_content) = mref.get(key.as_ref()) {
53                return Ok(decision_content.clone());
54            }
55        }
56
57        let path = self.key_to_path(key.as_ref());
58        if !Path::exists(&path) {
59            return Err(LoaderError::NotFound(String::from(key.as_ref())).into());
60        }
61
62        let file = File::open(path).map_err(|e| LoaderError::Internal {
63            key: String::from(key.as_ref()),
64            source: e.into(),
65        })?;
66
67        let reader = BufReader::new(file);
68        let result: DecisionContent =
69            serde_json::from_reader(reader).map_err(|e| LoaderError::Internal {
70                key: String::from(key.as_ref()),
71                source: e.into(),
72            })?;
73
74        let ptr = Arc::new(result);
75        if let Some(memory_refs) = &self.memory_refs {
76            let mut mref = memory_refs.write().await;
77            mref.insert(key.as_ref().to_string(), ptr.clone());
78        }
79
80        Ok(ptr)
81    }
82}
83
84impl DecisionLoader for FilesystemLoader {
85    fn load<'a>(&'a self, key: &'a str) -> impl Future<Output = LoaderResponse> + 'a {
86        async move { self.read_from_file(key).await }
87    }
88}