1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
// Copyright (C) 2020 - 2022, J2 Innovations

//! Haystack Dict

use crate::{dict_get, dict_has};

use crate::haystack::val::*;
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::iter::{FromIterator, Iterator};
use std::ops::{Deref, DerefMut};

// Alias for the underlying Dict type
pub type DictType = BTreeMap<String, Value>;

/// A Haystack Dictionary
///
/// Uses a [BTreeMap<String, Value>](std::collections::BTreeMap) for the back-store
///
/// # Example
/// Create a dictionary value
/// ```
/// use libhaystack::*;
/// use libhaystack::val::*;
///
/// let dict = Value::from(dict! {
///        "site" => Value::make_marker(),
///        "name" => Value::make_str("Foo")
///    });
/// assert!(dict.is_dict());
///
/// // Get the Dict value
/// let dict_value = Dict::try_from(&dict).unwrap();
/// assert!(!dict_value.is_empty());
/// assert!(dict_value.has("site"));
///
/// // Get a `Str` value from the dictionary
/// assert_eq!(dict_value.get_str("name"), Some(&"Foo".into()));
///```
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
pub struct Dict {
    value: DictType,
}

/// Dictionary trait with utilities that help working with
/// the haystack Dict types.
pub trait HaystackDict {
    /// Get the optional `id` of this `Dict`
    fn id(&self) -> Option<&Ref>;

    /// Get the `id` Ref of this `Dict`, or a default Ref if the id is not present
    fn safe_id(&self) -> Ref;

    /// Get the optional `mod` of this `Dict`.
    /// On record `Dict`s this represents the last time this
    /// record has been changed, or the time it was created.
    fn ts(&self) -> Option<&DateTime>;

    /// True if Dict contains the key
    fn has(&self, key: &str) -> bool;

    /// True if key is not found
    fn missing(&self, key: &str) -> bool;

    /// True if key exists and is a Marker
    fn has_marker(&self, key: &str) -> bool;

    /// True if key exists and is a Na
    fn has_na(&self, key: &str) -> bool;

    /// True if key exists and is a Remove
    fn has_remove(&self, key: &str) -> bool;

    /// Get optional Bool for the key
    fn get_bool<'a>(&'a self, key: &str) -> Option<&'a Bool>;

    /// Get optional Number for the key
    fn get_num<'a>(&'a self, key: &str) -> Option<&'a Number>;

    /// Get optional Ref for the key
    fn get_ref<'a>(&'a self, key: &str) -> Option<&'a Ref>;

    /// Get optional Str for the key
    fn get_str<'a>(&'a self, key: &str) -> Option<&'a Str>;

    /// Get optional XStr for the key
    fn get_xstr<'a>(&'a self, key: &str) -> Option<&'a XStr>;

    /// Get optional Uri for the key
    fn get_uri<'a>(&'a self, key: &str) -> Option<&'a Uri>;

    /// Get optional Symbol for the key
    fn get_symbol<'a>(&'a self, key: &str) -> Option<&'a Symbol>;

    /// Get optional Date for the key
    fn get_date<'a>(&'a self, key: &str) -> Option<&'a Date>;

    /// Get optional Time for the key
    fn get_time<'a>(&'a self, key: &str) -> Option<&'a Time>;

    /// Get optional DateTime for the key
    fn get_date_time<'a>(&'a self, key: &str) -> Option<&'a DateTime>;

    /// Get optional Coord for the key
    fn get_coord<'a>(&'a self, key: &str) -> Option<&'a Coord>;

    /// Get optional Dict for the key
    fn get_dict<'a>(&'a self, key: &str) -> Option<&'a Dict>;

    /// Get optional List for the key
    fn get_list<'a>(&'a self, key: &str) -> Option<&'a List>;

    /// Get optional Grid for the key
    fn get_grid<'a>(&'a self, key: &str) -> Option<&'a Grid>;
}

impl Dict {
    /// Construct a new `Dict`
    pub fn new() -> Dict {
        Dict {
            value: DictType::new(),
        }
    }
}

/// Implements the `Default` trait for `Dict`
impl Default for Dict {
    fn default() -> Self {
        Dict {
            value: DictType::default(),
        }
    }
}

/// Implement FromIterator for `Dict`
///
/// Allows constructing a `Dict` from a `(String, Value)` tuple iterator
impl FromIterator<(String, Value)> for Dict {
    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
        Dict {
            value: DictType::from_iter(iter),
        }
    }
}

/// Proxy method calls to the `Dict`'s `value` member
impl Deref for Dict {
    type Target = DictType;
    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

/// Proxy method calls to the mutable `Dict`'s `value` member
impl DerefMut for Dict {
    #[inline]
    fn deref_mut(&mut self) -> &mut DictType {
        &mut self.value
    }
}

impl PartialOrd for Dict {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl Ord for Dict {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        if self.is_empty() && other.is_empty() {
            std::cmp::Ordering::Equal
        } else {
            let keys_cmp = self.value.keys().cmp(other.value.keys());
            if keys_cmp == std::cmp::Ordering::Equal {
                self.value.values().cmp(other.value.values())
            } else {
                keys_cmp
            }
        }
    }
}

impl HaystackDict for Dict {
    fn id(&self) -> Option<&Ref> {
        self.get_ref("id")
    }

