aster_forge_cloud_files_core/
item.rs1use crate::{
4 CloudFilesCoreError, CloudItemId, CloudItemKey, ContentDigest, ContentRevision,
5 MetadataRevision, PageCursor, Result,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum CloudItemKind {
11 File,
13 Directory,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct CloudContentMetadata {
20 revision: ContentRevision,
21 digest: Option<ContentDigest>,
22 size: u64,
23}
24
25impl CloudContentMetadata {
26 #[must_use]
28 pub const fn new(revision: ContentRevision, digest: Option<ContentDigest>, size: u64) -> Self {
29 Self {
30 revision,
31 digest,
32 size,
33 }
34 }
35
36 #[must_use]
38 pub const fn revision(&self) -> &ContentRevision {
39 &self.revision
40 }
41
42 #[must_use]
44 pub const fn digest(&self) -> Option<&ContentDigest> {
45 self.digest.as_ref()
46 }
47
48 #[must_use]
50 pub const fn size(&self) -> u64 {
51 self.size
52 }
53
54 #[must_use]
56 pub fn into_parts(self) -> (ContentRevision, Option<ContentDigest>, u64) {
57 (self.revision, self.digest, self.size)
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct CloudItem {
64 key: CloudItemKey,
65 parent_id: Option<CloudItemId>,
66 name: String,
67 kind: CloudItemKind,
68 metadata_revision: MetadataRevision,
69 content: Option<CloudContentMetadata>,
70}
71
72impl CloudItem {
73 pub fn directory(
79 key: CloudItemKey,
80 parent_id: Option<CloudItemId>,
81 name: impl Into<String>,
82 metadata_revision: MetadataRevision,
83 ) -> Result<Self> {
84 let name = name.into();
85 validate_item_location(&key, parent_id.as_ref(), &name)?;
86 Ok(Self {
87 key,
88 parent_id,
89 name,
90 kind: CloudItemKind::Directory,
91 metadata_revision,
92 content: None,
93 })
94 }
95
96 pub fn file(
102 key: CloudItemKey,
103 parent_id: CloudItemId,
104 name: impl Into<String>,
105 metadata_revision: MetadataRevision,
106 content: CloudContentMetadata,
107 ) -> Result<Self> {
108 let name = name.into();
109 validate_item_location(&key, Some(&parent_id), &name)?;
110 Ok(Self {
111 key,
112 parent_id: Some(parent_id),
113 name,
114 kind: CloudItemKind::File,
115 metadata_revision,
116 content: Some(content),
117 })
118 }
119
120 #[must_use]
122 pub const fn key(&self) -> &CloudItemKey {
123 &self.key
124 }
125
126 #[must_use]
128 pub const fn parent_id(&self) -> Option<&CloudItemId> {
129 self.parent_id.as_ref()
130 }
131
132 #[must_use]
134 pub fn name(&self) -> &str {
135 &self.name
136 }
137
138 #[must_use]
140 pub const fn kind(&self) -> CloudItemKind {
141 self.kind
142 }
143
144 #[must_use]
146 pub const fn metadata_revision(&self) -> &MetadataRevision {
147 &self.metadata_revision
148 }
149
150 #[must_use]
152 pub const fn content(&self) -> Option<&CloudContentMetadata> {
153 self.content.as_ref()
154 }
155
156 #[must_use]
158 pub const fn is_root(&self) -> bool {
159 self.parent_id.is_none()
160 }
161
162 pub fn moved(
168 mut self,
169 parent_id: CloudItemId,
170 name: impl Into<String>,
171 metadata_revision: MetadataRevision,
172 ) -> Result<Self> {
173 if self.is_root() {
174 return Err(CloudFilesCoreError::invalid_item(
175 "the root item must not be moved",
176 ));
177 }
178 let name = name.into();
179 if name.is_empty() {
180 return Err(CloudFilesCoreError::empty("cloud item name"));
181 }
182 if &parent_id == self.key.item_id() {
183 return Err(CloudFilesCoreError::invalid_item(
184 "an item must not be its own parent",
185 ));
186 }
187 self.parent_id = Some(parent_id);
188 self.name = name;
189 self.metadata_revision = metadata_revision;
190 Ok(self)
191 }
192}
193
194fn validate_item_location(
195 key: &CloudItemKey,
196 parent_id: Option<&CloudItemId>,
197 name: &str,
198) -> Result<()> {
199 if name.is_empty() {
200 return Err(CloudFilesCoreError::empty("cloud item name"));
201 }
202 if parent_id == Some(key.item_id()) {
203 return Err(CloudFilesCoreError::invalid_item(
204 "an item must not be its own parent",
205 ));
206 }
207 Ok(())
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
212pub struct CloudItemPage {
213 items: Vec<CloudItem>,
214 next_cursor: Option<PageCursor>,
215}
216
217impl CloudItemPage {
218 #[must_use]
220 pub const fn new(items: Vec<CloudItem>, next_cursor: Option<PageCursor>) -> Self {
221 Self { items, next_cursor }
222 }
223
224 #[must_use]
226 pub fn items(&self) -> &[CloudItem] {
227 &self.items
228 }
229
230 #[must_use]
232 pub const fn next_cursor(&self) -> Option<&PageCursor> {
233 self.next_cursor.as_ref()
234 }
235
236 #[must_use]
238 pub fn into_parts(self) -> (Vec<CloudItem>, Option<PageCursor>) {
239 (self.items, self.next_cursor)
240 }
241}