1use crate::{
6 AbsolutePath, ChangeSeq, ContentRef, DisplayName, InodeId, InodeKind, NameKey, NamespaceId,
7 RevisionNo,
8};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
17pub struct AuthoritativePathEntry {
18 pub namespace_id: NamespaceId,
20 pub absolute_path: AbsolutePath,
22 pub inode_id: InodeId,
24 pub inode_kind: InodeKind,
26 pub head_seq: ChangeSeq,
28 pub parent_inode_id: Option<InodeId>,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub display_name: Option<DisplayName>,
33 pub revision_no: Option<RevisionNo>,
35 pub size_bytes: Option<u64>,
37 pub content_ref: Option<ContentRef>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub committed_at_ms: Option<u64>,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
53pub struct ListPathEntriesResponse {
54 pub namespace_id: NamespaceId,
56 pub absolute_path: AbsolutePath,
58 pub head_seq: ChangeSeq,
60 pub entries: Vec<AuthoritativePathEntry>,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub next_cursor: Option<String>,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
73pub struct AuthoritativeFileBytes {
74 pub entry: AuthoritativePathEntry,
76 pub bytes: Vec<u8>,
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 fn entry(
85 path: &str,
86 parent_inode_id: Option<InodeId>,
87 display_name: Option<&str>,
88 ) -> AuthoritativePathEntry {
89 AuthoritativePathEntry {
90 namespace_id: NamespaceId::parse("demo").expect("namespace id"),
91 absolute_path: AbsolutePath::parse(path).expect("absolute path"),
92 inode_id: InodeId(if parent_inode_id.is_some() { 2 } else { 1 }),
93 inode_kind: InodeKind::Directory,
94 head_seq: ChangeSeq(3),
95 parent_inode_id,
96 display_name: display_name.map(|name| DisplayName::parse(name).expect("display name")),
97 revision_no: None,
98 size_bytes: None,
99 content_ref: None,
100 committed_at_ms: None,
101 }
102 }
103
104 #[test]
105 fn authoritative_entry_paths_keep_the_plain_string_wire_shape() {
106 let named = entry("/docs", Some(InodeId(1)), Some("docs"));
107 assert_eq!(
108 serde_json::to_value(&named).expect("serialize named entry"),
109 serde_json::json!({
110 "namespace_id": "demo",
111 "absolute_path": "/docs",
112 "inode_id": 2,
113 "inode_kind": "dir",
114 "head_seq": 3,
115 "parent_inode_id": 1,
116 "display_name": "docs",
117 "revision_no": null,
118 "size_bytes": null,
119 "content_ref": null
120 })
121 );
122
123 let response = ListPathEntriesResponse {
124 namespace_id: NamespaceId::parse("demo").expect("namespace id"),
125 absolute_path: AbsolutePath::parse("/").expect("absolute path"),
126 head_seq: ChangeSeq(3),
127 entries: vec![named],
128 next_cursor: None,
129 };
130 assert_eq!(
131 serde_json::to_value(response).expect("serialize listing"),
132 serde_json::json!({
133 "namespace_id": "demo",
134 "absolute_path": "/",
135 "head_seq": 3,
136 "entries": [{
137 "namespace_id": "demo",
138 "absolute_path": "/docs",
139 "inode_id": 2,
140 "inode_kind": "dir",
141 "head_seq": 3,
142 "parent_inode_id": 1,
143 "display_name": "docs",
144 "revision_no": null,
145 "size_bytes": null,
146 "content_ref": null
147 }]
148 })
149 );
150 }
151
152 #[test]
153 fn nameless_root_omits_display_name_while_named_entries_carry_it() {
154 let root_json = serde_json::to_value(entry("/", None, None)).expect("serialize root");
155 assert!(root_json.get("display_name").is_none());
156
157 let named_json = serde_json::to_value(entry("/docs", Some(InodeId(1)), Some("docs")))
158 .expect("serialize named entry");
159 assert_eq!(named_json["display_name"], "docs");
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
169pub struct TrashEntry {
170 pub root_inode_id: InodeId,
172 pub deleted_at_seq: ChangeSeq,
174 pub deleted_at_ms: u64,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub parent_inode_id: Option<InodeId>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub name_key: Option<NameKey>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub display_name: Option<DisplayName>,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
190pub struct ListTrashResponse {
191 pub namespace_id: NamespaceId,
193 pub head_seq: ChangeSeq,
195 pub entries: Vec<TrashEntry>,
197 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub next_cursor: Option<String>,
200}