    fn safe_id(&self) -> Ref {
        self.get_ref("id").map_or(Ref::default(), |id| id.clone())
    }

    fn ts(&self) -> Option<&DateTime> {
        self.get_date_time("mod")
    }

    fn has(&self, key: &str) -> bool {
        self.contains_key(key)
    }

    fn missing(&self, key: &str) -> bool {
        !self.has(key)
    }

    fn has_marker(&self, key: &str) -> bool {
        dict_has! {self, key, Marker}
    }

    fn has_na(&self, key: &str) -> bool {
        dict_has! {self, key, Na}
    }

    fn has_remove(&self, key: &str) -> bool {
        dict_has! {self, key, Remove}
    }

    fn get_bool<'a>(&'a self, key: &str) -> Option<&'a Bool> {
        dict_get! {self, key, Bool}
    }

    fn get_num<'a>(&'a self, key: &str) -> Option<&'a Number> {
        dict_get! {self, key, Number}
    }

    fn get_str<'a>(&'a self, key: &str) -> Option<&'a Str> {
        dict_get! {self, key, Str}
    }

    fn get_xstr<'a>(&'a self, key: &str) -> Option<&'a XStr> {
        dict_get! {self, key, XStr}
    }

    fn get_ref<'a>(&'a self, key: &str) -> Option<&'a Ref> {
        dict_get! {self, key, Ref}
    }

    fn get_uri<'a>(&'a self, key: &str) -> Option<&'a Uri> {
        dict_get! {self, key, Uri}
    }

    fn get_symbol<'a>(&'a self, key: &str) -> Option<&'a Symbol> {
        dict_get! {self, key, Symbol}
    }

    fn get_date<'a>(&'a self, key: &str) -> Option<&'a Date> {
        dict_get! {self, key, Date}
    }

    fn get_time<'a>(&'a self, key: &str) -> Option<&'a Time> {
        dict_get! {self, key, Time}
    }

    fn get_date_time<'a>(&'a self, key: &str) -> Option<&'a DateTime> {
        dict_get! {self, key, DateTime}
    }

    fn get_coord<'a>(&'a self, key: &str) -> Option<&'a Coord> {
        dict_get! {self, key, Coord}
    }

    fn get_dict<'a>(&'a self, key: &str) -> Option<&'a Dict> {
        dict_get! {self, key, Dict}
    }

    fn get_list<'a>(&'a self, key: &str) -> Option<&'a List> {
        dict_get! {self, key, List}
    }

    fn get_grid<'a>(&'a self, key: &str) -> Option<&'a Grid> {
        dict_get! {self, key, Grid}
    }
}

/// Converts from `DictType` to a `Dict`
impl From<DictType> for Dict {
    fn from(from: DictType) -> Self {
        Dict { value: from }
    }
}

/// Converts from `DictType` to a `Dict` `Value`
impl From<DictType> for Value {
    fn from(from: DictType) -> Self {
        Value::from(Dict { value: from })
    }
}

/// Converts from `Dict` to a `Dict` `Value`
impl From<Dict> for Value {
    fn from(value: Dict) -> Self {
        Value::Dict(value)
    }
}

/// Tries to convert from `Value` to a `Dict`
impl TryFrom<&Value> for Dict {
    type Error = &'static str;
    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        match value {
            Value::Dict(v) => Ok(v.clone()),
            _ => Err("Value is not an `Dict`"),
        }
    }
}

/// Pretty print this
impl Display for Dict {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        Debug::fmt(&self.value, f)
    }
}

/// A macro for creating a [Dict](crate::val::Dict) from literals
///
/// # Example
/// ```
///  use libhaystack::*;
///  use libhaystack::val::*;
///     let dict = dict!{
///         "site" => Value::make_marker(),
///         "dis" => Value::make_str("Some site")
///     };
/// ```
///
#[macro_export]
macro_rules! dict(
    { $($key:expr => $value:expr),* $(,)? } => {
        {
            let mut map = ::std::collections::BTreeMap::new();
            $(
                map.insert(String::from($key), $value);
            )+
            Dict::from(map)
        }
     };
);

/// A macro for retrieving a type from a [Dict](crate::val::Dict) by a key
///
/// This is a private API, consider using the [Dict](crate::val::Dict) specialized functions for
/// getting the values.
///
#[macro_export]
macro_rules! dict_get(
    { $self:ident, $key:expr, $type:ident } => {
        {
            if let Some(value) = $self.get($key) {
                match value {
                    Value::$type(val) => Some(&val),
                    _ => None,
                }
            } else {
                None
            }
    }
     };
);

/// A macro for determining if [Dict](crate::val::Dict) has a type for the key
///
/// Private API, use the [Dict](crate::val::Dict) specialized functions
///
#[macro_export]
macro_rules! dict_has(
    { $self:ident, $key:expr, $type:ident } => {
        {
            let entry = $self.get($key);
            matches!(entry, Some(Value::$type))
        }
     };
);