1 use std::collections::HashMap; 2 use std::fmt; 3 use std::sync; 4 5 use crate::error::Result; 6 use crate::parser; 7 use crate::parser::Language; 8 use crate::runtime; 9 use crate::runtime::PartialStore; 10 use crate::runtime::Renderable; 11 12 use super::PartialCompiler; 13 use super::PartialSource; 14 15 /// An lazily-caching compiler for `PartialSource`. 16 /// 17 /// This would be useful in cases where: 18 /// - Most partial-templates aren't used 19 /// - Of the used partial-templates, they are generally used many times. 20 /// 21 /// Note: partial-compilation error reporting is deferred to render-time so content can still be 22 /// generated even when the content is in an intermediate-state. 23 #[derive(Debug)] 24 pub struct LazyCompiler<S: PartialSource> { 25 source: S, 26 } 27 28 impl<S> LazyCompiler<S> 29 where 30 S: PartialSource, 31 { 32 /// Create an on-demand compiler for `PartialSource`. new(source: S) -> Self33 pub fn new(source: S) -> Self { 34 LazyCompiler { source } 35 } 36 } 37 38 impl<S> LazyCompiler<S> 39 where 40 S: PartialSource + Default, 41 { 42 /// Create an empty compiler for `PartialSource`. empty() -> Self43 pub fn empty() -> Self { 44 Default::default() 45 } 46 } 47 48 impl<S> Default for LazyCompiler<S> 49 where 50 S: PartialSource + Default, 51 { default() -> Self52 fn default() -> Self { 53 Self { 54 source: Default::default(), 55 } 56 } 57 } 58 59 impl<S> ::std::ops::Deref for LazyCompiler<S> 60 where 61 S: PartialSource, 62 { 63 type Target = S; 64 deref(&self) -> &S65 fn deref(&self) -> &S { 66 &self.source 67 } 68 } 69 70 impl<S> ::std::ops::DerefMut for LazyCompiler<S> 71 where 72 S: PartialSource, 73 { deref_mut(&mut self) -> &mut S74 fn deref_mut(&mut self) -> &mut S { 75 &mut self.source 76 } 77 } 78 79 impl<S> PartialCompiler for LazyCompiler<S> 80 where 81 S: PartialSource + Send + Sync + 'static, 82 { compile(self, language: sync::Arc<Language>) -> Result<Box<dyn PartialStore + Send + Sync>>83 fn compile(self, language: sync::Arc<Language>) -> Result<Box<dyn PartialStore + Send + Sync>> { 84 let store = LazyStore { 85 language, 86 source: self.source, 87 cache: sync::Mutex::new(Default::default()), 88 }; 89 Ok(Box::new(store)) 90 } 91 source(&self) -> &dyn PartialSource92 fn source(&self) -> &dyn PartialSource { 93 &self.source 94 } 95 } 96 97 struct LazyStore<S: PartialSource> { 98 language: sync::Arc<Language>, 99 source: S, 100 cache: sync::Mutex<HashMap<String, Result<sync::Arc<dyn runtime::Renderable>>>>, 101 } 102 103 impl<S> LazyStore<S> 104 where 105 S: PartialSource, 106 { try_get_or_create(&self, name: &str) -> Option<sync::Arc<dyn Renderable>>107 fn try_get_or_create(&self, name: &str) -> Option<sync::Arc<dyn Renderable>> { 108 let cache = self.cache.lock().expect("not to be poisoned and reused"); 109 if let Some(result) = cache.get(name) { 110 result.as_ref().ok().cloned() 111 } else { 112 let s = self.source.try_get(name)?; 113 let s = s.as_ref(); 114 let template = parser::parse(s, &self.language) 115 .map(runtime::Template::new) 116 .map(sync::Arc::new) 117 .ok()?; 118 Some(template) 119 } 120 } 121 get_or_create(&self, name: &str) -> Result<sync::Arc<dyn Renderable>>122 fn get_or_create(&self, name: &str) -> Result<sync::Arc<dyn Renderable>> { 123 let cache = self.cache.lock().expect("not to be poisoned and reused"); 124 if let Some(result) = cache.get(name) { 125 result.clone() 126 } else { 127 let s = self.source.get(name)?; 128 let s = s.as_ref(); 129 let template = parser::parse(s, &self.language) 130 .map(runtime::Template::new) 131 .map(sync::Arc::new)?; 132 Ok(template) 133 } 134 } 135 } 136 137 impl<S> PartialStore for LazyStore<S> 138 where 139 S: PartialSource, 140 { contains(&self, name: &str) -> bool141 fn contains(&self, name: &str) -> bool { 142 self.source.contains(name) 143 } 144 names(&self) -> Vec<&str>145 fn names(&self) -> Vec<&str> { 146 self.source.names() 147 } 148 try_get(&self, name: &str) -> Option<sync::Arc<dyn Renderable>>149 fn try_get(&self, name: &str) -> Option<sync::Arc<dyn Renderable>> { 150 self.try_get_or_create(name) 151 } 152 get(&self, name: &str) -> Result<sync::Arc<dyn Renderable>>153 fn get(&self, name: &str) -> Result<sync::Arc<dyn Renderable>> { 154 self.get_or_create(name) 155 } 156 } 157 158 impl<S> fmt::Debug for LazyStore<S> 159 where 160 S: PartialSource, 161 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 163 self.source.fmt(f) 164 } 165 } 166