Skip to main content

relay_protocol/
value.rs

1use std::collections::BTreeMap;
2use std::fmt::Debug;
3use std::net::IpAddr;
4use std::{fmt, str};
5
6use serde::de::{Deserialize, MapAccess, SeqAccess, Visitor};
7use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
8use uuid::Uuid;
9
10use crate::FiniteF64;
11use crate::annotated::Annotated;
12use crate::meta::Meta;
13
14/// Alias for typed arrays.
15pub type Array<T> = Vec<Annotated<T>>;
16
17/// Alias for maps.
18pub type Map<K, T> = BTreeMap<K, T>;
19
20/// Alias for typed objects.
21pub type Object<T> = Map<String, Annotated<T>>;
22
23/// Represents a boxed value.
24#[derive(Debug, Clone, PartialEq)]
25pub enum Value {
26    /// A boolean value.
27    Bool(bool),
28    /// A signed integer value.
29    I64(i64),
30    /// An unsigned integer value.
31    U64(u64),
32    /// A floating point value.
33    F64(f64),
34    /// A string value.
35    String(String),
36    /// An array of annotated values.
37    Array(Array<Value>),
38    /// A mapping of strings to annotated values.
39    Object(Object<Value>),
40}
41
42/// Helper type that renders out a description of the value.
43pub struct ValueDescription<'a>(&'a Value);
44
45impl fmt::Display for ValueDescription<'_> {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        match *self.0 {
48            Value::Bool(true) => f.pad("true"),
49            Value::Bool(false) => f.pad("false"),
50            Value::I64(val) => write!(f, "integer {val}"),
51            Value::U64(val) => write!(f, "integer {val}"),
52            Value::F64(val) => write!(f, "float {val}"),
53            Value::String(ref val) => f.pad(val),
54            Value::Array(_) => f.pad("an array"),
55            Value::Object(_) => f.pad("an object"),
56        }
57    }
58}
59
60impl Value {
61    /// Returns a formattable that gives a helper description of the value.
62    pub fn describe(&self) -> ValueDescription<'_> {
63        ValueDescription(self)
64    }
65
66    /// Returns the string if this value is a string, otherwise `None`.
67    pub fn as_str(&self) -> Option<&str> {
68        match self {
69            Value::String(string) => Some(string.as_str()),
70            _ => None,
71        }
72    }
73
74    /// Returns the string if this value is a string, otherwise `None`.
75    pub fn into_string(self) -> Option<String> {
76        match self {
77            Value::String(string) => Some(string),
78            _ => None,
79        }
80    }
81
82    /// Returns a `f64` if the value can be converted to it, otherwise `None`.
83    pub fn as_f64(&self) -> Option<f64> {
84        match self {
85            Value::F64(f) => Some(*f),
86            Value::I64(i) => Some(*i as f64),
87            Value::U64(u) => Some(*u as f64),
88            _ => None,
89        }
90    }
91
92    /// Constructs a `Value` from a `serde_json::Value` object.
93    fn from_json(value: serde_json::Value) -> Option<Self> {
94        Some(match value {
95            serde_json::Value::Null => return None,
96            serde_json::Value::Bool(value) => Value::Bool(value),
97            serde_json::Value::Number(num) => {
98                if let Some(val) = num.as_i64() {
99                    Value::I64(val)
100                } else if let Some(val) = num.as_u64() {
101                    Value::U64(val)
102                } else if let Some(val) = num.as_f64() {
103                    Value::F64(val)
104                } else {
105                    // NB: Without the "arbitrary_precision" feature, serde_json's number will
106                    // always be one of the above.
107                    unreachable!()
108                }
109            }
110            serde_json::Value::String(val) => Value::String(val),
111            serde_json::Value::Array(items) => {
112                Value::Array(items.into_iter().map(Annotated::<Value>::from).collect())
113            }
114            serde_json::Value::Object(items) => Value::Object(
115                items
116                    .into_iter()
117                    .map(|(k, v)| (k, Annotated::<Value>::from(v)))
118                    .collect(),
119            ),
120        })
121    }
122}
123
124impl TryFrom<&Value> for String {
125    type Error = ();
126
127    fn try_from(value: &Value) -> Result<Self, Self::Error> {
128        Ok(match value {
129            Value::Bool(v) => v.to_string(),
130            Value::I64(v) => v.to_string(),
131            Value::U64(v) => v.to_string(),
132            Value::F64(v) => v.to_string(),
133            Value::String(v) => v.to_string(),
134            _ => return Err(()),
135        })
136    }
137}
138
139impl Serialize for Value {
140    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141    where
142        S: Serializer,
143    {
144        match *self {
145            Value::Bool(val) => serializer.serialize_bool(val),
146            Value::I64(val) => serializer.serialize_i64(val),
147            Value::U64(val) => serializer.serialize_u64(val),
148            Value::F64(val) => serializer.serialize_f64(val),
149            Value::String(ref val) => serializer.serialize_str(val),
150            Value::Array(ref items) => {
151                let mut seq_ser = serializer.serialize_seq(Some(items.len()))?;
152                for item in items {
153                    match item {
154                        Annotated(Some(val), _) => seq_ser.serialize_element(val)?,
155                        Annotated(None, _) => seq_ser.serialize_element(&())?,
156                    }
157                }
158                seq_ser.end()
159            }
160            Value::Object(ref items) => {
161                let mut map_ser = serializer.serialize_map(Some(items.len()))?;
162                for (key, value) in items {
163                    map_ser.serialize_key(key)?;
164                    match value {
165                        Annotated(Some(val), _) => map_ser.serialize_value(val)?,
166                        Annotated(None, _) => map_ser.serialize_value(&())?,
167                    }
168                }
169                map_ser.end()
170            }
171        }
172    }
173}
174
175impl From<serde_json::Value> for Annotated<Value> {
176    fn from(value: serde_json::Value) -> Annotated<Value> {
177        Annotated::from(Value::from_json(value))
178    }
179}
180
181impl From<Value> for serde_json::Value {
182    fn from(value: Value) -> serde_json::Value {
183        match value {
184            Value::Bool(value) => serde_json::Value::Bool(value),
185            Value::I64(value) => serde_json::Value::Number(value.into()),
186            Value::U64(value) => serde_json::Value::Number(value.into()),
187            Value::F64(value) => serde_json::Number::from_f64(value)
188                .map(serde_json::Value::Number)
189                .unwrap_or(serde_json::Value::Null),
190            Value::String(val) => serde_json::Value::String(val),
191            Value::Array(items) => {
192                serde_json::Value::Array(items.into_iter().map(serde_json::Value::from).collect())
193            }
194            Value::Object(items) => serde_json::Value::Object(
195                items
196                    .into_iter()
197                    .map(|(k, v)| (k, serde_json::Value::from(v)))
198                    .collect(),
199            ),
200        }
201    }
202}
203
204impl From<Annotated<Value>> for serde_json::Value {
205    fn from(value: Annotated<Value>) -> serde_json::Value {
206        value
207            .0
208            .map(serde_json::Value::from)
209            .unwrap_or(serde_json::Value::Null)
210    }
211}
212
213impl From<bool> for Value {
214    fn from(value: bool) -> Self {
215        Value::Bool(value)
216    }
217}
218
219impl From<i64> for Value {
220    fn from(value: i64) -> Self {
221        Value::I64(value)
222    }
223}
224
225impl From<u64> for Value {
226    fn from(value: u64) -> Self {
227        Value::U64(value)
228    }
229}
230
231impl From<f64> for Value {
232    fn from(value: f64) -> Self {
233        Value::F64(value)
234    }
235}
236
237impl<'a> From<&'a str> for Value {
238    fn from(value: &'a str) -> Self {
239        Value::String(value.to_owned())
240    }
241}
242
243impl From<String> for Value {
244    fn from(value: String) -> Self {
245        Value::String(value)
246    }
247}
248
249impl From<Array<Value>> for Value {
250    fn from(value: Array<Value>) -> Self {
251        Value::Array(value)
252    }
253}
254
255impl From<Object<Value>> for Value {
256    fn from(value: Object<Value>) -> Self {
257        Value::Object(value)
258    }
259}
260
261impl<'de> Deserialize<'de> for Value {
262    #[inline]
263    fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
264    where
265        D: serde::Deserializer<'de>,
266    {
267        struct ValueVisitor;
268
269        impl<'de> Visitor<'de> for ValueVisitor {
270            type Value = Value;
271
272            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
273                formatter.write_str("any valid JSON value")
274            }
275
276            #[inline]
277            fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
278                Ok(Value::Bool(value))
279            }
280
281            #[inline]
282            fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
283                Ok(Value::I64(value))
284            }
285
286            #[inline]
287            fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
288                Ok(value
289                    .try_into()
290                    .map(Value::I64)
291                    .unwrap_or(Value::U64(value)))
292            }
293
294            #[inline]
295            fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
296                Ok(Value::F64(value))
297            }
298
299            #[inline]
300            fn visit_str<E>(self, value: &str) -> Result<Value, E>
301            where
302                E: serde::de::Error,
303            {
304                self.visit_string(String::from(value))
305            }
306
307            #[inline]
308            fn visit_string<E>(self, value: String) -> Result<Value, E> {
309                Ok(Value::String(value))
310            }
311
312            #[inline]
313            fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
314            where
315                D: serde::Deserializer<'de>,
316            {
317                Deserialize::deserialize(deserializer)
318            }
319
320            #[inline]
321            fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
322            where
323                V: SeqAccess<'de>,
324            {
325                let mut vec = Vec::new();
326                while let Some(elem) = visitor.next_element()? {
327                    vec.push(Annotated(elem, Meta::default()));
328                }
329                Ok(Value::Array(vec))
330            }
331
332            fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
333            where
334                V: MapAccess<'de>,
335            {
336                let mut values = Map::new();
337                while let Some((key, value)) = visitor.next_entry()? {
338                    values.insert(key, Annotated(value, Meta::default()));
339                }
340                Ok(Value::Object(values))
341            }
342        }
343
344        deserializer.deserialize_any(ValueVisitor)
345    }
346}
347
348/// Convert `T` into a `Value`.
349pub fn to_value<T>(value: &T) -> Result<Option<Value>, serde_json::Error>
350where
351    T: Serialize,
352{
353    serde_json::to_value(value).map(Value::from_json)
354}
355
356/// Borrowed version of [`Array`].
357#[derive(Debug, Clone, Copy)]
358pub struct Arr<'a> {
359    _phantom: std::marker::PhantomData<&'a ()>,
360}
361
362/// Borrowed version of [`Object`].
363#[derive(Debug, Clone, Copy)]
364pub struct Obj<'a> {
365    _phantom: std::marker::PhantomData<&'a ()>,
366}
367
368/// Borrowed version of a "hex ID", like a span ID, UUID,
369/// &c, represented by a byte slice.
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub struct HexId<'a>(pub &'a [u8]);
372
373impl HexId<'_> {
374    /// Checks whether the given string is a valid hex encoding
375    /// of `self`.
376    pub fn match_str(&self, other: &str) -> bool {
377        if other.len() != 2 * self.0.len() {
378            return false;
379        }
380
381        let sx = (0..)
382            .step_by(2)
383            .map_while(|r| other.get(r..r + 2))
384            .map(|x| u8::from_str_radix(x, 16).ok());
385
386        self.0.iter().copied().map(Some).eq(sx)
387    }
388}
389
390/// Borrowed version of [`Value`].
391#[derive(Debug, Clone, Copy)]
392pub enum Val<'a> {
393    /// A boolean value.
394    Bool(bool),
395    /// A signed integer value.
396    I64(i64),
397    /// An unsigned integer value.
398    U64(u64),
399    /// A floating point value.
400    F64(f64),
401    /// A string value.
402    String(&'a str),
403    /// A hexadecimal ID (UUID, span ID, &c).
404    HexId(HexId<'a>),
405    /// An IPv4 or IPv6 address.
406    IpAddr(IpAddr),
407    /// An array of annotated values.
408    Array(Arr<'a>),
409    /// A mapping of strings to annotated values.
410    Object(Obj<'a>),
411}
412
413impl<'a> Val<'a> {
414    /// Returns the value if it is a boolean, otherwise `None`.
415    pub fn as_bool(&self) -> Option<bool> {
416        match self {
417            Self::Bool(value) => Some(*value),
418            _ => None,
419        }
420    }
421
422    /// Represents the value as `f64` if possible. Returns `None` otherwise.
423    pub fn as_i64(&self) -> Option<i64> {
424        match self {
425            Self::I64(value) => Some(*value),
426            Self::U64(value) => (*value).try_into().ok(),
427            _ => None,
428        }
429    }
430
431    /// Represents the value as `f64` if possible. Returns `None` otherwise.
432    pub fn as_u64(&self) -> Option<u64> {
433        match self {
434            Self::I64(value) => (*value).try_into().ok(),
435            Self::U64(value) => Some(*value),
436            _ => None,
437        }
438    }
439
440    /// Represents the value as `f64` if possible. Returns `None` otherwise.
441    pub fn as_f64(&self) -> Option<f64> {
442        match self {
443            Self::I64(value) => Some(*value as f64),
444            Self::U64(value) => Some(*value as f64),
445            Self::F64(value) => Some(*value),
446            _ => None,
447        }
448    }
449
450    /// Returns the string if this value is a string, otherwise `None`.
451    pub fn as_str(&self) -> Option<&'a str> {
452        match self {
453            Self::String(value) => Some(value),
454
455            _ => None,
456        }
457    }
458
459    /// Returns the ID if this value is a hex ID, otherwise `None`.
460    pub fn as_hex_id(&self) -> Option<HexId<'_>> {
461        match self {
462            Self::HexId(value) => Some(*value),
463
464            _ => None,
465        }
466    }
467}
468
469impl From<bool> for Val<'_> {
470    fn from(value: bool) -> Self {
471        Self::Bool(value)
472    }
473}
474
475impl From<i64> for Val<'_> {
476    fn from(value: i64) -> Self {
477        Self::I64(value)
478    }
479}
480
481impl From<u64> for Val<'_> {
482    fn from(value: u64) -> Self {
483        Self::U64(value)
484    }
485}
486
487impl From<f64> for Val<'_> {
488    fn from(value: f64) -> Self {
489        Self::F64(value)
490    }
491}
492
493impl From<FiniteF64> for Val<'_> {
494    fn from(value: FiniteF64) -> Self {
495        Self::F64(value.to_f64())
496    }
497}
498
499impl<'a> From<&'a str> for Val<'a> {
500    fn from(value: &'a str) -> Self {
501        Self::String(value)
502    }
503}
504
505impl<'a> From<&'a Uuid> for Val<'a> {
506    fn from(value: &'a Uuid) -> Self {
507        Self::HexId(HexId(value.as_bytes()))
508    }
509}
510
511impl From<IpAddr> for Val<'_> {
512    fn from(value: IpAddr) -> Self {
513        Self::IpAddr(value)
514    }
515}
516
517impl<'a, T> From<&'a T> for Val<'a>
518where
519    Val<'a>: From<T>,
520    T: Copy,
521{
522    fn from(value: &'a T) -> Self {
523        (*value).into()
524    }
525}
526
527impl<'a> From<&'a Value> for Val<'a> {
528    fn from(value: &'a Value) -> Self {
529        match value {
530            Value::Bool(value) => Self::Bool(*value),
531            Value::I64(value) => Self::I64(*value),
532            Value::U64(value) => Self::U64(*value),
533            Value::F64(value) => Self::F64(*value),
534            Value::String(value) => Self::String(value),
535            Value::Array(_) => Self::Array(Arr {
536                _phantom: Default::default(),
537            }),
538            Value::Object(_) => Self::Object(Obj {
539                _phantom: Default::default(),
540            }),
541        }
542    }
543}
544
545impl PartialEq for Val<'_> {
546    fn eq(&self, other: &Self) -> bool {
547        match (self, other) {
548            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
549            (Self::I64(l0), Self::I64(r0)) => l0 == r0,
550            (Self::I64(l0), Self::U64(r0)) => Ok(*l0) == (*r0).try_into(),
551            (Self::U64(l0), Self::U64(r0)) => l0 == r0,
552            (Self::U64(l0), Self::I64(r0)) => Ok(*l0) == (*r0).try_into(),
553            (Self::F64(l0), Self::F64(r0)) => l0 == r0,
554            (Self::String(l0), Self::String(r0)) => l0 == r0,
555            (Self::HexId(l0), Self::HexId(r0)) => l0 == r0,
556            (Self::IpAddr(l0), Self::IpAddr(r0)) => l0 == r0,
557            (Self::Array(_), Self::Array(_)) => false,
558            (Self::Object(_), Self::Object(_)) => false,
559            _ => false,
560        }
561    }
562}
563
564#[cfg(test)]
565mod tests {
566    use super::*;
567
568    #[test]
569    fn test_unsigned_signed() {
570        let v: Value = serde_json::from_str("9223372036854775816").unwrap();
571        assert_eq!(v, Value::U64(9223372036854775816));
572
573        let v: Value = serde_json::from_str("123").unwrap();
574        assert_eq!(v, Value::I64(123));
575    }
576
577    #[test]
578    fn test_hex_id_comparison() {
579        let id = HexId(&[0xde, 0xad, 0xbe, 0xef]);
580        assert!(id.match_str("deadbeef"));
581        // Matching is case insensitive
582        assert!(id.match_str("DEADBEEF"));
583        // Values don't match
584        assert!(!id.match_str("deedbeef"));
585        // Too short
586        assert!(!id.match_str("deadbee"));
587        // Too long
588        assert!(!id.match_str("deadbeeff"));
589        // Not a valid hex string at all
590        assert!(!id.match_str("deadbeer"));
591    }
592}