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}