use crate::{
AbsolutePath, ChangeSeq, ContentRef, DisplayName, InodeId, InodeKind, NameKey, NamespaceId,
RevisionNo,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AuthoritativePathEntry {
pub namespace_id: NamespaceId,
pub absolute_path: AbsolutePath,
pub inode_id: InodeId,
pub inode_kind: InodeKind,
pub head_seq: ChangeSeq,
pub parent_inode_id: Option<InodeId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<DisplayName>,
pub revision_no: Option<RevisionNo>,
pub size_bytes: Option<u64>,
pub content_ref: Option<ContentRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub committed_at_ms: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ListPathEntriesResponse {
pub namespace_id: NamespaceId,
pub absolute_path: AbsolutePath,
pub head_seq: ChangeSeq,
pub entries: Vec<AuthoritativePathEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AuthoritativeFileBytes {
pub entry: AuthoritativePathEntry,
pub bytes: Vec<u8>,
}
#[cfg(test)]
mod tests {
use super::*;
fn entry(
path: &str,
parent_inode_id: Option<InodeId>,
display_name: Option<&str>,
) -> AuthoritativePathEntry {
AuthoritativePathEntry {
namespace_id: NamespaceId::parse("demo").expect("namespace id"),
absolute_path: AbsolutePath::parse(path).expect("absolute path"),
inode_id: InodeId(if parent_inode_id.is_some() { 2 } else { 1 }),
inode_kind: InodeKind::Directory,
head_seq: ChangeSeq(3),
parent_inode_id,
display_name: display_name.map(|name| DisplayName::parse(name).expect("display name")),
revision_no: None,
size_bytes: None,
content_ref: None,
committed_at_ms: None,
}
}
#[test]
fn authoritative_entry_paths_keep_the_plain_string_wire_shape() {
let named = entry("/docs", Some(InodeId(1)), Some("docs"));
assert_eq!(
serde_json::to_value(&named).expect("serialize named entry"),
serde_json::json!({
"namespace_id": "demo",
"absolute_path": "/docs",
"inode_id": 2,
"inode_kind": "dir",
"head_seq": 3,
"parent_inode_id": 1,
"display_name": "docs",
"revision_no": null,
"size_bytes": null,
"content_ref": null
})
);
let response = ListPathEntriesResponse {
namespace_id: NamespaceId::parse("demo").expect("namespace id"),
absolute_path: AbsolutePath::parse("/").expect("absolute path"),
head_seq: ChangeSeq(3),
entries: vec![named],
next_cursor: None,
};
assert_eq!(
serde_json::to_value(response).expect("serialize listing"),
serde_json::json!({
"namespace_id": "demo",
"absolute_path": "/",
"head_seq": 3,
"entries": [{
"namespace_id": "demo",
"absolute_path": "/docs",
"inode_id": 2,
"inode_kind": "dir",
"head_seq": 3,
"parent_inode_id": 1,
"display_name": "docs",
"revision_no": null,
"size_bytes": null,
"content_ref": null
}]
})
);
}
#[test]
fn nameless_root_omits_display_name_while_named_entries_carry_it() {
let root_json = serde_json::to_value(entry("/", None, None)).expect("serialize root");
assert!(root_json.get("display_name").is_none());
let named_json = serde_json::to_value(entry("/docs", Some(InodeId(1)), Some("docs")))
.expect("serialize named entry");
assert_eq!(named_json["display_name"], "docs");
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct TrashEntry {
pub root_inode_id: InodeId,
pub deleted_at_seq: ChangeSeq,
pub deleted_at_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_inode_id: Option<InodeId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name_key: Option<NameKey>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<DisplayName>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ListTrashResponse {
pub namespace_id: NamespaceId,
pub head_seq: ChangeSeq,
pub entries: Vec<TrashEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}