1use crate::path::{InvalidPart, Path, PathPart};
21use crate::{ObjectStore, parse_url_opts};
22use parking_lot::RwLock;
23use std::collections::HashMap;
24use std::sync::Arc;
25use url::Url;
26
27pub trait ObjectStoreRegistry: Send + Sync + std::fmt::Debug + 'static {
29 fn register(&self, url: Url, store: Arc<dyn ObjectStore>) -> Option<Arc<dyn ObjectStore>>;
33
34 fn resolve(&self, url: &Url) -> crate::Result<(Arc<dyn ObjectStore>, Path)>;
85
86 fn deregister(&self, url: &Url) -> Option<Arc<dyn ObjectStore>> {
112 let _ = url;
113 None
114 }
115}
116
117#[derive(Debug, thiserror::Error)]
122#[non_exhaustive]
123enum Error {
124 #[error("ObjectStore not found")]
125 NotFound,
126
127 #[error("Error parsing URL path segment")]
128 InvalidPart(#[from] InvalidPart),
129}
130
131impl From<Error> for crate::Error {
132 fn from(value: Error) -> Self {
133 Self::Generic {
134 store: "ObjectStoreRegistry",
135 source: Box::new(value),
136 }
137 }
138}
139
140#[derive(Debug, Default)]
142pub struct DefaultObjectStoreRegistry {
143 map: RwLock<HashMap<String, PathEntry>>,
145}
146
147#[derive(Debug, Default)]
167struct PathEntry {
168 store: Option<Arc<dyn ObjectStore>>,
170 children: HashMap<String, Self>,
172}
173
174impl PathEntry {
175 fn lookup(&self, to_resolve: &Url) -> Option<(&Arc<dyn ObjectStore>, usize)> {
179 let mut current = self;
180 let mut ret = self.store.as_ref().map(|store| (store, 0));
181 let mut depth = 0;
182 for segment in path_segments(to_resolve.path()) {
184 match current.children.get(segment) {
185 Some(e) => {
186 current = e;
187 depth += 1;
188 if let Some(store) = ¤t.store {
189 ret = Some((store, depth))
190 }
191 }
192 None => break,
193 }
194 }
195 ret
196 }
197
198 fn remove(&mut self, segments: &[&str]) -> Option<Arc<dyn ObjectStore>> {
201 match segments.split_first() {
202 None => self.store.take(),
203 Some((head, rest)) => {
204 let child = self.children.get_mut(*head)?;
205 let removed = child.remove(rest);
206 if child.store.is_none() && child.children.is_empty() {
207 self.children.remove(*head);
208 }
209 removed
210 }
211 }
212 }
213}
214
215impl DefaultObjectStoreRegistry {
216 pub fn new() -> Self {
218 Self::default()
219 }
220}
221
222impl ObjectStoreRegistry for DefaultObjectStoreRegistry {
223 fn register(&self, url: Url, store: Arc<dyn ObjectStore>) -> Option<Arc<dyn ObjectStore>> {
224 let mut map = self.map.write();
225 let key = url_key(&url);
226 let mut entry = map.entry(key.to_string()).or_default();
227
228 for segment in path_segments(url.path()) {
229 entry = entry.children.entry(segment.to_string()).or_default();
230 }
231 entry.store.replace(store)
232 }
233
234 fn resolve(&self, to_resolve: &Url) -> crate::Result<(Arc<dyn ObjectStore>, Path)> {
235 let key = url_key(to_resolve);
236 {
237 let map = self.map.read();
238
239 if let Some((store, depth)) = map.get(key).and_then(|entry| entry.lookup(to_resolve)) {
240 let path = path_suffix(to_resolve, depth)?;
241 return Ok((Arc::clone(store), path));
242 }
243 }
244
245 if let Ok((store, path)) = parse_url_opts(to_resolve, std::env::vars()) {
246 let depth = num_segments(to_resolve.path()) - num_segments(path.as_ref());
247
248 let mut map = self.map.write();
249 let mut entry = map.entry(key.to_string()).or_default();
250 for segment in path_segments(to_resolve.path()).take(depth) {
251 entry = entry.children.entry(segment.to_string()).or_default();
252 }
253 let store = Arc::clone(match &entry.store {
254 None => entry.store.insert(Arc::from(store)),
255 Some(x) => x, });
257
258 let path = path_suffix(to_resolve, depth)?;
259 return Ok((store, path));
260 }
261
262 Err(Error::NotFound.into())
263 }
264
265 fn deregister(&self, url: &Url) -> Option<Arc<dyn ObjectStore>> {
266 let key = url_key(url);
267 let segments: Vec<&str> = path_segments(url.path()).collect();
268 let mut map = self.map.write();
269 let entry = map.get_mut(key)?;
270 let removed = entry.remove(&segments);
271 if entry.store.is_none() && entry.children.is_empty() {
273 map.remove(key);
274 }
275 removed
276 }
277}
278
279fn url_key(url: &Url) -> &str {
281 &url[..url::Position::AfterPort]
282}
283
284fn path_segments(s: &str) -> impl Iterator<Item = &str> {
288 s.split('/').filter(|x| !x.is_empty())
289}
290
291fn num_segments(s: &str) -> usize {
293 path_segments(s).count()
294}
295
296fn path_suffix(url: &Url, depth: usize) -> Result<Path, Error> {
298 let segments = path_segments(url.path()).skip(depth);
299 let path = segments.map(PathPart::parse).collect::<Result<_, _>>()?;
300 Ok(path)
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 use crate::memory::InMemory;
307 use crate::prefix::PrefixStore;
308
309 #[test]
310 fn test_num_segments() {
311 assert_eq!(num_segments(""), 0);
312 assert_eq!(num_segments("/"), 0);
313 assert_eq!(num_segments("/banana"), 1);
314 assert_eq!(num_segments("banana"), 1);
315 assert_eq!(num_segments("/banana/crumble"), 2);
316 assert_eq!(num_segments("banana/crumble"), 2);
317 }
318
319 #[test]
320 fn test_default_registry() {
321 let registry = DefaultObjectStoreRegistry::new();
322
323 let banana_url = Url::parse("memory:///banana").unwrap();
325 let (resolved, path) = registry.resolve(&banana_url).unwrap();
326 assert_eq!(path.as_ref(), "banana");
327
328 let url = Url::parse("memory:///").unwrap();
330 let root = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
331 let replaced = registry.register(url, Arc::clone(&root)).unwrap();
332 assert!(Arc::ptr_eq(&resolved, &replaced));
333
334 let banana = Arc::new(PrefixStore::new(InMemory::new(), "banana")) as Arc<dyn ObjectStore>;
336 assert!(
337 registry
338 .register(banana_url.clone(), Arc::clone(&banana))
339 .is_none()
340 );
341
342 let (resolved, path) = registry.resolve(&banana_url).unwrap();
344 assert_eq!(path.as_ref(), "");
345 assert!(Arc::ptr_eq(&resolved, &banana));
346
347 let apples_url = Url::parse("memory:///apples").unwrap();
349 let apples = Arc::new(PrefixStore::new(InMemory::new(), "apples")) as Arc<dyn ObjectStore>;
350 assert!(registry.register(apples_url, Arc::clone(&apples)).is_none());
351
352 let (resolved, path) = registry.resolve(&banana_url).unwrap();
354 assert_eq!(path.as_ref(), "");
355 assert!(Arc::ptr_eq(&resolved, &banana));
356
357 let banana_muffins_url = Url::parse("memory:///banana_muffins").unwrap();
359 let (resolved, path) = registry.resolve(&banana_muffins_url).unwrap();
360 assert_eq!(path.as_ref(), "banana_muffins");
361 assert!(Arc::ptr_eq(&resolved, &root));
362
363 let to_resolve = Url::parse("memory:///foo/banana").unwrap();
365 let (resolved, path) = registry.resolve(&to_resolve).unwrap();
366 assert_eq!(path.as_ref(), "foo/banana");
367 assert!(Arc::ptr_eq(&resolved, &root));
368
369 let nested_url = Url::parse("memory:///apples/bananas").unwrap();
370 let nested =
371 Arc::new(PrefixStore::new(InMemory::new(), "apples/bananas")) as Arc<dyn ObjectStore>;
372 assert!(registry.register(nested_url, Arc::clone(&nested)).is_none());
373
374 let to_resolve = Url::parse("memory:///apples/bananas/muffins/cupcakes").unwrap();
375 let (resolved, path) = registry.resolve(&to_resolve).unwrap();
376 assert_eq!(path.as_ref(), "muffins/cupcakes");
377 assert!(Arc::ptr_eq(&resolved, &nested));
378
379 let nested_url2 = Url::parse("memory:///1/2/3").unwrap();
380 let nested2 = Arc::new(PrefixStore::new(InMemory::new(), "1/2/3")) as Arc<dyn ObjectStore>;
381 assert!(
382 registry
383 .register(nested_url2, Arc::clone(&nested2))
384 .is_none()
385 );
386
387 let to_resolve = Url::parse("memory:///1/2/3/4/5/6").unwrap();
388 let (resolved, path) = registry.resolve(&to_resolve).unwrap();
389 assert_eq!(path.as_ref(), "4/5/6");
390 assert!(Arc::ptr_eq(&resolved, &nested2));
391
392 let custom_scheme_url = Url::parse("custom:///").unwrap();
393 let custom_scheme = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
394 assert!(
395 registry
396 .register(custom_scheme_url, Arc::clone(&custom_scheme))
397 .is_none()
398 );
399
400 let to_resolve = Url::parse("custom:///6/7").unwrap();
401 let (resolved, path) = registry.resolve(&to_resolve).unwrap();
402 assert_eq!(path.as_ref(), "6/7");
403 assert!(Arc::ptr_eq(&resolved, &custom_scheme));
404 }
405
406 #[test]
407 fn test_deregister() {
408 let registry = DefaultObjectStoreRegistry::new();
409 let a = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
410 let b = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
411
412 let a_url = Url::parse("mock://bucket/a").unwrap();
415 let b_url = Url::parse("mock://bucket/b").unwrap();
416 registry.register(a_url.clone(), Arc::clone(&a));
417 registry.register(b_url.clone(), Arc::clone(&b));
418
419 let removed = registry.deregister(&a_url).unwrap();
421 assert!(Arc::ptr_eq(&removed, &a));
422 assert!(registry.resolve(&a_url).is_err());
423 let (resolved, _) = registry.resolve(&b_url).unwrap();
424 assert!(Arc::ptr_eq(&resolved, &b));
425
426 assert!(registry.deregister(&a_url).is_none());
428 assert!(
430 registry
431 .deregister(&Url::parse("mock://other/x").unwrap())
432 .is_none()
433 );
434 }
435
436 #[test]
437 fn test_deregister_keeps_longer_prefix() {
438 let registry = DefaultObjectStoreRegistry::new();
439 let outer = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
440 let inner = Arc::new(InMemory::new()) as Arc<dyn ObjectStore>;
441 let outer_url = Url::parse("mock://bucket/a").unwrap();
442 let inner_url = Url::parse("mock://bucket/a/b").unwrap();
443 registry.register(outer_url.clone(), Arc::clone(&outer));
444 registry.register(inner_url.clone(), Arc::clone(&inner));
445
446 assert!(Arc::ptr_eq(
448 ®istry.deregister(&outer_url).unwrap(),
449 &outer
450 ));
451 let (resolved, path) = registry
452 .resolve(&Url::parse("mock://bucket/a/b/x").unwrap())
453 .unwrap();
454 assert!(Arc::ptr_eq(&resolved, &inner));
455 assert_eq!(path.as_ref(), "x");
456 }
457}