camel_proto_compiler/
cache.rs1use std::collections::HashMap;
2use std::collections::VecDeque;
3use std::path::Path;
4use std::sync::Mutex;
5
6use prost_reflect::DescriptorPool;
7
8use crate::compiler::compile_proto;
9use crate::{ProtoCompileError, hash_proto_content};
10
11const DEFAULT_MAX_ENTRIES: usize = 1000;
12
13pub struct ProtoCache {
14 pools: Mutex<HashMap<String, DescriptorPool>>,
15 order: Mutex<VecDeque<String>>,
16 max_entries: usize,
17}
18
19impl Default for ProtoCache {
20 fn default() -> Self {
21 Self {
22 pools: Mutex::new(HashMap::new()),
23 order: Mutex::new(VecDeque::new()),
24 max_entries: DEFAULT_MAX_ENTRIES,
25 }
26 }
27}
28
29impl ProtoCache {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn with_max_entries(max_entries: usize) -> Self {
38 Self {
39 pools: Mutex::new(HashMap::new()),
40 order: Mutex::new(VecDeque::with_capacity(max_entries)),
41 max_entries: max_entries.max(1),
42 }
43 }
44
45 fn insert_entry(&self, key: String, pool: DescriptorPool) {
46 let mut pools = self.pools.lock().expect("mutex poisoned"); let mut order = self.order.lock().expect("mutex poisoned"); #[allow(clippy::map_entry)]
53 if pools.contains_key(&key) {
54 pools.insert(key, pool);
55 return;
56 }
57
58 while pools.len() >= self.max_entries {
60 if let Some(old_key) = order.pop_front() {
61 pools.remove(&old_key);
62 } else {
63 break;
64 }
65 }
66
67 order.push_back(key.clone());
68 pools.insert(key, pool);
69 }
70
71 pub fn get_or_compile<P, I>(
72 &self,
73 proto_path: P,
74 includes: I,
75 ) -> Result<DescriptorPool, ProtoCompileError>
76 where
77 P: AsRef<Path>,
78 I: IntoIterator,
79 I::Item: AsRef<Path>,
80 {
81 let proto_path = proto_path.as_ref();
82 let include_paths = includes
83 .into_iter()
84 .map(|p| p.as_ref().to_path_buf())
85 .collect::<Vec<_>>();
86
87 let key = format!(
88 "{}:{}",
89 proto_path.display(),
90 hash_proto_content(proto_path)?
91 );
92
93 if let Some(pool) = self
94 .pools
95 .lock()
96 .expect("mutex poisoned") .get(&key)
98 .cloned()
99 {
100 return Ok(pool);
101 }
102
103 let pool = compile_proto(proto_path, &include_paths)?;
104 self.insert_entry(key, pool.clone());
105 Ok(pool)
106 }
107
108 pub fn len(&self) -> usize {
109 self.pools.lock().expect("mutex poisoned").len() }
111
112 pub fn is_empty(&self) -> bool {
113 self.pools.lock().expect("mutex poisoned").is_empty() }
115}