• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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