Skip to main content

cbor_core/
value_key.rs

1use std::{
2    borrow::{Borrow, Cow},
3    cmp,
4};
5
6use crate::{
7    Float, SimpleValue, Value,
8    codec::{Head, Major},
9};
10
11/// A key for looking up elements in [`Value`] arrays and maps.
12///
13/// `ValueKey` is the parameter type of [`Value::get`], [`Value::get_mut`],
14/// [`Value::remove`], and the [`Index`]/[`IndexMut`] implementations on [`Value`].
15/// You rarely name it directly: every type that implements
16/// `Into<ValueKey>` can be passed in, including integers, `&str`, `&[u8]`,
17/// and `&Value`.
18///
19/// Lookups are zero-copy. Passing `&str` or `&[u8]` does not allocate a
20/// full `Value` to compare against map keys.
21///
22/// # Examples
23///
24/// ```
25/// use cbor_core::{Value, array, map};
26///
27/// let a = array![10, 20, 30];
28/// assert_eq!(a.get(1).unwrap().to_u32().unwrap(), 20);
29///
30/// let m = map! { "x" => 10, 2 => 20 };
31/// assert_eq!(m.get("x").unwrap().to_u32().unwrap(), 10);
32/// assert_eq!(m.get(2).unwrap().to_u32().unwrap(), 20);
33///
34/// let mut v = array![1, 2, 3];
35/// v.remove(0);
36/// assert_eq!(v.len(), Some(2));
37/// ```
38///
39/// [`Index`]: std::ops::Index
40/// [`IndexMut`]: std::ops::IndexMut
41pub struct ValueKey<'a>(Inner<'a>);
42
43pub enum Inner<'a> {
44    Bytes(&'a [u8]),
45    Text(&'a str),
46    Other(Cow<'a, Value>),
47}
48
49impl<'a> ValueKey<'a> {
50    pub(crate) fn to_usize(&self) -> Option<usize> {
51        self.0.to_usize()
52    }
53}
54
55impl<'a> Inner<'a> {
56    fn cbor_head(&self) -> Head {
57        match self {
58            Inner::Bytes(bytes) => Head::from_value(Major::ByteString, bytes.len().try_into().unwrap()),
59            Inner::Text(text) => Head::from_value(Major::TextString, text.len().try_into().unwrap()),
60            Inner::Other(value) => value.cbor_head(),
61        }
62    }
63
64    pub(crate) fn to_usize(&self) -> Option<usize> {
65        if let Inner::Other(value) = self {
66            value.to_usize().ok()
67        } else {
68            None
69        }
70    }
71
72    fn as_bytes(&self) -> Option<&[u8]> {
73        match self {
74            Inner::Bytes(bytes) => Some(bytes),
75            Inner::Text(_text) => None,
76            Inner::Other(value) => value.as_bytes().ok(),
77        }
78    }
79
80    fn as_str(&self) -> Option<&str> {
81        match self {
82            Inner::Bytes(_bytes) => None,
83            Inner::Text(text) => Some(text),
84            Inner::Other(value) => value.as_str().ok(),
85        }
86    }
87}
88
89impl PartialEq for ValueKey<'_> {
90    fn eq(&self, other: &Self) -> bool {
91        self.cmp(other) == cmp::Ordering::Equal
92    }
93}
94
95impl Eq for ValueKey<'_> {}
96
97impl PartialOrd for ValueKey<'_> {
98    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
99        Some(self.cmp(other))
100    }
101}
102
103impl Ord for ValueKey<'_> {
104    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
105        self.0
106            .cbor_head()
107            .cmp(&other.0.cbor_head())
108            .then_with(|| match (&self.0, &other.0) {
109                (Inner::Bytes(a), b) => (*a).cmp(b.as_bytes().unwrap()),
110                (Inner::Text(a), b) => (*a).cmp(b.as_str().unwrap()),
111
112                (a, Inner::Bytes(b)) => a.as_bytes().unwrap().cmp(b),
113                (a, Inner::Text(b)) => a.as_str().unwrap().cmp(b),
114
115                (Inner::Other(a), Inner::Other(b)) => a.cmp(b),
116            })
117    }
118}
119
120impl<'a> From<Value> for ValueKey<'a> {
121    fn from(value: Value) -> Self {
122        Self(Inner::Other(Cow::Owned(value)))
123    }
124}
125
126impl<'a> From<&'a Value> for ValueKey<'a> {
127    fn from(value: &'a Value) -> Self {
128        Self(Inner::Other(Cow::Borrowed(value)))
129    }
130}
131
132impl<'a> From<&'a str> for ValueKey<'a> {
133    fn from(value: &'a str) -> Self {
134        Self(Inner::Text(value))
135    }
136}
137
138impl<'a> From<&'a String> for ValueKey<'a> {
139    fn from(value: &'a String) -> Self {
140        Self(Inner::Text(value))
141    }
142}
143
144impl<'a> From<&'a Box<str>> for ValueKey<'a> {
145    fn from(value: &'a Box<str>) -> Self {
146        Self(Inner::Text(value))
147    }
148}
149
150impl<'a> From<&'a [u8]> for ValueKey<'a> {
151    fn from(value: &'a [u8]) -> Self {
152        Self(Inner::Bytes(value))
153    }
154}
155
156impl<'a> From<&'a Vec<u8>> for ValueKey<'a> {
157    fn from(value: &'a Vec<u8>) -> Self {
158        Self(Inner::Bytes(value))
159    }
160}
161
162impl<'a> From<&'a Box<[u8]>> for ValueKey<'a> {
163    fn from(value: &'a Box<[u8]>) -> Self {
164        Self(Inner::Bytes(value))
165    }
166}
167
168macro_rules! impl_from {
169    ($($type:ty),* $(,)?) => { $(
170        impl<'a> From<$type> for ValueKey<'a> {
171            fn from(value: $type) -> ValueKey<'a> {
172                Self(Inner::Other(Cow::Owned(Value::from(value))))
173            }
174        }
175    )* }
176}
177
178impl_from!(bool, SimpleValue);
179
180impl_from!(u8, u16, u32, u64, u128, usize);
181impl_from!(i8, i16, i32, i64, i128, isize);
182
183impl_from!(f32, f64, Float);
184
185impl_from!(String, Box<str>, Vec<u8>, Box<[u8]>);
186
187// ---------------------- AsValueKey ----------------------
188//
189// Used as the `Borrow` target for `Value` map keys so that `BTreeMap<Value,
190// Value>` can be looked up by `&str`, `&[u8]`, or `&Value` without allocating
191// a full `Value`. This is an implementation detail of the `ValueKey`.
192
193pub(crate) trait AsValueKey {
194    fn as_value_ref(&self) -> ValueKey<'_>;
195}
196
197impl AsValueKey for Value {
198    fn as_value_ref(&self) -> ValueKey<'_> {
199        ValueKey(Inner::Other(Cow::Borrowed(self)))
200    }
201}
202
203impl AsValueKey for ValueKey<'_> {
204    fn as_value_ref(&self) -> ValueKey<'_> {
205        match &self.0 {
206            Inner::Bytes(bytes) => ValueKey(Inner::Bytes(bytes)),
207            Inner::Text(text) => ValueKey(Inner::Text(text)),
208            Inner::Other(value) => ValueKey(Inner::Other(Cow::Borrowed(value))),
209        }
210    }
211}
212
213impl<'a> PartialEq for dyn AsValueKey + 'a {
214    fn eq(&self, other: &Self) -> bool {
215        self.as_value_ref().cmp(&other.as_value_ref()) == cmp::Ordering::Equal
216    }
217}
218
219impl<'a> Eq for dyn AsValueKey + 'a {}
220
221impl<'a> PartialOrd for dyn AsValueKey + 'a {
222    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
223        Some(self.cmp(other))
224    }
225}
226
227impl<'a> Ord for dyn AsValueKey + 'a {
228    fn cmp(&self, other: &Self) -> cmp::Ordering {
229        self.as_value_ref().cmp(&other.as_value_ref())
230    }
231}
232
233impl<'a> Borrow<dyn AsValueKey + 'a> for Value {
234    fn borrow(&self) -> &(dyn AsValueKey + 'a) {
235        self
236    }
237}