Skip to main content

kdb_connection/
value.rs

1use serde::Serialize;
2use std::collections::HashMap;
3
4mod de;
5
6/// Map of values
7pub type ValueHash = HashMap<String, Value>;
8/// Array of values
9pub type ValueArray = Vec<Value>;
10
11#[derive(Serialize, Debug, Clone, PartialEq)]
12#[serde(untagged)]
13enum ValueData
14{
15    Null,
16    Boolean(bool),
17    Integer64(i64),
18    Float64(f64),
19    Array(ValueArray),
20    Map(ValueHash),
21    String(String),
22}
23
24#[derive(Serialize, Debug, Clone, PartialEq)]
25#[serde(tag = "type", rename = "literal")]
26pub struct Value
27{
28    datatype: String,
29    value: ValueData,
30}
31
32impl Value
33{
34    pub fn from_uri_value<T: Into<Value>>(datatype: impl Into<String>, v: T) -> Self
35    {
36        let t: Value = v.into();
37        Self {
38            datatype: datatype.into(),
39            value: t.value,
40        }
41    }
42    pub fn datatype_ref(&self) -> &String
43    {
44        &self.datatype
45    }
46}
47
48/// Convenient macro for creating ValueMap.
49///
50/// Example:
51///
52/// ```rust
53/// # use kdb_connection::{value_hash, ValueHash};
54/// let value_hash: ValueHash = value_hash!("hello" => 12);
55/// ```
56#[macro_export]
57macro_rules! value_hash {
58  // map-like
59  ($($k:expr => $v:expr),* $(,)?) => {
60    {
61      let value_map: $crate::ValueHash = core::convert::From::from([$(($k.to_string(), $v.into()),)*]);
62      value_map
63    }
64  };
65}
66
67macro_rules! define_value_field {
68    ($name: ident, $type: ident, $uri: expr) => {
69        impl<'a> TryFrom<&'a Value> for &'a $type
70        {
71            type Error = crate::errors::Error;
72            fn try_from(value: &'a Value) -> Result<&'a $type, Self::Error>
73            {
74                match &value.value
75                {
76                    ValueData::$name(v) => Ok(&v),
77                    _ => Err(Self::Error::InvalidValueCast {
78                        type_name: stringify!($name),
79                        value: format!("{:?}", value),
80                    }),
81                }
82            }
83        }
84        impl TryInto<$type> for Value
85        {
86            type Error = crate::errors::Error;
87            fn try_into(self) -> Result<$type, Self::Error>
88            {
89                match self.value
90                {
91                    ValueData::$name(v) => Ok(v),
92                    _ => Err(Self::Error::InvalidValueCast {
93                        type_name: stringify!($name),
94                        value: format!("{:?}", self),
95                    }),
96                }
97            }
98        }
99
100        impl From<$type> for Value
101        {
102            fn from(value: $type) -> Self
103            {
104                return Self {
105                    datatype: $uri.into(),
106                    value: ValueData::$name(value),
107                };
108            }
109        }
110    };
111}
112
113define_value_field!(Boolean, bool, "http://www.w3.org/2001/XMLSchema#bool");
114define_value_field!(Integer64, i64, "http://www.w3.org/2001/XMLSchema#long");
115define_value_field!(Float64, f64, "http://www.w3.org/2001/XMLSchema#float64");
116define_value_field!(String, String, "http://www.w3.org/2001/XMLSchema#string");
117define_value_field!(Map, ValueHash, "http://askco.re/datatype#valuehash");
118define_value_field!(Array, ValueArray, "http://askco.re/datatype#valuelist");
119
120impl From<ValueData> for Value
121{
122    fn from(v: ValueData) -> Self
123    {
124        let datatype = match &v
125        {
126            ValueData::Null => "http://www.w3.org/2001/XMLSchema#nil",
127            ValueData::Boolean(_) => "http://www.w3.org/2001/XMLSchema#boolean",
128            ValueData::Integer64(_) => "http://www.w3.org/2001/XMLSchema#long",
129            ValueData::Float64(_) => "http://www.w3.org/2001/XMLSchema#float64",
130            ValueData::String(_) => "http://www.w3.org/2001/XMLSchema#string",
131            ValueData::Map(_) => "http://askco.re/datatype#valuehash",
132            ValueData::Array(_) => "http://askco.re/datatype#valuelist",
133        };
134
135        Value {
136            datatype: datatype.to_string(),
137            value: v,
138        }
139    }
140}
141
142impl From<&str> for Value
143{
144    fn from(value: &str) -> Self
145    {
146        value.to_string().into()
147    }
148}
149
150#[cfg(test)]
151mod tests
152{
153    use super::*;
154
155    #[test]
156    fn test_value_integer_serialisation()
157    {
158        let v: Value = 42.into();
159        let vs = serde_json::to_string(&v).unwrap();
160        assert_eq!(
161            vs,
162            "{\"type\":\"literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#long\",\"value\":42}"
163        );
164        let v = serde_json::from_str::<Value>(vs.as_str()).unwrap();
165        assert_eq!(v.datatype, "http://www.w3.org/2001/XMLSchema#long");
166        let vi: i64 = v.try_into().unwrap();
167        assert_eq!(vi, 42);
168    }
169    #[test]
170    fn test_value_hash_serialisation()
171    {
172        let vh: ValueHash = [("a".into(), 32.into()), ("b".into(), "c".into())].into();
173        let v: Value = vh.clone().into();
174        let vs = serde_json::to_string(&v).unwrap();
175        assert!(
176            vs == "{\"type\":\"literal\",\"datatype\":\"http://askco.re/datatype#valuehash\",\"value\":{\"b\":{\"type\":\"literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"value\":\"c\"},\"a\":{\"type\":\"literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#long\",\"value\":32}}}"
177                || vs
178                    == "{\"type\":\"literal\",\"datatype\":\"http://askco.re/datatype#valuehash\",\"value\":{\"a\":{\"type\":\"literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#long\",\"value\":32},\"b\":{\"type\":\"literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"value\":\"c\"}}}"
179        );
180        let v = serde_json::from_str::<Value>(vs.as_str()).unwrap();
181        assert_eq!(v.datatype, "http://askco.re/datatype#valuehash");
182        let vh2: ValueHash = v.try_into().unwrap();
183        assert_eq!(vh, vh2);
184
185        let v = serde_json::from_str::<Value>(
186            r#"
187    {
188      "datatype":"http://askco.re/datatype#valuehash",
189      "type":"literal",
190      "value":{
191        "datatype":{
192          "datatype":"http://www.w3.org/2001/XMLSchema#string",
193          "value":"http://www.w3.org/2001/XMLSchema#long"
194        },
195        "type":{
196          "datatype":"http://www.w3.org/2001/XMLSchema#string",
197          "value":"literal"
198        },
199        "value":{
200          "datatype":"http://www.w3.org/2001/XMLSchema#long",
201          "value":12
202        }
203      }
204    }"#,
205        )
206        .unwrap();
207        let vh2: ValueHash = v.try_into().unwrap();
208        assert_eq!(vh2.len(), 3);
209        assert_eq!(
210            *vh2.get("datatype").unwrap(),
211            "http://www.w3.org/2001/XMLSchema#long".into()
212        );
213        assert_eq!(*vh2.get("type").unwrap(), "literal".into());
214        assert_eq!(
215            *vh2.get("value").unwrap(),
216            Value::from_uri_value("http://www.w3.org/2001/XMLSchema#long", 12)
217        );
218    }
219}