1use std::fmt;
8use std::sync::Arc;
9
10use super::error::{Conflict, Resolution, Resolver};
11
12pub type MergePolicyFn = Arc<dyn Fn(&Conflict) -> Resolution + Send + Sync + 'static>;
14
15type KeyMatcherFn = Arc<dyn Fn(&[u8]) -> bool + Send + Sync + 'static>;
16
17#[derive(Clone)]
19pub struct MergePolicyRegistry {
20 rules: Vec<MergePolicyRule>,
21 default: Option<MergePolicyFn>,
22}
23
24impl MergePolicyRegistry {
25 pub fn new() -> Self {
27 Self {
28 rules: Vec::new(),
29 default: None,
30 }
31 }
32
33 pub fn with_default<F>(policy: F) -> Self
35 where
36 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
37 {
38 Self::new().default_policy(policy)
39 }
40
41 pub fn set_default<F>(&mut self, policy: F) -> &mut Self
43 where
44 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
45 {
46 self.default = Some(Arc::new(policy));
47 self
48 }
49
50 pub fn default_policy<F>(mut self, policy: F) -> Self
52 where
53 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
54 {
55 self.set_default(policy);
56 self
57 }
58
59 pub fn push_prefix<F>(&mut self, prefix: impl Into<Vec<u8>>, policy: F) -> &mut Self
64 where
65 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
66 {
67 self.rules
68 .push(MergePolicyRule::prefix(prefix.into(), Arc::new(policy)));
69 self
70 }
71
72 pub fn add_prefix<F>(mut self, prefix: impl Into<Vec<u8>>, policy: F) -> Self
74 where
75 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
76 {
77 self.push_prefix(prefix, policy);
78 self
79 }
80
81 pub fn push_exact<F>(&mut self, key: impl Into<Vec<u8>>, policy: F) -> &mut Self
86 where
87 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
88 {
89 self.rules
90 .push(MergePolicyRule::exact(key.into(), Arc::new(policy)));
91 self
92 }
93
94 pub fn add_exact<F>(mut self, key: impl Into<Vec<u8>>, policy: F) -> Self
96 where
97 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
98 {
99 self.push_exact(key, policy);
100 self
101 }
102
103 pub fn push_pattern<M, F>(
107 &mut self,
108 name: impl Into<String>,
109 matcher: M,
110 policy: F,
111 ) -> &mut Self
112 where
113 M: Fn(&[u8]) -> bool + Send + Sync + 'static,
114 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
115 {
116 self.rules.push(MergePolicyRule::pattern(
117 name.into(),
118 Arc::new(matcher),
119 Arc::new(policy),
120 ));
121 self
122 }
123
124 pub fn add_pattern<M, F>(mut self, name: impl Into<String>, matcher: M, policy: F) -> Self
126 where
127 M: Fn(&[u8]) -> bool + Send + Sync + 'static,
128 F: Fn(&Conflict) -> Resolution + Send + Sync + 'static,
129 {
130 self.push_pattern(name, matcher, policy);
131 self
132 }
133
134 pub fn len(&self) -> usize {
136 self.rules.len()
137 }
138
139 pub fn is_empty(&self) -> bool {
141 self.rules.is_empty()
142 }
143
144 pub fn has_default(&self) -> bool {
146 self.default.is_some()
147 }
148
149 pub fn rules(&self) -> &[MergePolicyRule] {
151 &self.rules
152 }
153
154 pub fn matching_rule(&self, key: &[u8]) -> Option<&MergePolicyRule> {
156 self.rules.iter().rev().find(|rule| rule.matches(key))
157 }
158
159 pub fn resolve(&self, conflict: &Conflict) -> Resolution {
164 if let Some(rule) = self.matching_rule(&conflict.key) {
165 return (rule.policy.as_ref())(conflict);
166 }
167
168 self.default
169 .as_ref()
170 .map(|policy| (policy.as_ref())(conflict))
171 .unwrap_or_else(Resolution::unresolved)
172 }
173
174 pub fn into_resolver(self) -> Resolver {
176 Box::new(move |conflict| self.resolve(conflict))
177 }
178
179 pub fn as_resolver(&self) -> Resolver {
181 self.clone().into_resolver()
182 }
183}
184
185impl Default for MergePolicyRegistry {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191impl fmt::Debug for MergePolicyRegistry {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 f.debug_struct("MergePolicyRegistry")
194 .field("rules", &self.rules)
195 .field("has_default", &self.has_default())
196 .finish()
197 }
198}
199
200#[derive(Clone)]
202pub struct MergePolicyRule {
203 matcher: MergePolicyMatcher,
204 policy: MergePolicyFn,
205}
206
207impl MergePolicyRule {
208 fn prefix(prefix: Vec<u8>, policy: MergePolicyFn) -> Self {
209 Self {
210 matcher: MergePolicyMatcher::Prefix(prefix),
211 policy,
212 }
213 }
214
215 fn exact(key: Vec<u8>, policy: MergePolicyFn) -> Self {
216 Self {
217 matcher: MergePolicyMatcher::Exact(key),
218 policy,
219 }
220 }
221
222 fn pattern(name: String, matcher: KeyMatcherFn, policy: MergePolicyFn) -> Self {
223 Self {
224 matcher: MergePolicyMatcher::Pattern { name, matcher },
225 policy,
226 }
227 }
228
229 pub fn matches(&self, key: &[u8]) -> bool {
231 match &self.matcher {
232 MergePolicyMatcher::Prefix(prefix) => key.starts_with(prefix),
233 MergePolicyMatcher::Exact(exact) => key == exact,
234 MergePolicyMatcher::Pattern { matcher, .. } => (matcher.as_ref())(key),
235 }
236 }
237
238 pub fn label(&self) -> MergePolicyRuleLabel<'_> {
240 match &self.matcher {
241 MergePolicyMatcher::Prefix(prefix) => MergePolicyRuleLabel::Prefix(prefix),
242 MergePolicyMatcher::Exact(key) => MergePolicyRuleLabel::Exact(key),
243 MergePolicyMatcher::Pattern { name, .. } => MergePolicyRuleLabel::Pattern(name),
244 }
245 }
246}
247
248impl fmt::Debug for MergePolicyRule {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 f.debug_struct("MergePolicyRule")
251 .field("label", &self.label())
252 .finish_non_exhaustive()
253 }
254}
255
256#[derive(Clone)]
257enum MergePolicyMatcher {
258 Prefix(Vec<u8>),
259 Exact(Vec<u8>),
260 Pattern { name: String, matcher: KeyMatcherFn },
261}
262
263#[derive(Clone, Copy, Debug, PartialEq, Eq)]
265pub enum MergePolicyRuleLabel<'a> {
266 Prefix(&'a [u8]),
268 Exact(&'a [u8]),
270 Pattern(&'a str),
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use crate::prolly::error::resolver;
278
279 fn conflict(key: &[u8]) -> Conflict {
280 Conflict {
281 key: key.to_vec(),
282 base: Some(b"base".to_vec()),
283 left: Some(b"left".to_vec()),
284 right: Some(b"right".to_vec()),
285 }
286 }
287
288 #[test]
289 fn newest_matching_rule_wins() {
290 let registry = MergePolicyRegistry::new()
291 .add_prefix(b"settings/".to_vec(), resolver::prefer_left)
292 .add_exact(b"settings/theme".to_vec(), resolver::prefer_right);
293
294 let result = registry.resolve(&conflict(b"settings/theme"));
295 assert_eq!(result, Resolution::value(b"right".to_vec()));
296
297 let rule = registry.matching_rule(b"settings/theme").unwrap();
298 assert_eq!(rule.label(), MergePolicyRuleLabel::Exact(b"settings/theme"));
299 }
300
301 #[test]
302 fn pattern_and_default_policies_work() {
303 let registry = MergePolicyRegistry::with_default(|_| Resolution::unresolved()).add_pattern(
304 "summary",
305 |key| key.ends_with(b"/summary"),
306 |conflict| {
307 let mut value = conflict.left.clone().unwrap_or_default();
308 value.push(b'\n');
309 value.extend(conflict.right.clone().unwrap_or_default());
310 Resolution::value(value)
311 },
312 );
313
314 assert_eq!(
315 registry.resolve(&conflict(b"documents/42/summary")),
316 Resolution::value(b"left\nright".to_vec())
317 );
318 assert_eq!(
319 registry.resolve(&conflict(b"documents/42/title")),
320 Resolution::unresolved()
321 );
322 }
323}