mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 08:02:28 +00:00
123 lines
3.6 KiB
Rust
123 lines
3.6 KiB
Rust
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::loader::{DynamicLoader, FilesystemLoader, FilesystemLoaderOptions, MemoryLoader};
|
|
use crate::model::DecisionContent;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(
|
|
tag = "type",
|
|
rename_all = "camelCase",
|
|
rename_all_fields = "camelCase"
|
|
)]
|
|
pub enum LoaderConfig {
|
|
Static {
|
|
content: HashMap<String, DecisionContent>,
|
|
},
|
|
#[serde(rename = "fs")]
|
|
Filesystem {
|
|
path: String,
|
|
},
|
|
Zip {
|
|
bytes: Vec<u8>,
|
|
},
|
|
}
|
|
|
|
impl LoaderConfig {
|
|
pub fn into_loader(self) -> anyhow::Result<DynamicLoader> {
|
|
match self {
|
|
LoaderConfig::Static { content } => {
|
|
let loader = MemoryLoader::default();
|
|
for (key, decision_content) in content {
|
|
loader.add(key, decision_content);
|
|
}
|
|
Ok(Arc::new(loader))
|
|
}
|
|
LoaderConfig::Filesystem { path } => {
|
|
Ok(Arc::new(FilesystemLoader::new(FilesystemLoaderOptions {
|
|
root: path,
|
|
})))
|
|
}
|
|
LoaderConfig::Zip { bytes } => Self::loader_from_zip(&bytes),
|
|
}
|
|
}
|
|
|
|
fn loader_from_zip(bytes: &[u8]) -> anyhow::Result<DynamicLoader> {
|
|
use std::io::Read;
|
|
|
|
let mut archive = zip::ZipArchive::new(std::io::Cursor::new(bytes))?;
|
|
let loader = MemoryLoader::default();
|
|
for index in 0..archive.len() {
|
|
let mut entry = archive.by_index(index)?;
|
|
if !entry.is_file() || !entry.name().ends_with(".json") {
|
|
continue;
|
|
}
|
|
|
|
let key = entry.name().to_string();
|
|
let mut buffer = Vec::with_capacity(entry.size() as usize);
|
|
entry.read_to_end(&mut buffer)?;
|
|
let content: DecisionContent = serde_json::from_slice(&buffer)?;
|
|
loader.add(key, content);
|
|
}
|
|
|
|
Ok(Arc::new(loader))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const GRAPH_JSON: &str = r#"{"nodes":[],"edges":[]}"#;
|
|
|
|
#[tokio::test]
|
|
async fn static_config_serves_decisions_by_key() {
|
|
let mut content = HashMap::new();
|
|
content.insert(
|
|
"graph.json".to_string(),
|
|
serde_json::from_str::<DecisionContent>(GRAPH_JSON).unwrap(),
|
|
);
|
|
|
|
let loader = LoaderConfig::Static { content }.into_loader().unwrap();
|
|
assert!(loader.load("graph.json").await.is_ok());
|
|
assert!(loader.load("missing.json").await.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn fs_config_reads_path() {
|
|
let config: LoaderConfig = serde_json::from_str(r#"{"type":"fs","path":"p"}"#).unwrap();
|
|
|
|
let LoaderConfig::Filesystem { path } = config else {
|
|
panic!("expected filesystem loader config");
|
|
};
|
|
|
|
assert_eq!(path, "p");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn zip_config_decompresses_and_serves_decisions() {
|
|
use std::io::Write;
|
|
|
|
let mut cursor = std::io::Cursor::new(Vec::new());
|
|
{
|
|
let mut writer = zip::ZipWriter::new(&mut cursor);
|
|
let options = zip::write::SimpleFileOptions::default()
|
|
.compression_method(zip::CompressionMethod::Deflated);
|
|
writer.start_file("graph.json", options).unwrap();
|
|
writer.write_all(GRAPH_JSON.as_bytes()).unwrap();
|
|
writer.finish().unwrap();
|
|
}
|
|
|
|
let loader = LoaderConfig::Zip {
|
|
bytes: cursor.into_inner(),
|
|
}
|
|
.into_loader()
|
|
.unwrap();
|
|
|
|
assert!(loader.load("graph.json").await.is_ok());
|
|
assert!(loader.load("missing.json").await.is_err());
|
|
}
|
|
}
|