aster_forge_cloud_files_core/
identity.rs

1//! Stable scoped identities independent from local paths and native platform handles.
2
3use std::fmt;
4
5use crate::{CloudFilesCoreError, Result};
6
7macro_rules! string_identity {
8    ($name:ident, $field:literal, $docs:literal) => {
9        #[doc = $docs]
10        #[derive(Clone, PartialEq, Eq, Hash)]
11        pub struct $name(String);
12
13        impl $name {
14            /// Creates an identity while preserving the caller-provided opaque value exactly.
15            /// # Errors
16            ///
17            /// Returns an error when validation fails or an underlying backend, store, or platform
18            /// operation fails.
19            pub fn new(value: impl Into<String>) -> Result<Self> {
20                let value = value.into();
21                if value.is_empty() {
22                    return Err(CloudFilesCoreError::empty($field));
23                }
24                Ok(Self(value))
25            }
26
27            /// Returns the opaque identity value.
28            pub fn as_str(&self) -> &str {
29                &self.0
30            }
31
32            /// Consumes the identity and returns its opaque value.
33            pub fn into_string(self) -> String {
34                self.0
35            }
36        }
37
38        impl fmt::Debug for $name {
39            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40                formatter
41                    .debug_tuple(stringify!($name))
42                    .field(&self.0)
43                    .finish()
44            }
45        }
46
47        impl fmt::Display for $name {
48            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
49                formatter.write_str(&self.0)
50            }
51        }
52    };
53}
54
55string_identity!(
56    CloudNamespaceId,
57    "cloud namespace id",
58    "Opaque identity namespace used to isolate unrelated backend identity spaces."
59);
60string_identity!(
61    CloudRootId,
62    "cloud root id",
63    "Stable root identity within one cloud namespace."
64);
65string_identity!(
66    CloudItemId,
67    "cloud item id",
68    "Stable path-independent item identity within one cloud root."
69);
70
71/// Namespace and root pair that scopes item identities.
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
73pub struct CloudScope {
74    namespace_id: CloudNamespaceId,
75    root_id: CloudRootId,
76}
77
78impl CloudScope {
79    /// Creates a scoped root identity.
80    #[must_use]
81    pub const fn new(namespace_id: CloudNamespaceId, root_id: CloudRootId) -> Self {
82        Self {
83            namespace_id,
84            root_id,
85        }
86    }
87
88    /// Returns the backend identity namespace.
89    #[must_use]
90    pub const fn namespace_id(&self) -> &CloudNamespaceId {
91        &self.namespace_id
92    }
93
94    /// Returns the stable root identity.
95    #[must_use]
96    pub const fn root_id(&self) -> &CloudRootId {
97        &self.root_id
98    }
99
100    /// Consumes the scope and returns its namespace and root identities.
101    #[must_use]
102    pub fn into_parts(self) -> (CloudNamespaceId, CloudRootId) {
103        (self.namespace_id, self.root_id)
104    }
105}
106
107/// Fully scoped identity for one cloud item.
108///
109/// Paths, names, local inodes, CFAPI identity blobs, and File Provider identifiers are adapter
110/// state and are deliberately absent from this key.
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub struct CloudItemKey {
113    scope: CloudScope,
114    item_id: CloudItemId,
115}
116
117impl CloudItemKey {
118    /// Creates a fully scoped item key.
119    #[must_use]
120    pub const fn new(scope: CloudScope, item_id: CloudItemId) -> Self {
121        Self { scope, item_id }
122    }
123
124    /// Returns the namespace/root scope.
125    #[must_use]
126    pub const fn scope(&self) -> &CloudScope {
127        &self.scope
128    }
129
130    /// Returns the stable path-independent item identity.
131    #[must_use]
132    pub const fn item_id(&self) -> &CloudItemId {
133        &self.item_id
134    }
135
136    /// Consumes the key and returns its scope and item identity.
137    #[must_use]
138    pub fn into_parts(self) -> (CloudScope, CloudItemId) {
139        (self.scope, self.item_id)
140    }
141}