1use arkhe_kernel::abi::{EntityId, Tick};
4use serde::{Deserialize, Serialize};
5
6use crate::actor::ActorId;
7use crate::brand::ShellId;
8use crate::component::BoundedString;
9use crate::pii::{AeadKind, DekId};
10use crate::space::SpaceId;
11use crate::ArkheComponent;
12
13#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct EntryId(EntityId);
17
18impl EntryId {
19 #[inline]
23 #[must_use]
24 pub fn new(id: EntityId) -> Self {
25 Self(id)
26 }
27
28 #[inline]
30 #[must_use]
31 pub fn get(self) -> EntityId {
32 self.0
33 }
34}
35
36#[non_exhaustive]
38#[repr(u8)]
39#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
40pub enum RelayKind {
41 Plain = 0,
43 Quote = 1,
45}
46
47#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, ArkheComponent)]
49#[arkhe(type_code = 0x0003_0301, schema_version = 1)]
50pub struct EntryCore {
51 pub schema_version: u16,
53 pub shell_id: ShellId,
55 pub space_id: SpaceId,
57 pub author_id: ActorId,
59 pub parent_entry: Option<EntryId>,
61 pub relay_of: Option<EntryId>,
63 pub relay_kind: Option<RelayKind>,
65 pub created_tick: Tick,
67}
68
69#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, ArkheComponent)]
73#[arkhe(type_code = 0x0003_0302, schema_version = 1)]
74pub struct EntryBody {
75 pub schema_version: u16,
77 pub title: Option<BoundedString<256>>,
79 pub body_hash: [u8; 32],
81 pub body_cipher_meta: Option<BodyCipherMeta>,
84 pub edit_seq: u32,
86}
87
88#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
91pub struct BodyCipherMeta {
92 pub dek_id: DekId,
94 pub aead_kind: AeadKind,
96 pub nonce: [u8; 24],
98}
99
100#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, ArkheComponent)]
102#[arkhe(type_code = 0x0003_0303, schema_version = 1)]
103pub struct EntryParentDepth {
104 pub schema_version: u16,
106 pub depth: u8,
108}
109
110pub const MAX_ENTRY_DEPTH: u8 = 64;
112
113#[cfg(test)]
114#[allow(clippy::unwrap_used, clippy::expect_used)]
115mod tests {
116 use super::*;
117 use crate::component::ArkheComponent;
118
119 fn ent(v: u64) -> EntityId {
120 EntityId::new(v).unwrap()
121 }
122
123 #[test]
124 fn entry_core_serde_roundtrip_postcard() {
125 let ec = EntryCore {
126 schema_version: 1,
127 shell_id: ShellId([0u8; 16]),
128 space_id: SpaceId::new(ent(1)),
129 author_id: ActorId::new(ent(2)),
130 parent_entry: None,
131 relay_of: None,
132 relay_kind: None,
133 created_tick: Tick(10),
134 };
135 let bytes = postcard::to_stdvec(&ec).unwrap();
136 let back: EntryCore = postcard::from_bytes(&bytes).unwrap();
137 assert_eq!(ec, back);
138 }
139
140 #[test]
141 fn entry_body_serde_roundtrip_with_cipher_meta() {
142 let body = EntryBody {
143 schema_version: 1,
144 title: Some(BoundedString::<256>::new("hello").unwrap()),
145 body_hash: [0xABu8; 32],
146 body_cipher_meta: Some(BodyCipherMeta {
147 dek_id: DekId([0x11u8; 16]),
148 aead_kind: AeadKind::XChaCha20Poly1305,
149 nonce: [0x22u8; 24],
150 }),
151 edit_seq: 1,
152 };
153 let bytes = postcard::to_stdvec(&body).unwrap();
154 let back: EntryBody = postcard::from_bytes(&bytes).unwrap();
155 assert_eq!(body, back);
156 }
157
158 #[test]
159 fn entry_component_type_codes() {
160 assert_eq!(EntryCore::TYPE_CODE, 0x0003_0301);
161 assert_eq!(EntryBody::TYPE_CODE, 0x0003_0302);
162 assert_eq!(EntryParentDepth::TYPE_CODE, 0x0003_0303);
163 }
164
165 #[test]
166 fn max_entry_depth_is_sixty_four() {
167 assert_eq!(MAX_ENTRY_DEPTH, 64);
168 }
169}