use std::collections::BTreeMap;
use std::io;
use std::path::Path;
pub trait SourceTree {
fn list(&self, root: &Path) -> io::Result<Vec<String>>;
fn read(&self, key: &str) -> io::Result<String>;
}
#[derive(Debug, Clone, Default)]
pub struct InMemory {
files: BTreeMap<String, String>,
}
impl InMemory {
#[must_use]
pub fn new(files: BTreeMap<String, String>) -> Self {
Self { files }
}
}
impl SourceTree for InMemory {
fn list(&self, _root: &Path) -> io::Result<Vec<String>> {
Ok(self.files.keys().cloned().collect())
}
fn read(&self, key: &str) -> io::Result<String> {
self.files
.get(key)
.cloned()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("{key}: not found")))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn in_memory_list_is_sorted_despite_hostile_reverse_insertion_order() {
let mut files = BTreeMap::new();
for key in ["c/z.brink", "b/m.brink", "a/a.brink"] {
files.insert(key.to_string(), format!("-- {key} --"));
}
let tree = InMemory::new(files);
let keys = tree.list(Path::new(".")).expect("list succeeds");
assert_eq!(keys, vec!["a/a.brink", "b/m.brink", "c/z.brink"]);
}
#[test]
fn in_memory_list_is_sorted_despite_hostile_shuffled_insertion_order() {
let mut files = BTreeMap::new();
for key in ["m/mid.brink", "a/first.brink", "z/last.brink", "b/b.brink"] {
files.insert(key.to_string(), format!("-- {key} --"));
}
let tree = InMemory::new(files);
let keys = tree.list(Path::new(".")).expect("list succeeds");
assert_eq!(
keys,
vec!["a/first.brink", "b/b.brink", "m/mid.brink", "z/last.brink"]
);
}
#[test]
fn in_memory_read_round_trips() {
let mut files = BTreeMap::new();
files.insert(
"market/barter.brink".to_string(),
"flow barter() {}".to_string(),
);
files.insert("main.brink".to_string(), "flow main() {}".to_string());
let tree = InMemory::new(files);
assert_eq!(
tree.read("market/barter.brink").expect("key exists"),
"flow barter() {}"
);
assert_eq!(
tree.read("main.brink").expect("key exists"),
"flow main() {}"
);
}
#[test]
fn in_memory_read_missing_key_is_not_found() {
let tree = InMemory::new(BTreeMap::new());
let err = tree.read("missing.brink").expect_err("key absent");
assert_eq!(err.kind(), io::ErrorKind::NotFound);
}
#[test]
fn in_memory_list_empty_is_ok_empty() {
let tree = InMemory::new(BTreeMap::new());
assert_eq!(
tree.list(Path::new(".")).expect("list succeeds"),
Vec::<String>::new()
);
}
}