harmos_domain/model/storage/
objects.rs1pub mod codec;
13
14pub use codec::Key;
17
18use std::borrow::Cow;
22use std::num::{NonZeroU64, NonZeroUsize};
23
24use serde::{Deserialize, Serialize};
26
27use crate::Identifier;
29use crate::model::storage::keys::invalid;
30use crate::model::storage::{ObjectKey, ObjectValue, StorageCursor, StoreHistory};
31use crate::schema::declaration::{Field, Kind};
32use crate::schema::{Cardinality, Described};
33use crate::{MemorySize, Message, Result};
34
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(deny_unknown_fields)]
45pub struct ObjectEntry {
46 pub position: StorageCursor,
47 pub version: ObjectVersionToken,
48 pub value: ObjectValue,
49}
50
51#[derive(Clone, Debug)]
52pub struct ObjectRevision<T> {
53 pub position: StorageCursor,
54 pub version: ObjectVersionToken,
55 pub value: T,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(deny_unknown_fields)]
60pub struct ObjectPoint {
61 pub schema: Identifier,
62 pub item: ObjectKey,
63 pub version: ObjectVersion,
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
67#[serde(deny_unknown_fields)]
68pub struct ObjectVersionToken {
69 pub(crate) history: StoreHistory,
70 pub(crate) revision: NonZeroU64,
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
74pub struct ObjectReceipt {
75 pub position: StorageCursor,
76 pub version: ObjectVersionToken,
77}
78
79#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
82pub enum ObjectVersion {
83 Latest,
84 Exact(ObjectVersionToken),
85}
86
87impl MemorySize for ObjectEntry {
92 fn memory_size(&self) -> usize {
93 std::mem::size_of::<Self>()
94 .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
95 .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
96 .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<ObjectValue>()))
97 }
98}
99
100impl<T: MemorySize> MemorySize for ObjectRevision<T> {
101 fn memory_size(&self) -> usize {
102 std::mem::size_of::<Self>()
103 .saturating_add(self.position.memory_size().saturating_sub(std::mem::size_of::<StorageCursor>()))
104 .saturating_add(self.version.memory_size().saturating_sub(std::mem::size_of::<ObjectVersionToken>()))
105 .saturating_add(self.value.memory_size().saturating_sub(std::mem::size_of::<T>()))
106 }
107}
108
109impl MemorySize for ObjectReceipt {
110 fn memory_size(&self) -> usize {
111 std::mem::size_of::<Self>() + self.position.memory_size() - std::mem::size_of::<StorageCursor>() + self.version.memory_size()
112 - std::mem::size_of::<ObjectVersionToken>()
113 }
114}
115
116impl MemorySize for ObjectVersionToken {
117 fn memory_size(&self) -> usize {
118 std::mem::size_of::<Self>().saturating_add(self.history.memory_size().saturating_sub(std::mem::size_of::<StoreHistory>()))
119 }
120}
121
122impl ObjectVersionToken {
123 pub fn history(&self) -> &StoreHistory {
124 &self.history
125 }
126
127 pub fn revision(&self) -> NonZeroU64 {
128 self.revision
129 }
130
131 pub fn encode_bounded(&self, max_bytes: NonZeroUsize) -> Result<Vec<u8>> {
132 if self.encoded_len() > max_bytes.get() {
133 return Err(invalid("object version encoding exceeds allowance"));
134 }
135 Ok(self.encode_to_vec())
136 }
137
138 pub fn decode_bounded(bytes: &[u8], max_bytes: NonZeroUsize) -> Result<Self> {
139 let required = Self::decoding_memory(bytes).map_err(|_| invalid("invalid object version encoding"))?;
140 if required > max_bytes.get() {
141 return Err(invalid("object version decoding exceeds allowance"));
142 }
143 let token = Self::decode(bytes).map_err(|_| invalid("invalid object version encoding"))?;
144 if token.memory_size() > max_bytes.get() {
145 return Err(invalid("object version decoding exceeds allowance"));
146 }
147 Ok(token)
148 }
149
150 pub fn new(position: StorageCursor) -> Self {
151 Self { history: position.history, revision: NonZeroU64::new(position.sequence).expect("admission positions are nonzero") }
152 }
153
154 pub fn check_position(&self, position: &StorageCursor) -> Result<()> {
155 self.history.check(&position.history)?;
156 if self.revision.get() != position.sequence {
157 return Err(invalid("object version does not match its admission identity"));
158 }
159 Ok(())
160 }
161}
162
163crate::postcard_product!(ObjectVersionToken { history: StoreHistory, revision: NonZeroU64 });
164
165impl Described for ObjectVersionToken {
166 const NAME: &'static str = concat!(module_path!(), "::", stringify!(ObjectVersionToken));
167 const FIELDS: &'static [Field] = &[
168 Field { name: Cow::Borrowed("store"), tag: 1, kind: Kind::String, cardinality: Cardinality::Required },
169 Field { name: Cow::Borrowed("epoch"), tag: 2, kind: Kind::String, cardinality: Cardinality::Required },
170 Field { name: Cow::Borrowed("revision"), tag: 3, kind: Kind::U64, cardinality: Cardinality::Required },
171 ];
172
173 const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
174 { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
175 >(Self::NAME, Self::FIELDS);
176}
177
178