prikk_object/payload/
tag.rs1use prikk_error::{PrikkError, Result};
26
27use crate::canonical::WireType;
28use crate::payload::common::PatchSetDigest;
29use crate::{CanonicalEncode, CanonicalWriter, ObjectId};
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct TagPayload {
34 pub name: String,
36 pub target_block_id: ObjectId,
38 pub message: Option<String>,
40 pub created_at: u64,
45 pub author_key_id: String,
47 pub patch_set_digest: PatchSetDigest,
51 pub patch_count: u64,
55}
56
57impl CanonicalEncode for TagPayload {
58 fn encode_canonical(&self, writer: &mut CanonicalWriter) -> Result<()> {
59 writer.field_string(1, &self.name)?;
60 writer.field_object_id(2, &self.target_block_id)?;
61 if let Some(message) = &self.message {
62 writer.field_string(3, message)?;
63 }
64 writer.field_u64(4, self.created_at)?;
65 writer.field_string(5, &self.author_key_id)?;
66 writer.field_bytes(6, &self.patch_set_digest.0)?;
67 writer.field_u64(7, self.patch_count)?;
68 Ok(())
69 }
70}
71
72impl TagPayload {
73 pub fn decode_canonical(bytes: &[u8]) -> Result<Self> {
75 let mut cursor = TagCursor::new(bytes);
76 let mut name = None;
77 let mut target_block_id = None;
78 let mut message = None;
79 let mut created_at = None;
80 let mut author_key_id = None;
81 let mut patch_set_digest = None;
82 let mut patch_count = None;
83 while let Some(field) = cursor.next_field()? {
84 match field.tag {
85 1 => name = Some(field.read_string()?),
86 2 => target_block_id = Some(field.read_object_id()?),
87 3 => message = Some(field.read_string()?),
88 4 => created_at = Some(field.read_u64()?),
89 5 => author_key_id = Some(field.read_string()?),
90 6 => patch_set_digest = Some(PatchSetDigest(field.read_array::<32>()?)),
91 7 => patch_count = Some(field.read_u64()?),
92 other => {
93 return Err(PrikkError::MalformedData(format!(
94 "unknown Tag field tag: {other}"
95 )));
96 }
97 }
98 }
99 Ok(Self {
100 name: name.ok_or_else(|| PrikkError::MalformedData("Tag missing name".to_string()))?,
101 target_block_id: target_block_id.ok_or_else(|| {
102 PrikkError::MalformedData("Tag missing target_block_id".to_string())
103 })?,
104 message,
105 created_at: created_at
106 .ok_or_else(|| PrikkError::MalformedData("Tag missing created_at".to_string()))?,
107 author_key_id: author_key_id.ok_or_else(|| {
108 PrikkError::MalformedData("Tag missing author_key_id".to_string())
109 })?,
110 patch_set_digest: patch_set_digest.ok_or_else(|| {
111 PrikkError::MalformedData("Tag missing patch_set_digest".to_string())
112 })?,
113 patch_count: patch_count
114 .ok_or_else(|| PrikkError::MalformedData("Tag missing patch_count".to_string()))?,
115 })
116 }
117}
118
119struct TagCursor<'a> {
120 bytes: &'a [u8],
121 pos: usize,
122 last_tag: Option<u16>,
123}
124
125impl<'a> TagCursor<'a> {
126 const fn new(bytes: &'a [u8]) -> Self {
127 Self {
128 bytes,
129 pos: 0,
130 last_tag: None,
131 }
132 }
133
134 fn next_field(&mut self) -> Result<Option<TagField<'a>>> {
135 if self.pos == self.bytes.len() {
136 return Ok(None);
137 }
138 let tag = u16::from_be_bytes(self.read_array::<2>()?);
139 if tag == 0 {
140 return Err(PrikkError::MalformedData(
141 "field tag 0 is reserved".to_string(),
142 ));
143 }
144 if let Some(last) = self.last_tag {
145 if tag < last {
146 return Err(PrikkError::MalformedData(format!(
147 "field tag order violation: {tag} after {last}"
148 )));
149 }
150 }
151 self.last_tag = Some(tag);
152 let wire_type = self.read_u8()?;
153 let len = usize::try_from(u64::from_be_bytes(self.read_array::<8>()?)).map_err(|_| {
154 PrikkError::MalformedData("canonical field length does not fit usize".to_string())
155 })?;
156 let value = self.read_exact(len)?;
157 Ok(Some(TagField {
158 tag,
159 wire_type,
160 value,
161 }))
162 }
163
164 fn read_u8(&mut self) -> Result<u8> {
165 let value = self.read_exact(1)?;
166 let Some(byte) = value.first() else {
167 return Err(PrikkError::MalformedData(
168 "unexpected empty byte".to_string(),
169 ));
170 };
171 Ok(*byte)
172 }
173
174 fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
175 let bytes = self.read_exact(N)?;
176 let mut out = [0_u8; N];
177 out.copy_from_slice(bytes);
178 Ok(out)
179 }
180
181 fn read_exact(&mut self, len: usize) -> Result<&'a [u8]> {
182 let end = self
183 .pos
184 .checked_add(len)
185 .ok_or_else(|| PrikkError::MalformedData("canonical range overflow".to_string()))?;
186 let Some(slice) = self.bytes.get(self.pos..end) else {
187 return Err(PrikkError::MalformedData(
188 "unexpected end of canonical payload".to_string(),
189 ));
190 };
191 self.pos = end;
192 Ok(slice)
193 }
194}
195
196struct TagField<'a> {
197 tag: u16,
198 wire_type: u8,
199 value: &'a [u8],
200}
201
202impl<'a> TagField<'a> {
203 fn read_string(&self) -> Result<String> {
204 self.require_wire(WireType::String)?;
205 String::from_utf8(self.value.to_vec())
206 .map_err(|err| PrikkError::MalformedData(format!("invalid UTF-8 string: {err}")))
207 }
208
209 fn read_u64(&self) -> Result<u64> {
210 self.require_wire(WireType::U64)?;
211 Ok(u64::from_be_bytes(self.read_array::<8>()?))
212 }
213
214 fn read_object_id(&self) -> Result<ObjectId> {
215 self.require_wire(WireType::ObjectId)?;
216 Ok(ObjectId::from_bytes(self.read_array::<32>()?))
217 }
218
219 fn require_wire(&self, expected: WireType) -> Result<()> {
220 if self.wire_type == expected as u8 {
221 return Ok(());
222 }
223 Err(PrikkError::MalformedData(format!(
224 "field {} has wrong wire type: expected {}, got {}",
225 self.tag, expected as u8, self.wire_type
226 )))
227 }
228
229 fn read_array<const N: usize>(&self) -> Result<[u8; N]> {
230 if self.value.len() != N {
231 return Err(PrikkError::MalformedData(format!(
232 "field {} expected {N} bytes, got {}",
233 self.tag,
234 self.value.len()
235 )));
236 }
237 let mut out = [0_u8; N];
238 out.copy_from_slice(self.value);
239 Ok(out)
240 }
241}