Skip to main content

qubit_fs/provider/
file_systems.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//! Global filesystem registry facade.
11
12use std::sync::{
13    Arc,
14    OnceLock,
15};
16
17use parking_lot::RwLock;
18use qubit_spi::ServiceProvider;
19
20use crate::{
21    FileResource,
22    FileSystem,
23    FileSystemProvider,
24    FileSystemRegistry,
25    FileSystemSpec,
26    FsResult,
27    FsUri,
28};
29
30static GLOBAL_REGISTRY: OnceLock<RwLock<FileSystemRegistry>> = OnceLock::new();
31
32/// Global filesystem registry facade.
33///
34/// `FileSystems` is an uninhabited namespace type. It cannot be instantiated
35/// and only exposes static methods backed by a process-wide registry.
36pub enum FileSystems {}
37
38impl FileSystems {
39    fn registry() -> &'static RwLock<FileSystemRegistry> {
40        GLOBAL_REGISTRY.get_or_init(|| RwLock::new(FileSystemRegistry::new()))
41    }
42
43    /// Registers a filesystem provider in the global registry.
44    ///
45    /// # Parameters
46    /// - `provider`: Filesystem provider to register.
47    ///
48    /// # Errors
49    /// Returns an error when a provider with the same descriptor already exists.
50    pub fn register<P>(provider: P) -> FsResult<()>
51    where
52        P: ServiceProvider<FileSystemSpec> + 'static,
53    {
54        let mut registry = Self::registry().write();
55        registry.register(provider)
56    }
57
58    /// Registers a shared filesystem provider in the global registry.
59    ///
60    /// # Parameters
61    /// - `provider`: Shared filesystem provider to register.
62    ///
63    /// # Errors
64    /// Returns an error when a provider with the same descriptor already exists.
65    pub fn register_shared(provider: Arc<FileSystemProvider>) -> FsResult<()> {
66        let mut registry = Self::registry().write();
67        registry.register_shared(provider)
68    }
69
70    /// Resolves a URI into a filesystem instance from the global registry.
71    ///
72    /// # Parameters
73    /// - `uri`: Filesystem URI.
74    ///
75    /// # Returns
76    /// A filesystem instance created by the matching provider.
77    ///
78    /// # Errors
79    /// Returns an error when the URI cannot be parsed or no provider can create
80    /// a filesystem for the URI scheme.
81    pub fn fs(uri: &str) -> FsResult<Arc<dyn FileSystem>> {
82        let uri = FsUri::parse(uri)?;
83        Self::fs_for_uri(&uri)
84    }
85
86    /// Resolves a parsed URI into a filesystem instance from the global registry.
87    ///
88    /// # Parameters
89    /// - `uri`: Parsed filesystem URI.
90    ///
91    /// # Returns
92    /// A filesystem instance created by the matching provider.
93    ///
94    /// # Errors
95    /// Returns an error when no provider can create a filesystem for the URI
96    /// scheme.
97    pub fn fs_for_uri(uri: &FsUri) -> FsResult<Arc<dyn FileSystem>> {
98        let registry = Self::registry().read();
99        registry.fs(uri)
100    }
101
102    /// Resolves a filesystem instance from a URI scheme.
103    ///
104    /// This is equivalent to resolving the minimal URI `{scheme}:///`. It is
105    /// only suitable for providers that can be created from default authority,
106    /// root path, and default options.
107    ///
108    /// # Parameters
109    /// - `scheme`: URI scheme used to select a provider.
110    ///
111    /// # Returns
112    /// A filesystem instance created by the matching provider.
113    ///
114    /// # Errors
115    /// Returns an error when the scheme cannot form a valid URI or no provider
116    /// can create a filesystem from the minimal URI.
117    pub fn fs_for_scheme(scheme: &str) -> FsResult<Arc<dyn FileSystem>> {
118        let uri = FsUri::parse(&format!("{scheme}:///"))?;
119        Self::fs_for_uri(&uri)
120    }
121
122    /// Resolves a URI into a bound file resource from the global registry.
123    ///
124    /// # Parameters
125    /// - `uri`: Filesystem URI.
126    ///
127    /// # Returns
128    /// A file resource containing the matching filesystem and filesystem-local
129    /// path.
130    ///
131    /// # Errors
132    /// Returns an error when the URI cannot be parsed or no provider can create
133    /// a filesystem for the URI scheme.
134    pub fn resource(uri: &str) -> FsResult<FileResource> {
135        let uri = FsUri::parse(uri)?;
136        Self::resource_for_uri(&uri)
137    }
138
139    /// Resolves a parsed URI into a bound file resource from the global registry.
140    ///
141    /// # Parameters
142    /// - `uri`: Parsed filesystem URI.
143    ///
144    /// # Returns
145    /// A file resource containing the matching filesystem and filesystem-local
146    /// path.
147    ///
148    /// # Errors
149    /// Returns an error when no provider can create a filesystem for the URI
150    /// scheme.
151    pub fn resource_for_uri(uri: &FsUri) -> FsResult<FileResource> {
152        let registry = Self::registry().read();
153        registry.resource(uri)
154    }
155
156    /// Lists provider names registered in the global registry.
157    ///
158    /// # Returns
159    /// Registered provider names.
160    #[must_use]
161    pub fn provider_names() -> Vec<String> {
162        let registry = Self::registry().read();
163        registry.provider_names().into_iter().map(str::to_owned).collect()
164    }
165}