Skip to main content

mzdata_param/
value.rs

1use std::borrow::Cow;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::str::{self, FromStr};
5use std::{io, mem};
6
7use thiserror::Error;
8
9use crate::ValueRef;
10
11
12macro_rules! param_value_int {
13    ($val:ty) => {
14        impl From<$val> for Value {
15            fn from(value: $val) -> Self {
16                Self::Int(value as i64)
17            }
18        }
19
20        impl From<&$val> for Value {
21            fn from(value: &$val) -> Self {
22                Self::Int(*value as i64)
23            }
24        }
25
26        impl From<Option<$val>> for Value {
27            fn from(value: Option<$val>) -> Self {
28                if let Some(v) = value {
29                    Self::Int(v as i64)
30                } else {
31                    Self::Empty
32                }
33            }
34        }
35    };
36}
37
38macro_rules! param_value_float {
39    ($val:ty) => {
40        impl From<$val> for Value {
41            fn from(value: $val) -> Self {
42                Self::Float(value as f64)
43            }
44        }
45
46        impl From<&$val> for Value {
47            fn from(value: &$val) -> Self {
48                Self::Float(*value as f64)
49            }
50        }
51
52        impl From<Option<$val>> for Value {
53            fn from(value: Option<$val>) -> Self {
54                if let Some(v) = value {
55                    Self::Float(v as f64)
56                } else {
57                    Self::Empty
58                }
59            }
60        }
61    };
62}
63/// An owned parameter value that may be a string, a number, or empty. It is intended to
64/// be paired with the [`ParamValue`] trait.
65///
66/// The borrowed equivalent of this type is [`ValueRef`].
67#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub enum Value {
70    /// A text value of arbitrary length
71    String(String),
72    /// A floating point number
73    Float(f64),
74    /// A integral number
75    Int(i64),
76    /// Arbitrary binary data
77    Buffer(Box<[u8]>),
78    /// true/false value
79    Boolean(bool),
80    /// No value specified
81    #[default]
82    Empty,
83    /// A list of heterogenous [`Value`]
84    List(Box<[Value]>),
85}
86
87impl Eq for Value {}
88
89impl From<String> for Value {
90    fn from(value: String) -> Self {
91        Value::new(value)
92    }
93}
94
95impl From<&str> for Value {
96    fn from(value: &str) -> Self {
97        Value::wrap(value)
98    }
99}
100
101impl From<Cow<'_, str>> for Value {
102    fn from(value: Cow<'_, str>) -> Self {
103        Value::wrap(&value)
104    }
105}
106
107/// Access a parameter's value, with specific coercion rules
108/// and eager type conversion.
109pub trait ParamValue {
110    /// Check if the value is empty
111    fn is_empty(&self) -> bool;
112
113    /// Check if the value is an integer
114    fn is_i64(&self) -> bool;
115
116    /// Check if the value is a floating point
117    /// number explicitly. An integral number might
118    /// still be usable as a floating point number
119    fn is_f64(&self) -> bool;
120
121    /// Check if the value is an arbitrary buffer
122    fn is_buffer(&self) -> bool;
123
124    /// Check if the value is stored as an explicit string.
125    /// All variants can be coerced to a string.
126    fn is_str(&self) -> bool;
127
128    /// Check if the value is of either numeric type.
129    fn is_numeric(&self) -> bool {
130        self.is_i64() | self.is_f64()
131    }
132
133    /// Check if the value is a list
134    fn is_list(&self) -> bool;
135
136    /// Check if the value is a boolean
137    fn is_boolean(&self) -> bool;
138
139    /// Get the value as an `f64`, if possible
140    fn to_f64(&self) -> Result<f64, ParamValueParseError>;
141
142    /// Get the value as an `f32`, if possible
143    fn to_f32(&self) -> Result<f32, ParamValueParseError> {
144        let v = self.to_f64()?;
145        Ok(v as f32)
146    }
147
148    /// Get the value as a `bool`, if possible
149    fn to_bool(&self) -> Result<bool, ParamValueParseError>;
150
151    /// Get the value as an `i64`, if possible
152    fn to_i64(&self) -> Result<i64, ParamValueParseError>;
153
154    /// Get the value as an `i32`, if possible
155    fn to_i32(&self) -> Result<i32, ParamValueParseError> {
156        let v = self.to_i64()?;
157        Ok(v as i32)
158    }
159
160    /// Get the value as an `u64`, if possible
161    fn to_u64(&self) -> Result<u64, ParamValueParseError> {
162        let v = self.to_i64()?;
163        Ok(v as u64)
164    }
165
166    /// Get the value as a string
167    fn to_str(&self) -> Cow<'_, str>;
168
169    /// Get the value as a string, possibly borrowed
170    fn as_str(&self) -> Cow<'_, str> {
171        self.to_str()
172    }
173
174    /// Get the value as a byte buffer, if possible.
175    ///
176    /// The intent here is distinct from [`ParamValue::as_bytes`]. The byte buffer
177    /// represents the byte representation of the native value, while
178    /// [`ParamValue::as_bytes`] is a byte string of the string representation.
179    fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError>;
180
181    /// Get the value as a slice
182    fn as_slice(&self) -> Cow<'_, [Value]>;
183
184    /// Convert the value's string representation to `T` if possible
185    fn parse<T: FromStr>(&self) -> Result<T, T::Err>;
186
187    /// Convert the value to a byte string, the bytes
188    /// of the string representation.
189    fn as_bytes(&self) -> Cow<'_, [u8]>;
190
191    /// Get a reference to the stored value
192    fn as_ref(&self) -> crate::ValueRef<'_>;
193
194    /// Get the size of the stored data type
195    fn data_len(&self) -> usize;
196}
197
198/// Errors that might occur while trying to convert to a particular value type
199/// from whatever type might be stored in a [`ParamValue`]-like object.
200#[derive(Debug, Clone, Error, PartialEq)]
201pub enum ParamValueParseError {
202    /// The value could not be interpreted as a floating point number.
203    #[error("Failed to extract a float from {0:?}")]
204    FailedToExtractFloat(Option<String>),
205    /// The value could not be interpreted as an integer.
206    #[error("Failed to extract a int from {0:?}")]
207    FailedToExtractInt(Option<String>),
208    /// The value could not be interpreted as a string.
209    #[error("Failed to extract a string")]
210    FailedToExtractString,
211    /// The value could not be interpreted as a byte buffer.
212    #[error("Failed to extract a buffer")]
213    FailedToExtractBuffer,
214}
215
216/// A [`Value`] can be parsed from a string infallibly, even though an error type
217/// is given, but unless one of the numerical parsers succeeds, the stored value will
218/// just be a string.
219impl FromStr for Value {
220    type Err = ParamValueParseError;
221
222    fn from_str(s: &str) -> Result<Self, Self::Err> {
223        if s.is_empty() {
224            return Ok(Self::Empty);
225        }
226        if let Ok(value) = s.parse::<i64>() {
227            Ok(Self::Int(value))
228        } else if let Ok(value) = s.parse::<f64>() {
229            Ok(Self::Float(value))
230        } else if let Ok(value) = s.parse::<bool>() {
231            Ok(Self::Boolean(value))
232        } else {
233            Ok(Self::String(s.to_string()))
234        }
235    }
236}
237
238impl Display for Value {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        match self {
241            Value::String(v) => f.write_str(v),
242            Value::Float(v) => v.fmt(f),
243            Value::Int(v) => v.fmt(f),
244            Value::Buffer(v) => f.write_str(&String::from_utf8_lossy(v)),
245            Value::Empty => f.write_str(""),
246            Value::Boolean(v) => v.fmt(f),
247            Value::List(v) => {
248                f.write_str("[ ")?;
249                if let Some(vi) = v.first() {
250                    vi.fmt(f)?;
251                }
252                for vi in v.iter().skip(1) {
253                    f.write_str(", ")?;
254                    vi.fmt(f)?;
255                }
256                f.write_str(" ]")
257            }
258        }
259    }
260}
261
262impl From<ParamValueParseError> for io::Error {
263    fn from(value: ParamValueParseError) -> Self {
264        Self::new(io::ErrorKind::InvalidData, value)
265    }
266}
267
268impl Value {
269    /// Convert a string value into a precise value type by trying
270    /// successive types to parse, defaulting to storing the string
271    /// as-is.
272    ///
273    /// This takes ownership of the string. To coerce from a borrowed
274    /// string see [`Value::wrap`].
275    pub fn new(s: String) -> Self {
276        if s.is_empty() {
277            Self::Empty
278        } else if let Ok(value) = s.parse::<i64>() {
279            Self::Int(value)
280        } else if let Ok(value) = s.parse::<f64>() {
281            Self::Float(value)
282        } else if let Ok(value) = s.parse::<bool>() {
283            Self::Boolean(value)
284        } else {
285            Self::String(s)
286        }
287    }
288
289    /// Convert a borrowed string value into a precise value type by trying
290    /// successive types to parse, defaulting to storing the string
291    /// as-is.
292    ///
293    /// This only makes a copy of the string if it cannot be parsed into a
294    /// numeric type.
295    pub fn wrap(s: &str) -> Self {
296        if s.is_empty() {
297            Self::Empty
298        } else if let Ok(value) = s.parse::<i64>() {
299            Self::Int(value)
300        } else if let Ok(value) = s.parse::<f64>() {
301            Self::Float(value)
302        } else {
303            Self::String(s.to_string())
304        }
305    }
306
307    /// See [`ParamValue::is_empty`].
308    pub fn is_empty(&self) -> bool {
309        matches!(self, Self::Empty)
310    }
311
312    /// See [`ParamValue::is_i64`].
313    pub fn is_i64(&self) -> bool {
314        matches!(self, Self::Int(_))
315    }
316
317    /// See [`ParamValue::is_f64`].
318    pub fn is_f64(&self) -> bool {
319        matches!(self, Self::Float(_))
320    }
321
322    /// See [`ParamValue::is_buffer`].
323    pub fn is_buffer(&self) -> bool {
324        matches!(self, Self::Buffer(_))
325    }
326
327    /// See [`ParamValue::is_str`].
328    pub fn is_str(&self) -> bool {
329        matches!(self, Self::String(_))
330    }
331
332    /// See [`ParamValue::is_list`].
333    pub fn is_list(&self) -> bool {
334        matches!(self, Self::List(_))
335    }
336
337    /// Store the value as a floating point number
338    pub fn coerce_f64(&mut self) -> Result<(), ParamValueParseError> {
339        let value = self.to_f64()?;
340        *self = Self::Float(value);
341        Ok(())
342    }
343
344    /// Store the value as an integer
345    pub fn coerce_i64(&mut self) -> Result<(), ParamValueParseError> {
346        let value = self.to_i64()?;
347        *self = Self::Int(value);
348        Ok(())
349    }
350
351    /// Store the value as a string
352    pub fn coerce_str(&mut self) -> Result<(), ParamValueParseError> {
353        let value = self.to_string();
354        *self = Self::String(value);
355        Ok(())
356    }
357
358    /// Discard the value, leaving this value [`Value::Empty`]
359    pub fn coerce_empty(&mut self) {
360        *self = Self::Empty;
361    }
362
363    /// Store the value as a byte buffer
364    pub fn coerce_buffer(&mut self) -> Result<(), ParamValueParseError> {
365        let buffer = self.to_buffer()?;
366        *self = Self::Buffer(buffer.into());
367        Ok(())
368    }
369
370    /// Store the value as a boolean
371    pub fn coerce_bool(&mut self) -> Result<(), ParamValueParseError> {
372        let value = self.to_bool()?;
373        *self = Self::Boolean(value);
374        Ok(())
375    }
376
377    /// Store the value as a list
378    pub fn coerce_list(&mut self) -> Result<(), ParamValueParseError> {
379        if !self.is_list() {
380            let mut tmp = Self::Empty;
381            core::mem::swap(&mut tmp, self);
382            *self = Self::List([tmp].into());
383        }
384        Ok(())
385    }
386
387    /// Convert the value to a text string and then try to parse it into `T`.
388    /// If the type is one of the common numeric types, prefer one of the provided
389    /// methods with a `to_` prefix as they avoid the string conversions.
390    pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
391        match self {
392            Value::String(s) => s.parse(),
393            Value::Float(v) => v.to_string().parse(),
394            Value::Int(i) => i.to_string().parse(),
395            Value::Buffer(b) => String::from_utf8_lossy(b).parse(),
396            Value::Empty => "".parse(),
397            Value::Boolean(b) => b.to_string().parse(),
398            Value::List(_) => self.to_string().parse(),
399        }
400    }
401
402    /// See [`ParamValue::to_bool`].
403    pub fn to_bool(&self) -> Result<bool, ParamValueParseError> {
404        if let Self::Boolean(val) = self {
405            Ok(*val)
406        } else if self.is_numeric() {
407            Ok(self.to_i64()? != 0)
408        } else if let Self::Empty = self {
409            Ok(false)
410        } else if let Ok(v) = self.parse() {
411            Ok(v)
412        } else {
413            Err(ParamValueParseError::FailedToExtractInt(Some(
414                self.to_string(),
415            )))
416        }
417    }
418
419    /// See [`ParamValue::to_f64`].
420    pub fn to_f64(&self) -> Result<f64, ParamValueParseError> {
421        if let Self::Float(val) = self {
422            return Ok(*val);
423        } else if let Self::Int(val) = self {
424            return Ok(*val as f64);
425        } else if let Self::String(val) = self {
426            if let Ok(v) = val.parse() {
427                return Ok(v);
428            }
429        }
430        Err(ParamValueParseError::FailedToExtractFloat(Some(
431            self.to_string(),
432        )))
433    }
434
435    /// See [`ParamValue::to_i64`].
436    pub fn to_i64(&self) -> Result<i64, ParamValueParseError> {
437        if let Self::Int(val) = self {
438            return Ok(*val);
439        } else if let Self::Float(val) = self {
440            return Ok(*val as i64);
441        } else if let Self::String(val) = self {
442            if let Ok(v) = val.parse() {
443                return Ok(v);
444            }
445        }
446        Err(ParamValueParseError::FailedToExtractInt(Some(
447            self.to_string(),
448        )))
449    }
450
451    /// See [`ParamValue::to_str`].
452    pub fn to_str(&self) -> Cow<'_, str> {
453        if let Self::String(val) = self {
454            Cow::Borrowed(val)
455        } else {
456            Cow::Owned(self.to_string())
457        }
458    }
459
460    /// See [`ParamValue::to_buffer`].
461    pub fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
462        if let Self::Buffer(val) = self {
463            Ok(Cow::Borrowed(val))
464        } else if let Self::String(val) = self {
465            Ok(Cow::Borrowed(val.as_bytes()))
466        } else {
467            Err(ParamValueParseError::FailedToExtractBuffer)
468        }
469    }
470
471    /// See [`ParamValue::as_ref`].
472    pub fn as_ref(&self) -> ValueRef<'_> {
473        self.into()
474    }
475
476    /// View this [`Value`] as a [`slice`]
477    pub fn as_slice(&self) -> &[Self] {
478        if let Self::List(val) = self {
479            val.as_ref()
480        } else {
481            core::slice::from_ref(self)
482        }
483    }
484}
485
486impl ParamValue for Value {
487    fn is_empty(&self) -> bool {
488        self.is_empty()
489    }
490
491    fn is_i64(&self) -> bool {
492        self.is_i64()
493    }
494
495    fn is_f64(&self) -> bool {
496        self.is_f64()
497    }
498
499    fn is_buffer(&self) -> bool {
500        self.is_buffer()
501    }
502
503    fn is_str(&self) -> bool {
504        self.is_str()
505    }
506
507    fn to_f64(&self) -> Result<f64, ParamValueParseError> {
508        self.to_f64()
509    }
510
511    fn to_i64(&self) -> Result<i64, ParamValueParseError> {
512        self.to_i64()
513    }
514
515    fn to_str(&self) -> Cow<'_, str> {
516        self.to_str()
517    }
518
519    fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
520        self.to_buffer()
521    }
522
523    fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
524        self.parse()
525    }
526
527    fn as_bytes(&self) -> Cow<'_, [u8]> {
528        match self {
529            Self::String(v) => Cow::Borrowed(v.as_bytes()),
530            Self::Buffer(v) => Cow::Borrowed(v.as_ref()),
531            Self::Float(v) => Cow::Owned(v.to_string().into_bytes()),
532            Self::Int(v) => Cow::Owned(v.to_string().into_bytes()),
533            Self::Empty => Cow::Borrowed(b""),
534            Self::Boolean(v) => Cow::Owned(v.to_string().into_bytes()),
535            Self::List(_) => Cow::Owned(self.to_string().into_bytes()),
536        }
537    }
538
539    fn as_ref(&self) -> ValueRef<'_> {
540        self.into()
541    }
542
543    fn data_len(&self) -> usize {
544        match self {
545            Self::String(v) => v.len(),
546            Self::Buffer(v) => v.len(),
547            Self::Float(_) => 8,
548            Self::Int(_) => 8,
549            Self::Empty => 0,
550            Self::Boolean(_) => mem::size_of::<bool>(),
551            Self::List(v) => v.iter().map(|vi| vi.data_len()).sum(),
552        }
553    }
554
555    fn is_boolean(&self) -> bool {
556        matches!(self, Self::Boolean(_))
557    }
558
559    fn to_bool(&self) -> Result<bool, ParamValueParseError> {
560        self.to_bool()
561    }
562
563    fn is_list(&self) -> bool {
564        self.is_list()
565    }
566
567    fn as_slice(&self) -> Cow<'_, [Value]> {
568        Cow::Borrowed(self.as_slice())
569    }
570}
571
572impl PartialEq<String> for Value {
573    fn eq(&self, other: &String) -> bool {
574        self.as_str() == other.as_str()
575    }
576}
577
578impl PartialEq<str> for Value {
579    fn eq(&self, other: &str) -> bool {
580        self.as_str() == other
581    }
582}
583
584impl PartialEq<&str> for Value {
585    fn eq(&self, other: &&str) -> bool {
586        self.as_str() == *other
587    }
588}
589
590impl PartialEq<i64> for Value {
591    fn eq(&self, other: &i64) -> bool {
592        if let Self::Int(val) = self {
593            val == other
594        } else {
595            false
596        }
597    }
598}
599
600impl PartialEq<f64> for Value {
601    fn eq(&self, other: &f64) -> bool {
602        if let Self::Float(val) = self {
603            val == other
604        } else {
605            false
606        }
607    }
608}
609
610impl PartialEq<bool> for Value {
611    fn eq(&self, other: &bool) -> bool {
612        if let Self::Boolean(val) = self {
613            val == other
614        } else {
615            false
616        }
617    }
618}
619
620impl Hash for Value {
621    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
622        core::mem::discriminant(self).hash(state);
623        match self {
624            Self::String(s) => s.hash(state),
625            Self::Float(v) => v.to_bits().hash(state),
626            Self::Int(v) => (*v).hash(state),
627            Self::Buffer(v) => v.hash(state),
628            Self::Empty => 0u8.hash(state),
629            Self::Boolean(v) => v.hash(state),
630            Self::List(v) => {
631                v.iter().for_each(|vi| vi.hash(state));
632            }
633        }
634    }
635}
636
637param_value_int!(i8);
638param_value_int!(i16);
639param_value_int!(i32);
640param_value_int!(i64);
641
642param_value_int!(u8);
643param_value_int!(u16);
644param_value_int!(u32);
645param_value_int!(u64);
646param_value_int!(usize);
647
648param_value_float!(f32);
649param_value_float!(f64);
650
651
652/// When the `serde` feature is enabled, [`mzdata_param::Value`] can be converted from
653/// `serde_json::Value` without going through the generic deserialization process.
654#[cfg(feature = "serde")]
655impl From<Value> for serde_json::Value {
656    fn from(value: Value) -> Self {
657        match value {
658            Value::Boolean(val) => serde_json::Value::Bool(val),
659            Value::Float(val) => {
660                serde_json::Value::Number(serde_json::Number::from_f64(val).unwrap())
661            }
662            Value::Int(val) => {
663                serde_json::Value::Number(serde_json::Number::from_i128(val as i128).unwrap())
664            }
665            Value::String(val) => serde_json::Value::String(val),
666            Value::Buffer(val) => serde_json::to_value(&val).unwrap(),
667            Value::Empty => serde_json::Value::Null,
668            Value::List(val) => {
669                let mut ve = Vec::new();
670                for vi in val {
671                    ve.push(vi.into());
672                }
673                serde_json::Value::Array(ve)
674            }
675        }
676    }
677}