1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use failure; use std::{cell::RefCell, collections::HashMap, fs::File, io::Read, path::PathBuf}; use textwrap::dedent as d; pub trait Context { fn lookup(&self, key: &str) -> Result<String, failure::Error>; } pub struct StaticContext { pub aliases: HashMap<String, String>, } impl Context for StaticContext { fn lookup(&self, key: &str) -> Result<String, failure::Error> { self.aliases .get(key) .map(|v| v.to_string()) .ok_or_else(|| failure::err_msg(format!("key not found: {}", key))) } } impl StaticContext { pub fn new(key: &str, value: &str) -> StaticContext { let mut m = HashMap::new(); m.insert(key.into(), d(value).trim().to_string()); StaticContext { aliases: m } } pub fn with(mut self, key: &str, value: &str) -> StaticContext { self.aliases.insert(key.into(), d(value).trim().to_string()); self } } pub struct DirContext { pub dir: PathBuf, } impl Context for DirContext { fn lookup(&self, key: &str) -> Result<String, failure::Error> { let mut file = File::open(self.dir.join(key))?; let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } } impl DirContext { pub fn new(dir: PathBuf) -> DirContext { DirContext { dir } } } pub struct CachedContext<Context> { root: Box<Context>, cache: RefCell<HashMap<String, Option<String>>>, } impl<T> Context for CachedContext<T> where T: Context, { fn lookup(&self, key: &str) -> Result<String, failure::Error> { match self.cache.borrow().get(key) { Some(v) => match v { Some(v) => return Ok(v.to_string()), None => return Err(failure::err_msg("not found")), }, None => {} }; (&self.root) .lookup(key) .map(|v| { self.cache .borrow_mut() .insert(key.to_string(), Some(v.clone())); v }).map_err(|e| { self.cache.borrow_mut().insert(key.to_string(), None); e }) } } impl<T> CachedContext<T> where T: Context, { pub fn new(root: Box<T>) -> CachedContext<T> { CachedContext { root, cache: RefCell::new(HashMap::new()), } } }