Skip to main content

qubit_fs/metadata/
file_system_info.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Immutable configured filesystem information.
9
10use std::fmt::Display;
11
12use crate::error::FsResult;
13use crate::metadata::FileSystemId;
14use crate::metadata::NonSensitiveMetadata;
15use crate::metadata::UserMetadata;
16use crate::path::PathSemantics;
17use crate::path::Uri;
18
19/// Construction-time local snapshot describing one filesystem object.
20///
21/// # Examples
22///
23/// ```
24/// use qubit_fs::metadata::FileSystemId;
25/// use qubit_fs::metadata::FileSystemInfo;
26/// use qubit_fs::path::PathSemantics;
27///
28/// let info = FileSystemInfo::new(
29///     FileSystemId::new("local-instance")?,
30///     "local",
31///     PathSemantics::Hierarchical,
32/// );
33/// assert_eq!("local", info.provider_id());
34/// # Ok::<(), qubit_fs::FsError>(())
35/// ```
36#[derive(Clone, Debug, PartialEq)]
37pub struct FileSystemInfo {
38    /// Stable identity of the configured filesystem.
39    id: FileSystemId,
40    /// Stable identity of the provider implementation.
41    provider_id: Box<str>,
42    /// Validated URI schemes accepted by this filesystem.
43    schemes: Vec<String>,
44    /// Logical path model used by the provider.
45    path_semantics: PathSemantics,
46    /// Provider metadata safe for automatic structural formatting.
47    provider_metadata: NonSensitiveMetadata,
48}
49
50impl FileSystemInfo {
51    /// Creates a filesystem information snapshot without scheme aliases.
52    #[inline]
53    #[must_use]
54    pub fn new(id: FileSystemId, provider_id: impl Display, path_semantics: PathSemantics) -> Self {
55        Self {
56            id,
57            provider_id: provider_id.to_string().into(),
58            schemes: Vec::new(),
59            path_semantics,
60            provider_metadata: NonSensitiveMetadata::new(),
61        }
62    }
63
64    /// Adds one validated supported URI scheme.
65    ///
66    /// # Errors
67    ///
68    /// Returns an invalid-URI error when `scheme` is not a valid URI scheme.
69    pub fn with_scheme(mut self, scheme: &str) -> FsResult<Self> {
70        let scheme = Uri::parse(&format!("{scheme}:/"))?.scheme().to_owned();
71        if !self.schemes.contains(&scheme) {
72            self.schemes.push(scheme);
73        }
74        Ok(self)
75    }
76
77    /// Replaces the scrubbed provider metadata snapshot.
78    ///
79    /// `metadata` has already rejected credential-like keys. Providers must
80    /// expose secrets only through an external credential boundary, never
81    /// through this debug-visible local snapshot.
82    #[inline]
83    #[must_use]
84    pub fn with_provider_metadata(mut self, metadata: UserMetadata) -> Self {
85        self.provider_metadata = NonSensitiveMetadata::from(metadata);
86        self
87    }
88
89    /// Returns the configured filesystem identity.
90    #[inline]
91    #[must_use]
92    pub const fn id(&self) -> &FileSystemId {
93        &self.id
94    }
95
96    /// Returns the provider identity that created this filesystem.
97    #[inline]
98    #[must_use]
99    pub const fn provider_id(&self) -> &str {
100        &self.provider_id
101    }
102
103    /// Returns supported URI schemes in provider-defined order.
104    #[inline]
105    #[must_use]
106    pub fn schemes(&self) -> &[String] {
107        &self.schemes
108    }
109
110    /// Returns the provider-local path semantics.
111    #[inline]
112    #[must_use]
113    pub const fn path_semantics(&self) -> PathSemantics {
114        self.path_semantics
115    }
116
117    /// Returns scrubbed provider-specific information.
118    #[inline]
119    #[must_use]
120    pub const fn provider_metadata(&self) -> &NonSensitiveMetadata {
121        &self.provider_metadata
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::FileSystemInfo;
128    use crate::metadata::FileSystemId;
129    use crate::metadata::UserMetadata;
130    use crate::path::PathSemantics;
131
132    #[test]
133    fn runtime_contract_covers_snapshot_accessors() {
134        let id = FileSystemId::new("file-system-info-test").expect("valid id");
135        let info = FileSystemInfo::new(id.clone(), "provider", PathSemantics::Hierarchical)
136            .with_scheme("file")
137            .expect("valid scheme")
138            .with_provider_metadata(UserMetadata::new());
139
140        assert_eq!(info.id(), &id);
141        assert_eq!(info.provider_id(), "provider");
142        assert_eq!(info.schemes(), &["file".to_owned()]);
143        assert_eq!(info.path_semantics(), PathSemantics::Hierarchical);
144        assert!(info.provider_metadata().is_empty());
145    }
146}