libhaystack/haystack/val/
value.rs

1// Copyright (C) 2020 - 2022, J2 Innovations
2
3use crate::haystack::val::boolean::*;
4use crate::haystack::val::coord::*;
5use crate::haystack::val::date::*;
6use crate::haystack::val::datetime::*;
7use crate::haystack::val::dict::*;
8use crate::haystack::val::grid::*;
9use crate::haystack::val::list::*;
10use crate::haystack::val::number::*;
11use crate::haystack::val::reference::*;
12use crate::haystack::val::string::*;
13use crate::haystack::val::symbol::*;
14use crate::haystack::val::time::*;
15use crate::haystack::val::uri::*;
16use crate::haystack::val::xstr::*;
17use crate::units::Unit;
18use std::fmt::{Display, Formatter};
19use std::hash::Hash;
20
21///
22/// The `Value` object is the universal type that can have any of supported
23/// tag values specified by the [Project Haystack 4.0 spec](https://project-haystack.org/doc/docHaystack/Kinds).
24///
25/// It is implemented as an Algebraic type (enum) than can be constructed
26/// from build-in types, such as `bool` or `i32`, and dedicated Haystack types such as `Str`
27///
28/// # Usage
29///
30/// Creating a scalar Haystack number value
31/// ```
32/// use libhaystack::val::*;
33/// use libhaystack::units::get_unit_or_default;
34///
35/// let num = Value::make_number_unit(42.0, get_unit_or_default("°F"));
36///
37/// assert!(num.is_number());
38/// assert_eq!(Number::try_from(&num).unwrap().unit, Some(get_unit_or_default("°F")));
39/// ```
40///
41/// Creating complex structures such as a Dict
42/// ```
43/// use libhaystack::dict;
44/// use libhaystack::val::*;
45///
46/// let dict = Value::make_dict(dict!("strTagName" => Value::from("Str Tag value"),
47///                  "marker" => Value::make_marker()));
48/// assert!(dict.is_dict());
49/// ```
50///
51/// Creating a Haystack Grid
52/// ```
53/// use libhaystack::dict;
54/// use libhaystack::val::*;
55///
56/// let grid = Value::make_grid_from_dicts(vec![
57///     dict!("dis" => Value::from("First dict row"),
58///           "id" => Value::make_ref_gen()),
59///     dict!("dis" => Value::from("Second dict row"),
60///           "id" => Value::make_ref_gen())
61/// ]);
62/// assert!(grid.is_grid());
63/// assert_eq!(Grid::try_from(&grid).unwrap().len(), 2);
64/// ```
65///
66#[derive(PartialOrd, Eq, Ord, Clone, Debug, Default)]
67pub enum Value {
68    /// No value
69    #[default]
70    Null,
71    /// A remove tag
72    Remove,
73    /// Marker tag
74    Marker,
75    /// Bool true/false
76    Bool(Bool),
77    /// Na tag
78    Na,
79    /// Number floating point value and optional unit
80    Number(Number),
81    // String value
82    Str(Str),
83    /// URI with string value
84    Uri(Uri),
85    /// Ref with string value
86    Ref(Ref),
87    /// A symbol with string value
88    Symbol(Symbol),
89    /// Date year, month, date
90    Date(Date),
91    /// Time hour, minutes, seconds with optional millis
92    Time(Time),
93    /// DateTime date, time and timezone
94    DateTime(DateTime),
95    /// Coordinate latitude and longitude
96    Coord(Coord),
97    /// XStr with type and value
98    XStr(XStr),
99    /// List of tags
100    List(List),
101    /// Dictionary of `String` key and `Value` values
102    Dict(Dict),
103    /// Haystack Grid
104    Grid(Grid),
105}
106
107///
108/// Implements the utility functions for creating and getting the current type
109///
110impl Value {
111    /// True if this `Value` is a haystack `Null`
112    pub fn is_null(&self) -> bool {
113        matches!(self, Value::Null)
114    }
115
116    /// True if this Haystack `Value` is not null
117    pub fn has_value(&self) -> bool {
118        !self.is_null()
119    }
120
121    /// Construct a `Remove` `Value`
122    pub fn make_remove() -> Value {
123        Value::Remove
124    }
125
126    /// True if this `Value` is a haystack `Remove`
127    pub fn is_remove(&self) -> bool {
128        matches!(self, Value::Remove)
129    }
130
131    /// Construct a `Marker` `Value`
132    pub fn make_marker() -> Value {
133        Value::Marker
134    }
135
136    /// True if this `Value` is a haystack `Marker`
137    pub fn is_marker(&self) -> bool {
138        matches!(self, Value::Marker)
139    }
140
141    /// Construct a `Bool` `Value`
142    pub fn make_bool(value: bool) -> Value {
143        Value::Bool(Bool::from(value))
144    }
145
146    /// Construct a `Bool` 'true' `Value`
147    pub fn make_true() -> Value {
148        Value::Bool(true.into())
149    }
150
151    /// True if [Value](crate::val::Value) is a `true` (Bool)[crate::val::Bool]
152    pub fn is_true(&self) -> bool {
153        match self {
154            Value::Bool(v) => v.value,
155            _ => false,
156        }
157    }
158
159    /// True if [Value](crate::val::Value) is a `false` (Bool)[crate::val::Bool]
160    pub fn is_false(&self) -> bool {
161        !self.is_true()
162    }
163
164    /// Construct a `Bool` 'false' `Value`
165    pub fn make_false() -> Value {
166        Value::Bool(false.into())
167    }
168
169    pub fn is_bool(&self) -> bool {
170        matches!(self, Value::Bool(_))
171    }
172
173    /// Construct a `Na` `Value`
174    pub fn make_na() -> Value {
175        Value::Na
176    }
177
178    /// True if this `Value` is a haystack `Na`
179    pub fn is_na(&self) -> bool {
180        matches!(self, Value::Na)
181    }
182
183    /// Construct a `Number` `Value` from a floating point value
184    pub fn make_number(value: f64) -> Value {
185        Value::from(value)
186    }
187
188    /// Construct a `Number` `Value` from a integer value
189    pub fn make_int(value: i64) -> Value {
190        Value::from(value as f64)
191    }
192
193    /// Construct a `Number` `Value` with an unit
194    pub fn make_number_unit(value: f64, unit: &'static Unit) -> Value {
195        Value::Number(Number::make_with_unit(value, unit))
196    }
197
198    /// True if this `Value` is a haystack `Number`
199    pub fn is_number(&self) -> bool {
200        matches!(self, Value::Number(_))
201    }
202
203    /// Construct a `Str` `Value` from a string
204    pub fn make_str(value: &str) -> Value {
205        Value::from(value)
206    }
207    /// True if this `Value` is a haystack `Str`
208    pub fn is_str(&self) -> bool {
209        matches!(self, Value::Str(_))
210    }
211
212    /// Construct a `Ref` `Value` from a string
213    pub fn make_ref(value: &str) -> Value {
214        Value::from(Ref::from(value))
215    }
216
217    /// Construct a `Ref` `Value` by generating an unique `Ref`
218    pub fn make_ref_gen() -> Value {
219        Value::from(Ref::gen())
220    }
221
222    /// Construct a `Ref` `Value` from a string with a display name
223    pub fn make_ref_with_dis(value: &str, dis: &str) -> Value {
224        Value::from(Ref {
225            value: String::from(value),
226            dis: Some(String::from(dis)),
227        })
228    }
229
230    /// True if this `Value` is a haystack `Ref`
231    pub fn is_ref(&self) -> bool {
232        matches!(self, Value::Ref(_))
233    }
234
235    /// Construct a `Symbol` `Value` from a string
236    pub fn make_symbol(value: &str) -> Value {
237        Value::from(Symbol::from(value))
238    }
239
240    /// True if this `Value` is a haystack `Symbol`
241    pub fn is_symbol(&self) -> bool {
242        matches!(self, Value::Symbol(_))
243    }
244
245    /// Construct a `Uri` `Value` from a string
246    pub fn make_uri(value: &str) -> Value {
247        Value::from(Uri::from(value))
248    }
249
250    /// True if this `Value` is a haystack `Uri`
251    pub fn is_uri(&self) -> bool {
252        matches!(self, Value::Uri(_))
253    }
254
255    /// Construct a `Date` `Value` from a `Date`
256    pub fn make_date(value: Date) -> Value {
257        Value::from(value)
258    }
259
260    /// True if this `Value` is a haystack `Date`
261    pub fn is_date(&self) -> bool {
262        matches!(self, Value::Date(_))
263    }
264
265    /// Construct a `Time` `Value` from a `Time`
266    pub fn make_time(value: Time) -> Value {
267        Value::from(value)
268    }
269
270    /// True if this `Value` is a haystack `Time`
271    pub fn is_time(&self) -> bool {
272        matches!(self, Value::Time(_))
273    }
274
275    /// Construct a `DateTime` `Value` from a `DateTime`
276    pub fn make_datetime(value: DateTime) -> Value {
277        Value::from(value)
278    }
279    /// Try to construct a `DateTime` `Value` from a string
280    pub fn make_datetime_from_iso(value: &str) -> Result<Value, String> {
281        let date_time = DateTime::parse_from_rfc3339(value)?;
282        Ok(Value::from(date_time))
283    }
284
285    /// True if this `Value` is a haystack `DateTime`
286    pub fn is_datetime(&self) -> bool {
287        matches!(self, Value::DateTime(_))
288    }
289
290    /// Construct a `Coord` `Value` from a `Coord`
291    pub fn make_coord(value: Coord) -> Value {
292        Value::from(value)
293    }
294
295    /// Construct a `Coord` `Value` from a latitude and longitude
296    pub fn make_coord_from(lat: f64, long: f64) -> Value {
297        Value::from(Coord { lat, long })
298    }
299
300    /// True if this `Value` is a haystack `Coord`
301    pub fn is_coord(&self) -> bool {
302        matches!(self, Value::Coord(_))
303    }
304
305    /// Construct a `XStr` `Value` from a `XStr`
306    pub fn make_xstr(value: XStr) -> Value {
307        Value::from(value)
308    }
309
310    /// Construct a `XStr` `Value` from a type and value
311    pub fn make_xstr_from(typ: &str, value: &str) -> Value {
312        Value::from(XStr::make(typ, value))
313    }
314
315    /// True if this `Value` is a haystack `XStr`
316    pub fn is_xstr(&self) -> bool {
317        matches!(self, Value::XStr(_))
318    }
319
320    /// Construct a `List` `Value` from a `List`
321    pub fn make_list(value: List) -> Value {
322        Value::from(value)
323    }
324
325    /// True if this `Value` is a haystack `List`
326    pub fn is_list(&self) -> bool {
327        matches!(self, Value::List(_))
328    }
329
330    /// Construct a `Dict` `Value` from a `Dict`
331    pub fn make_dict(value: Dict) -> Value {
332        Value::from(value)
333    }
334
335    /// True if this `Value` is a haystack `Dict`
336    pub fn is_dict(&self) -> bool {
337        matches!(self, Value::Dict(_))
338    }
339
340    /// Construct a `Grid` `Value` from a `Grid`
341    pub fn make_grid(value: Grid) -> Value {
342        Value::from(value)
343    }
344
345    /// Construct a `Grid` `Value` from a list of `Dict`s
346    pub fn make_grid_from_dicts(value: Vec<Dict>) -> Value {
347        Value::from(Grid::make_from_dicts(value))
348    }
349
350    /// True if this `Value` is a haystack `Dict
351    pub fn is_grid(&self) -> bool {
352        matches!(self, Value::Grid(_))
353    }
354}
355
356/// Implement user friendly display for a [Value](crate::val::Value)
357impl Display for Value {
358    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
359        use crate::haystack::encoding::zinc::encode::ToZinc;
360        match self {
361            Value::Null => f.write_str("Null"),
362
363            Value::Remove => f.write_str("Remove"),
364
365            Value::Marker => f.write_str("Marker"),
366
367            Value::Bool(val) => f.write_str(if val.value { "true" } else { "false" }),
368
369            Value::Na => f.write_str("Na"),
370
371            Value::Number(_)
372            | Value::Str(_)
373            | Value::Ref(_)
374            | Value::Uri(_)
375            | Value::Symbol(_)
376            | Value::Date(_)
377            | Value::Time(_)
378            | Value::DateTime(_)
379            | Value::Coord(_)
380            | Value::XStr(_)
381            | Value::List(_)
382            | Value::Dict(_)
383            | Value::Grid(_) => match self.to_zinc_string() {
384                Ok(zinc) => f.write_str(&zinc),
385                Err(_) => Err(std::fmt::Error),
386            },
387        }
388    }
389}
390
391impl Hash for Value {
392    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
393        match self {
394            Value::Null => std::any::TypeId::of::<Value>().hash(state),
395
396            Value::Marker => super::marker::Marker.hash(state),
397            Value::Remove => super::remove::Remove.hash(state),
398            Value::Na => super::na::Na.hash(state),
399
400            Value::Bool(val) => val.hash(state),
401            Value::Number(val) => val.hash(state),
402            Value::Str(val) => val.hash(state),
403            Value::Ref(val) => val.hash(state),
404            Value::Uri(val) => val.hash(state),
405            Value::Symbol(val) => val.hash(state),
406            Value::Date(val) => val.hash(state),
407            Value::Time(val) => val.hash(state),
408            Value::DateTime(val) => val.hash(state),
409            Value::Coord(val) => val.hash(state),
410            Value::XStr(val) => val.hash(state),
411            Value::List(val) => val.hash(state),
412            Value::Dict(val) => val.hash(state),
413            Value::Grid(val) => val.hash(state),
414        }
415    }
416}
417
418impl PartialEq for Value {
419    fn eq(&self, other: &Self) -> bool {
420        match self {
421            Value::Null => other.is_null(),
422
423            Value::Marker => other.is_marker(),
424            Value::Remove => other.is_remove(),
425            Value::Na => other.is_na(),
426
427            Value::Bool(val) => matches!(other, Value::Bool(v) if v == val),
428            Value::Number(val) => matches!(other, Value::Number(v) if v == val),
429            Value::Str(val) => matches!(other, Value::Str(v) if v == val),
430            Value::Ref(val) => matches!(other, Value::Ref(v) if v == val),
431            Value::Uri(val) => matches!(other, Value::Uri(v) if v == val),
432            Value::Symbol(val) => matches!(other, Value::Symbol(v) if v == val),
433            Value::Date(val) => matches!(other, Value::Date(v) if v == val),
434            Value::Time(val) => matches!(other, Value::Time(v) if v == val),
435            Value::DateTime(val) => matches!(other, Value::DateTime(v) if v == val),
436            Value::Coord(val) => matches!(other, Value::Coord(v) if v == val),
437            Value::XStr(val) => matches!(other, Value::XStr(v) if v == val),
438            Value::List(val) => matches!(other, Value::List(v) if v == val),
439            Value::Dict(val) => matches!(other, Value::Dict(v) if v == val),
440            Value::Grid(val) => matches!(other, Value::Grid(v) if v == val),
441        }
442    }
443}