p2panda_rs/operation/plain/
plain_value.rs1use std::convert::TryInto;
4
5use ciborium::Value;
6use serde::{Deserialize, Serialize};
7
8use crate::document::{DocumentId, DocumentViewId};
9use crate::hash::{Hash, HashId};
10use crate::operation::error::PlainValueError;
11
12#[derive(Serialize, Debug, PartialEq, Clone)]
20#[serde(untagged)]
21pub enum PlainValue {
22 Boolean(bool),
24
25 Integer(i64),
27
28 Float(f64),
30
31 String(String),
33
34 #[serde(with = "serde_bytes")]
37 BytesOrRelation(Vec<u8>),
38
39 AmbiguousRelation(Vec<Hash>),
42
43 PinnedRelationList(Vec<Vec<Hash>>),
45}
46
47impl PlainValue {
48 pub fn field_type(&self) -> &str {
52 match self {
53 PlainValue::Boolean(_) => "bool",
54 PlainValue::Integer(_) => "int",
55 PlainValue::Float(_) => "float",
56 PlainValue::String(_) => "str",
57 PlainValue::BytesOrRelation(_) => "bytes",
58 PlainValue::AmbiguousRelation(_) => "hash[]",
59 PlainValue::PinnedRelationList(_) => "hash[][]",
60 }
61 }
62}
63
64impl<'de> Deserialize<'de> for PlainValue {
65 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66 where
67 D: serde::Deserializer<'de>,
68 {
69 let is_human_readable = deserializer.is_human_readable();
70 let cbor_value: Value = Deserialize::deserialize(deserializer)?;
71
72 to_plain_value(is_human_readable, cbor_value).map_err(|err| {
73 serde::de::Error::custom(format!("error deserializing plain value: {}", err))
74 })
75 }
76}
77
78impl From<bool> for PlainValue {
79 fn from(value: bool) -> Self {
80 PlainValue::Boolean(value)
81 }
82}
83
84impl From<Vec<u8>> for PlainValue {
85 fn from(value: Vec<u8>) -> Self {
86 PlainValue::BytesOrRelation(value)
87 }
88}
89
90impl From<&[u8]> for PlainValue {
91 fn from(value: &[u8]) -> Self {
92 PlainValue::BytesOrRelation(value.to_owned())
93 }
94}
95
96impl From<f64> for PlainValue {
97 fn from(value: f64) -> Self {
98 PlainValue::Float(value)
99 }
100}
101
102impl From<i64> for PlainValue {
103 fn from(value: i64) -> Self {
104 PlainValue::Integer(value)
105 }
106}
107
108impl From<String> for PlainValue {
109 fn from(value: String) -> Self {
110 PlainValue::String(value)
111 }
112}
113
114impl From<Vec<Hash>> for PlainValue {
115 fn from(value: Vec<Hash>) -> Self {
116 PlainValue::AmbiguousRelation(value)
117 }
118}
119
120impl From<&str> for PlainValue {
121 fn from(value: &str) -> Self {
122 PlainValue::String(value.to_owned())
123 }
124}
125
126impl From<DocumentId> for PlainValue {
127 fn from(value: DocumentId) -> Self {
128 PlainValue::BytesOrRelation(hex::decode(value.as_str()).unwrap())
129 }
130}
131
132impl From<Vec<DocumentId>> for PlainValue {
133 fn from(value: Vec<DocumentId>) -> Self {
134 PlainValue::AmbiguousRelation(value.iter().map(HashId::as_hash).cloned().collect())
135 }
136}
137
138impl From<DocumentViewId> for PlainValue {
139 fn from(value: DocumentViewId) -> Self {
140 PlainValue::AmbiguousRelation(value.into())
141 }
142}
143
144impl From<Vec<DocumentViewId>> for PlainValue {
145 fn from(value: Vec<DocumentViewId>) -> Self {
146 PlainValue::PinnedRelationList(value.iter().cloned().map(Into::<Vec<Hash>>::into).collect())
147 }
148}
149
150fn to_plain_value(is_human_readable: bool, value: Value) -> Result<PlainValue, PlainValueError> {
152 let result: Result<PlainValue, PlainValueError> = match value {
153 Value::Integer(int) => {
154 let int: i64 = int.try_into()?;
155 Ok(int.into())
156 }
157 Value::Bytes(bytes) => Ok(bytes.into()),
158 Value::Float(float) => Ok(float.into()),
159 Value::Text(text) => Ok(text.into()),
160 Value::Bool(bool) => Ok(bool.into()),
161 Value::Array(array) => to_plain_value_list(is_human_readable, array),
162 _ => return Err(PlainValueError::UnsupportedValue),
163 };
164
165 result
166}
167
168fn to_plain_value_list(
173 is_human_readable: bool,
174 array: Vec<Value>,
175) -> Result<PlainValue, PlainValueError> {
176 let to_hex_str = |value: &Value| -> Result<String, PlainValueError> {
182 if is_human_readable {
183 match value.as_text() {
184 Some(text) => Ok(text.to_owned()),
185 None => Err(PlainValueError::UnsupportedValue),
186 }
187 } else {
188 match value.as_bytes() {
189 Some(bytes) => Ok(hex::encode(bytes)),
190 None => Err(PlainValueError::UnsupportedValue),
191 }
192 }
193 };
194
195 let ambiguous_relation: Result<Vec<Hash>, PlainValueError> = array
198 .iter()
199 .map(|value| {
200 let hex_str = to_hex_str(value)?;
201 let hash = Hash::new(&hex_str).map_err(|_| PlainValueError::UnsupportedValue)?;
202 Ok(hash)
203 })
204 .collect();
205
206 if let Ok(hashes) = ambiguous_relation {
208 return Ok(PlainValue::AmbiguousRelation(hashes));
209 };
210
211 let mut pinned_relations = Vec::new();
214 for inner_array in array {
215 let inner_array = match inner_array.as_array() {
216 Some(array) => Ok(array),
217 None => Err(PlainValueError::UnsupportedValue),
218 }?;
219
220 let pinned_relation: Result<Vec<Hash>, PlainValueError> = inner_array
221 .iter()
222 .map(|value| {
223 let hex_str = to_hex_str(value)?;
224 let hash = Hash::new(&hex_str).map_err(|_| PlainValueError::UnsupportedValue)?;
225 Ok(hash)
226 })
227 .collect();
228
229 pinned_relations.push(pinned_relation?);
230 }
231
232 Ok(PlainValue::PinnedRelationList(pinned_relations))
233}
234
235#[cfg(test)]
236mod tests {
237 use ciborium::cbor;
238 use rstest::rstest;
239 use serde_bytes::ByteBuf;
240
241 use crate::document::{DocumentId, DocumentViewId};
242 use crate::hash::{Hash, HashId};
243 use crate::serde::{deserialize_into, hex_string_to_bytes, serialize_from, serialize_value};
244 use crate::test_utils::fixtures::{document_id, document_view_id, random_hash};
245
246 use super::PlainValue;
247
248 #[test]
249 fn field_type_representation() {
250 assert_eq!("int", PlainValue::Integer(5).field_type());
251 assert_eq!("bool", PlainValue::Boolean(false).field_type());
252 assert_eq!(
253 "bytes",
254 PlainValue::BytesOrRelation("test".as_bytes().into()).field_type()
255 );
256 assert_eq!("str", PlainValue::String("test".into()).field_type());
257 assert_eq!(
258 "hash[]",
259 PlainValue::AmbiguousRelation(vec![random_hash()]).field_type()
260 );
261 }
262
263 #[rstest]
264 fn from_primitives(document_id: DocumentId, document_view_id: DocumentViewId) {
265 assert_eq!(PlainValue::Boolean(true), true.into());
267 assert_eq!(PlainValue::Float(1.5), 1.5.into());
268 assert_eq!(PlainValue::Integer(3), 3.into());
269 assert_eq!(
270 PlainValue::BytesOrRelation("hellö".as_bytes().to_vec()),
271 "hellö".as_bytes().into()
272 );
273 assert_eq!(PlainValue::String("hellö".to_string()), "hellö".into());
274
275 assert_eq!(
277 PlainValue::BytesOrRelation(document_id.to_bytes()),
278 document_id.clone().into()
279 );
280 assert_eq!(
281 PlainValue::AmbiguousRelation(vec![document_id.clone().into()]),
282 vec![document_id].into()
283 );
284 assert_eq!(
285 PlainValue::AmbiguousRelation(document_view_id.clone().into()),
286 document_view_id.clone().into()
287 );
288 assert_eq!(
289 PlainValue::PinnedRelationList(vec![document_view_id.clone().into()]),
290 vec![document_view_id].into()
291 );
292 }
293
294 #[test]
295 fn serialize() {
296 assert_eq!(
297 serialize_from(PlainValue::Integer(5)),
298 serialize_value(cbor!(5))
299 );
300
301 assert_eq!(
302 serialize_from(PlainValue::AmbiguousRelation(vec![Hash::new(
303 "002089e5c6f0cbc0e8d8c92050dffc60e3217b556d62eace0d2e5d374c70a1d0c2d4"
304 )
305 .unwrap()])),
306 serialize_value(cbor!([hex_string_to_bytes(
307 "002089e5c6f0cbc0e8d8c92050dffc60e3217b556d62eace0d2e5d374c70a1d0c2d4"
308 )]))
309 );
310
311 assert_eq!(
312 serialize_from(PlainValue::PinnedRelationList(vec![vec![Hash::new(
313 "002089e5c6f0cbc0e8d8c92050dffc60e3217b556d62eace0d2e5d374c70a1d0c2d4"
314 )
315 .unwrap()]])),
316 serialize_value(cbor!([[hex_string_to_bytes(
317 "002089e5c6f0cbc0e8d8c92050dffc60e3217b556d62eace0d2e5d374c70a1d0c2d4"
318 )]]))
319 );
320
321 assert_eq!(
322 serialize_from(PlainValue::BytesOrRelation(vec![0, 1, 2, 3])),
323 serialize_value(cbor!(ByteBuf::from(vec![0, 1, 2, 3])))
324 );
325
326 assert_eq!(
327 serialize_from(PlainValue::String("username".to_string())),
328 serialize_value(cbor!("username"))
329 );
330
331 assert_eq!(
332 serialize_from(PlainValue::AmbiguousRelation(vec![])),
333 serialize_value(cbor!([]))
334 );
335 }
336
337 #[test]
338 fn deserialize() {
339 assert_eq!(
340 deserialize_into::<PlainValue>(&serialize_value(cbor!(12))).unwrap(),
341 PlainValue::Integer(12)
342 );
343 assert_eq!(
344 deserialize_into::<PlainValue>(&serialize_value(cbor!(12.0))).unwrap(),
345 PlainValue::Float(12.0)
346 );
347 assert_eq!(
348 deserialize_into::<PlainValue>(&serialize_value(cbor!(ByteBuf::from(vec![
349 0, 1, 2, 3
350 ]))))
351 .unwrap(),
352 PlainValue::BytesOrRelation(vec![0, 1, 2, 3])
353 );
354 assert_eq!(
355 deserialize_into::<PlainValue>(&serialize_value(cbor!("hello"))).unwrap(),
356 PlainValue::String("hello".to_string())
357 );
358 assert_eq!(
359 deserialize_into::<PlainValue>(&serialize_value(cbor!([]))).unwrap(),
360 PlainValue::AmbiguousRelation(vec![])
361 );
362 }
363
364 #[test]
365 fn deserialize_human_readable() {
366 assert_eq!(
367 serde_json::from_str::<PlainValue>("12").unwrap(),
368 PlainValue::Integer(12)
369 );
370 assert_eq!(
371 serde_json::from_str::<PlainValue>("12.0").unwrap(),
372 PlainValue::Float(12.0)
373 );
374 assert_eq!(
375 serde_json::from_str::<PlainValue>("\"hello\"").unwrap(),
376 PlainValue::String("hello".to_string())
377 );
378 assert_eq!(
379 serde_json::from_str::<PlainValue>("[]").unwrap(),
380 PlainValue::AmbiguousRelation(vec![])
381 );
382 assert_eq!(
383 serde_json::from_str::<PlainValue>(
384 "[[\"00200801063d8aaba76c283a2fb63cf5cd4ec86765424452ce7327fda04c5da80d62\"]]"
385 )
386 .unwrap(),
387 PlainValue::PinnedRelationList(vec![vec![Hash::new(
388 "00200801063d8aaba76c283a2fb63cf5cd4ec86765424452ce7327fda04c5da80d62"
389 )
390 .unwrap()]])
391 );
392 }
393
394 #[test]
395 fn large_numbers() {
396 assert_eq!(
397 deserialize_into::<PlainValue>(&serialize_value(cbor!(i64::MAX))).unwrap(),
398 PlainValue::Integer(i64::MAX)
399 );
400 assert_eq!(
401 deserialize_into::<PlainValue>(&serialize_value(cbor!(f64::MAX))).unwrap(),
402 PlainValue::Float(f64::MAX)
403 );
404
405 let bytes = serialize_value(cbor!(u64::MAX));
407 let value = deserialize_into::<PlainValue>(&bytes);
408 assert!(value.is_err());
409 }
410}