Skip to main content

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}