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}