1 //! This module defines a helper for parsing fields in a CBOR map.
2
3 use anyhow::{anyhow, bail, Result};
4 use coset::cbor::value::Value;
5
6 pub(super) struct FieldValue {
7 name: &'static str,
8 value: Option<Value>,
9 }
10
11 impl FieldValue {
new(name: &'static str) -> Self12 pub fn new(name: &'static str) -> Self {
13 Self { name, value: None }
14 }
15
set(&mut self, value: Value) -> Result<()>16 pub fn set(&mut self, value: Value) -> Result<()> {
17 if let Some(existing) = &self.value {
18 bail!("Duplicate values for {}: {:?} and {:?}", self.name, existing, value);
19 } else {
20 self.value = Some(value);
21 Ok(())
22 }
23 }
24
is_bytes(&self) -> bool25 pub fn is_bytes(&self) -> bool {
26 self.value.as_ref().map_or(false, |v| v.is_bytes())
27 }
28
into_optional_bytes(self) -> Result<Option<Vec<u8>>>29 pub fn into_optional_bytes(self) -> Result<Option<Vec<u8>>> {
30 self.value
31 .map(|v| {
32 if let Value::Bytes(b) = v {
33 Ok(b)
34 } else {
35 bail!("{}: expected bytes, got {:?}", self.name, v)
36 }
37 })
38 .transpose()
39 }
40
into_bytes(self) -> Result<Vec<u8>>41 pub fn into_bytes(self) -> Result<Vec<u8>> {
42 require_present(self.name, self.into_optional_bytes())
43 }
44
into_optional_string(self) -> Result<Option<String>>45 pub fn into_optional_string(self) -> Result<Option<String>> {
46 self.value
47 .map(|v| {
48 if let Value::Text(s) = v {
49 Ok(s)
50 } else {
51 bail!("{}: expected text, got {:?}", self.name, v)
52 }
53 })
54 .transpose()
55 }
56
into_string(self) -> Result<String>57 pub fn into_string(self) -> Result<String> {
58 require_present(self.name, self.into_optional_string())
59 }
60
is_null(&self) -> Result<bool>61 pub fn is_null(&self) -> Result<bool> {
62 // If there's no value, return false; if there is a null value, return true; anything else
63 // is an error.
64 self.value
65 .as_ref()
66 .map(|v| {
67 if *v == Value::Null {
68 Ok(true)
69 } else {
70 bail!("{}: expected null, got {:?}", self.name, v)
71 }
72 })
73 .unwrap_or(Ok(false))
74 }
75
is_integer(&self) -> bool76 pub fn is_integer(&self) -> bool {
77 self.value.as_ref().map_or(false, |v| v.is_integer())
78 }
79
into_optional_i64(self) -> Result<Option<i64>>80 pub fn into_optional_i64(self) -> Result<Option<i64>> {
81 self.value
82 .map(|v| {
83 let value =
84 if let Value::Integer(i) = v { i128::from(i).try_into().ok() } else { None };
85 value.ok_or_else(|| anyhow!("{}: expected integer, got {:?}", self.name, v))
86 })
87 .transpose()
88 }
89 }
90
require_present<T>(name: &'static str, value: Result<Option<T>>) -> Result<T>91 fn require_present<T>(name: &'static str, value: Result<Option<T>>) -> Result<T> {
92 value.and_then(|opt| opt.ok_or_else(|| anyhow!("{} must be present", name)))
93 }
94