Skip to main content

qubit_fs/
path_constraints.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8// facade.
9//! Immutable path constraints used by filesystem property snapshots.
10
11use crate::error::FsError;
12use crate::error::FsOperation;
13use crate::error::FsResult;
14use crate::path::Path;
15use crate::path::PathForm;
16
17/// Immutable path form constraints attached to one filesystem snapshot.
18///
19/// # Examples
20///
21/// ```rust
22/// use qubit_fs::path::{PathConstraints, PathForm};
23///
24/// let constraints = PathConstraints::absolute();
25/// assert_eq!(PathForm::Absolute, constraints.form());
26/// ```
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub struct PathConstraints {
29    /// Accepted absolute-versus-relative path policy.
30    form: PathForm,
31}
32
33impl PathConstraints {
34    /// Creates constraints accepting only absolute paths.
35    ///
36    /// # Returns
37    /// Constraints rejecting every relative path.
38    #[inline]
39    #[must_use]
40    pub const fn absolute() -> Self {
41        Self {
42            form: PathForm::Absolute,
43        }
44    }
45
46    /// Creates constraints accepting only relative paths.
47    ///
48    /// # Returns
49    /// Constraints rejecting every absolute path.
50    #[inline]
51    #[must_use]
52    pub const fn relative() -> Self {
53        Self {
54            form: PathForm::Relative,
55        }
56    }
57
58    /// Creates constraints accepting either logical path form.
59    ///
60    /// # Returns
61    /// Constraints accepting absolute and relative paths.
62    #[inline]
63    #[must_use]
64    pub const fn either() -> Self {
65        Self { form: PathForm::Either }
66    }
67
68    /// Returns the configured accepted path form.
69    ///
70    /// # Returns
71    /// The immutable accepted path-form policy.
72    #[inline]
73    #[must_use]
74    pub const fn form(&self) -> PathForm {
75        self.form
76    }
77
78    /// Validates a logical path without performing I/O.
79    ///
80    /// # Parameters
81    /// - `path`: Logical path whose absolute or relative form is checked.
82    ///
83    /// # Errors
84    /// Returns an invalid-path error when `path` has a disallowed form.
85    #[inline]
86    pub fn validate(&self, path: &Path) -> FsResult<()> {
87        let allowed = matches!(self.form, PathForm::Either)
88            || matches!(
89                (self.form, path.is_absolute()),
90                (PathForm::Absolute, true) | (PathForm::Relative, false)
91            );
92        if allowed {
93            Ok(())
94        } else {
95            Err(FsError::invalid_path(
96                FsOperation::ParsePath,
97                "path form is not accepted by this filesystem",
98            ))
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use std::hint::black_box;
106
107    use super::PathConstraints;
108    use crate::path::PathForm;
109
110    #[test]
111    fn relative_constructor_is_executed_at_runtime() {
112        let constructor: fn() -> PathConstraints = black_box(PathConstraints::relative);
113        let either_constructor: fn() -> PathConstraints = black_box(PathConstraints::either);
114
115        assert_eq!(PathForm::Relative, constructor().form());
116        assert_eq!(PathForm::Either, either_constructor().form());
117    }
118}