qubit_fs/provider/file_system_registry.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Filesystem provider registry.
11
12use std::sync::Arc;
13
14use qubit_spi::{
15 ProviderRegistry,
16 ProviderRegistryError,
17 ServiceProvider,
18};
19
20use crate::{
21 FileResource,
22 FileSystem,
23 FileSystemConfig,
24 FileSystemProvider,
25 FileSystemSpec,
26 FsError,
27 FsErrorKind,
28 FsOperation,
29 FsResult,
30 FsUri,
31};
32
33/// Registry of filesystem providers.
34#[derive(Debug, Default)]
35pub struct FileSystemRegistry {
36 /// Underlying typed SPI registry.
37 providers: ProviderRegistry<FileSystemSpec>,
38}
39
40impl FileSystemRegistry {
41 /// Creates an empty filesystem registry.
42 ///
43 /// # Returns
44 /// Empty registry.
45 #[inline]
46 #[must_use]
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 /// Registers an owned filesystem provider.
52 ///
53 /// # Parameters
54 /// - `provider`: Provider to register.
55 ///
56 /// # Errors
57 /// Returns [`FsError`] when provider metadata is invalid or conflicts with
58 /// an existing provider.
59 pub fn register<P>(&mut self, provider: P) -> FsResult<()>
60 where
61 P: ServiceProvider<FileSystemSpec> + 'static,
62 {
63 self.providers.register(provider).map_err(map_provider_error)
64 }
65
66 /// Registers a shared filesystem provider.
67 ///
68 /// # Parameters
69 /// - `provider`: Shared provider to register.
70 ///
71 /// # Errors
72 /// Returns [`FsError`] when provider metadata is invalid or conflicts with
73 /// an existing provider.
74 pub fn register_shared(&mut self, provider: Arc<FileSystemProvider>) -> FsResult<()> {
75 self.providers.register_shared(provider).map_err(map_provider_error)
76 }
77
78 /// Resolves a parsed URI into a filesystem instance.
79 ///
80 /// # Parameters
81 /// - `uri`: Parsed filesystem URI.
82 ///
83 /// # Returns
84 /// Shared filesystem instance created by the selected provider.
85 ///
86 /// # Errors
87 /// Returns [`FsError`] when provider resolution or creation fails.
88 pub fn fs(&self, uri: &FsUri) -> FsResult<Arc<dyn FileSystem>> {
89 let selector = uri.scheme.clone();
90 let config = FileSystemConfig::new(uri.clone());
91 self.providers
92 .create_arc(&selector, &config)
93 .map_err(map_provider_error)
94 }
95
96 /// Resolves a parsed URI into a bound file resource.
97 ///
98 /// # Parameters
99 /// - `uri`: Parsed filesystem URI.
100 ///
101 /// # Returns
102 /// A file resource containing the matching filesystem and filesystem-local
103 /// path.
104 ///
105 /// # Errors
106 /// Returns [`FsError`] when provider resolution or creation fails.
107 pub fn resource(&self, uri: &FsUri) -> FsResult<FileResource> {
108 let path = uri.path.clone();
109 let fs = self.fs(uri)?;
110 Ok(FileResource::new(fs, path))
111 }
112
113 /// Gets registered provider names in registration order.
114 ///
115 /// # Returns
116 /// Provider names.
117 #[inline]
118 #[must_use]
119 pub fn provider_names(&self) -> Vec<&str> {
120 self.providers.provider_names()
121 }
122}
123
124/// Maps SPI registry errors into filesystem errors.
125///
126/// # Parameters
127/// - `error`: SPI registry error.
128///
129/// # Returns
130/// Filesystem error preserving provider diagnostic text.
131fn map_provider_error(error: ProviderRegistryError) -> FsError {
132 let kind = match &error {
133 ProviderRegistryError::EmptyProviderName
134 | ProviderRegistryError::InvalidProviderName { .. }
135 | ProviderRegistryError::DuplicateProviderName { .. }
136 | ProviderRegistryError::DuplicateProviderCandidate { .. } => FsErrorKind::InvalidPath,
137 ProviderRegistryError::UnknownProvider { .. }
138 | ProviderRegistryError::ProviderUnavailable { .. }
139 | ProviderRegistryError::NoAvailableProvider { .. }
140 | ProviderRegistryError::EmptyRegistry => FsErrorKind::ProviderUnavailable,
141 ProviderRegistryError::ProviderCreate { .. } => FsErrorKind::Other,
142 };
143 let message = error.to_string();
144 FsError::with_source(kind, FsOperation::Provider, &message, error)
145}