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    #[error("Failed to extract a float from {0:?}")]
203    FailedToExtractFloat(Option<String>),
204    #[error("Failed to extract a int from {0:?}")]
205    FailedToExtractInt(Option<String>),
206    #[error("Failed to extract a string")]
207    FailedToExtractString,
208    #[error("Failed to extract a buffer")]
209    FailedToExtractBuffer,
210}
211
212/// A [`Value`] can be parsed from a string infallibly, even though an error type
213/// is given, but unless one of the numerical parsers succeeds, the stored value will
214/// just be a string.
215impl FromStr for Value {
216    type Err = ParamValueParseError;
217
218    fn from_str(s: &str) -> Result<Self, Self::Err> {
219        if s.is_empty() {
220            return Ok(Self::Empty);
221        }
222        if let Ok(value) = s.parse::<i64>() {
223            Ok(Self::Int(value))
224        } else if let Ok(value) = s.parse::<f64>() {
225            Ok(Self::Float(value))
226        } else if let Ok(value) = s.parse::<bool>() {
227            Ok(Self::Boolean(value))
228        } else {
229            Ok(Self::String(s.to_string()))
230        }
231    }
232}
233
234impl Display for Value {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        match self {
237            Value::String(v) => f.write_str(v),
238            Value::Float(v) => v.fmt(f),
239            Value::Int(v) => v.fmt(f),
240            Value::Buffer(v) => f.write_str(&String::from_utf8_lossy(v)),
241            Value::Empty => f.write_str(""),
242            Value::Boolean(v) => v.fmt(f),
243            Value::List(v) => {
244                f.write_str("[ ")?;
245                if let Some(vi) = v.first() {
246                    vi.fmt(f)?;
247                }
248                for vi in v.iter().skip(1) {
249                    f.write_str(", ")?;
250                    vi.fmt(f)?;
251                }
252                f.write_str(" ]")
253            }
254        }
255    }
256}
257
258impl From<ParamValueParseError> for io::Error {
259    fn from(value: ParamValueParseError) -> Self {
260        Self::new(io::ErrorKind::InvalidData, value)
261    }
262}
263
264impl Value {
265    /// Convert a string value into a precise value type by trying
266    /// successive types to parse, defaulting to storing the string
267    /// as-is.
268    ///
269    /// This takes ownership of the string. To coerce from a borrowed
270    /// string see [`Value::wrap`].
271    pub fn new(s: String) -> Self {
272        if s.is_empty() {
273            Self::Empty
274        } else if let Ok(value) = s.parse::<i64>() {
275            Self::Int(value)
276        } else if let Ok(value) = s.parse::<f64>() {
277            Self::Float(value)
278        } else if let Ok(value) = s.parse::<bool>() {
279            Self::Boolean(value)
280        } else {
281            Self::String(s)
282        }
283    }
284
285    /// Convert a borrowed string value into a precise value type by trying
286    /// successive types to parse, defaulting to storing the string
287    /// as-is.
288    ///
289    /// This only makes a copy of the string if it cannot be parsed into a
290    /// numeric type.
291    pub fn wrap(s: &str) -> Self {
292        if s.is_empty() {
293            Self::Empty
294        } else if let Ok(value) = s.parse::<i64>() {
295            Self::Int(value)
296        } else if let Ok(value) = s.parse::<f64>() {
297            Self::Float(value)
298        } else {
299            Self::String(s.to_string())
300        }
301    }
302
303    pub fn is_empty(&self) -> bool {
304        matches!(self, Self::Empty)
305    }
306
307    pub fn is_i64(&self) -> bool {
308        matches!(self, Self::Int(_))
309    }
310
311    pub fn is_f64(&self) -> bool {
312        matches!(self, Self::Float(_))
313    }
314
315    pub fn is_buffer(&self) -> bool {
316        matches!(self, Self::Buffer(_))
317    }
318
319    pub fn is_str(&self) -> bool {
320        matches!(self, Self::String(_))
321    }
322
323    pub fn is_list(&self) -> bool {
324        matches!(self, Self::List(_))
325    }
326
327    /// Store the value as a floating point number
328    pub fn coerce_f64(&mut self) -> Result<(), ParamValueParseError> {
329        let value = self.to_f64()?;
330        *self = Self::Float(value);
331        Ok(())
332    }
333
334    /// Store the value as an integer
335    pub fn coerce_i64(&mut self) -> Result<(), ParamValueParseError> {
336        let value = self.to_i64()?;
337        *self = Self::Int(value);
338        Ok(())
339    }
340
341    /// Store the value as a string
342    pub fn coerce_str(&mut self) -> Result<(), ParamValueParseError> {
343        let value = self.to_string();
344        *self = Self::String(value);
345        Ok(())
346    }
347
348    /// Discard the value, leaving this value [`Value::Empty`]
349    pub fn coerce_empty(&mut self) {
350        *self = Self::Empty;
351    }
352
353    /// Store the value as a byte buffer
354    pub fn coerce_buffer(&mut self) -> Result<(), ParamValueParseError> {
355        let buffer = self.to_buffer()?;
356        *self = Self::Buffer(buffer.into());
357        Ok(())
358    }
359
360    /// Store the value as a boolean
361    pub fn coerce_bool(&mut self) -> Result<(), ParamValueParseError> {
362        let value = self.to_bool()?;
363        *self = Self::Boolean(value);
364        Ok(())
365    }
366
367    /// Store the value as a list
368    pub fn coerce_list(&mut self) -> Result<(), ParamValueParseError> {
369        if !self.is_list() {
370            let mut tmp = Self::Empty;
371            core::mem::swap(&mut tmp, self);
372            *self = Self::List([tmp].into());
373        }
374        Ok(())
375    }
376
377    /// Convert the value to a text string and then try to parse it into `T`.
378    /// If the type is one of the common numeric types, prefer one of the provided
379    /// methods with a `to_` prefix as they avoid the string conversions.
380    pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
381        match self {
382            Value::String(s) => s.parse(),
383            Value::Float(v) => v.to_string().parse(),
384            Value::Int(i) => i.to_string().parse(),
385            Value::Buffer(b) => String::from_utf8_lossy(b).parse(),
386            Value::Empty => "".parse(),
387            Value::Boolean(b) => b.to_string().parse(),
388            Value::List(_) => self.to_string().parse(),
389        }
390    }
391
392    pub fn to_bool(&self) -> Result<bool, ParamValueParseError> {
393        if let Self::Boolean(val) = self {
394            Ok(*val)
395        } else if self.is_numeric() {
396            Ok(self.to_i64()? != 0)
397        } else if let Self::Empty = self {
398            Ok(false)
399        } else if let Ok(v) = self.parse() {
400            Ok(v)
401        } else {
402            Err(ParamValueParseError::FailedToExtractInt(Some(
403                self.to_string(),
404            )))
405        }
406    }
407
408    pub fn to_f64(&self) -> Result<f64, ParamValueParseError> {
409        if let Self::Float(val) = self {
410            return Ok(*val);
411        } else if let Self::Int(val) = self {
412            return Ok(*val as f64);
413        } else if let Self::String(val) = self {
414            if let Ok(v) = val.parse() {
415                return Ok(v);
416            }
417        }
418        Err(ParamValueParseError::FailedToExtractFloat(Some(
419            self.to_string(),
420        )))
421    }
422
423    pub fn to_i64(&self) -> Result<i64, ParamValueParseError> {
424        if let Self::Int(val) = self {
425            return Ok(*val);
426        } else if let Self::Float(val) = self {
427            return Ok(*val as i64);
428        } else if let Self::String(val) = self {
429            if let Ok(v) = val.parse() {
430                return Ok(v);
431            }
432        }
433        Err(ParamValueParseError::FailedToExtractInt(Some(
434            self.to_string(),
435        )))
436    }
437
438    pub fn to_str(&self) -> Cow<'_, str> {
439        if let Self::String(val) = self {
440            Cow::Borrowed(val)
441        } else {
442            Cow::Owned(self.to_string())
443        }
444    }
445
446    pub fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
447        if let Self::Buffer(val) = self {
448            Ok(Cow::Borrowed(val))
449        } else if let Self::String(val) = self {
450            Ok(Cow::Borrowed(val.as_bytes()))
451        } else {
452            Err(ParamValueParseError::FailedToExtractBuffer)
453        }
454    }
455
456    pub fn as_ref(&self) -> ValueRef<'_> {
457        self.into()
458    }
459
460    /// View this [`Value`] as a [`slice`]
461    pub fn as_slice(&self) -> &[Self] {
462        if let Self::List(val) = self {
463            val.as_ref()
464        } else {
465            core::slice::from_ref(self)
466        }
467    }
468}
469
470impl ParamValue for Value {
471    fn is_empty(&self) -> bool {
472        self.is_empty()
473    }
474
475    fn is_i64(&self) -> bool {
476        self.is_i64()
477    }
478
479    fn is_f64(&self) -> bool {
480        self.is_f64()
481    }
482
483    fn is_buffer(&self) -> bool {
484        self.is_buffer()
485    }
486
487    fn is_str(&self) -> bool {
488        self.is_str()
489    }
490
491    fn to_f64(&self) -> Result<f64, ParamValueParseError> {
492        self.to_f64()
493    }
494
495    fn to_i64(&self) -> Result<i64, ParamValueParseError> {
496        self.to_i64()
497    }
498
499    fn to_str(&self) -> Cow<'_, str> {
500        self.to_str()
501    }
502
503    fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
504        self.to_buffer()
505    }
506
507    fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
508        self.parse()
509    }
510
511    fn as_bytes(&self) -> Cow<'_, [u8]> {
512        match self {
513            Self::String(v) => Cow::Borrowed(v.as_bytes()),
514            Self::Buffer(v) => Cow::Borrowed(v.as_ref()),
515            Self::Float(v) => Cow::Owned(v.to_string().into_bytes()),
516            Self::Int(v) => Cow::Owned(v.to_string().into_bytes()),
517            Self::Empty => Cow::Borrowed(b""),
518            Self::Boolean(v) => Cow::Owned(v.to_string().into_bytes()),
519            Self::List(_) => Cow::Owned(self.to_string().into_bytes()),
520        }
521    }
522
523    fn as_ref(&self) -> ValueRef<'_> {
524        self.into()
525    }
526
527    fn data_len(&self) -> usize {
528        match self {
529            Self::String(v) => v.len(),
530            Self::Buffer(v) => v.len(),
531            Self::Float(_) => 8,
532            Self::Int(_) => 8,
533            Self::Empty => 0,
534            Self::Boolean(_) => mem::size_of::<bool>(),
535            Self::List(v) => v.iter().map(|vi| vi.data_len()).sum(),
536        }
537    }
538
539    fn is_boolean(&self) -> bool {
540        matches!(self, Self::Boolean(_))
541    }
542
543    fn to_bool(&self) -> Result<bool, ParamValueParseError> {
544        self.to_bool()
545    }
546
547    fn is_list(&self) -> bool {
548        self.is_list()
549    }
550
551    fn as_slice(&self) -> Cow<'_, [Value]> {
552        Cow::Borrowed(self.as_slice())
553    }
554}
555
556impl PartialEq<String> for Value {
557    fn eq(&self, other: &String) -> bool {
558        self.as_str() == other.as_str()
559    }
560}
561
562impl PartialEq<str> for Value {
563    fn eq(&self, other: &str) -> bool {
564        self.as_str() == other
565    }
566}
567
568impl PartialEq<&str> for Value {
569    fn eq(&self, other: &&str) -> bool {
570        self.as_str() == *other
571    }
572}
573
574impl PartialEq<i64> for Value {
575    fn eq(&self, other: &i64) -> bool {
576        if let Self::Int(val) = self {
577            val == other
578        } else {
579            false
580        }
581    }
582}
583
584impl PartialEq<f64> for Value {
585    fn eq(&self, other: &f64) -> bool {
586        if let Self::Float(val) = self {
587            val == other
588        } else {
589            false
590        }
591    }
592}
593
594impl PartialEq<bool> for Value {
595    fn eq(&self, other: &bool) -> bool {
596        if let Self::Boolean(val) = self {
597            val == other
598        } else {
599            false
600        }
601    }
602}
603
604impl Hash for Value {
605    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
606        core::mem::discriminant(self).hash(state);
607        match self {
608            Self::String(s) => s.hash(state),
609            Self::Float(v) => v.to_bits().hash(state),
610            Self::Int(v) => (*v).hash(state),
611            Self::Buffer(v) => v.hash(state),
612            Self::Empty => 0u8.hash(state),
613            Self::Boolean(v) => v.hash(state),
614            Self::List(v) => {
615                v.iter().for_each(|vi| vi.hash(state));
616            }
617        }
618    }
619}
620
621param_value_int!(i8);
622param_value_int!(i16);
623param_value_int!(i32);
624param_value_int!(i64);
625
626param_value_int!(u8);
627param_value_int!(u16);
628param_value_int!(u32);
629param_value_int!(u64);
630param_value_int!(usize);
631
632param_value_float!(f32);
633param_value_float!(f64);
634
635#[cfg(feature = "serde")]
636impl From<Value> for serde_json::Value {
637    fn from(value: Value) -> Self {
638        match value {
639            Value::Boolean(val) => serde_json::Value::Bool(val),
640            Value::Float(val) => {
641                serde_json::Value::Number(serde_json::Number::from_f64(val).unwrap())
642            }
643            Value::Int(val) => {
644                serde_json::Value::Number(serde_json::Number::from_i128(val as i128).unwrap())
645            }
646            Value::String(val) => serde_json::Value::String(val),
647            Value::Buffer(val) => serde_json::to_value(&val).unwrap(),
648            Value::Empty => serde_json::Value::Null,
649            Value::List(val) => {
650                let mut ve = Vec::new();
651                for vi in val {
652                    ve.push(vi.into());
653                }
654                serde_json::Value::Array(ve)
655            }
656        }
657    }
658}