1use serde::Serialize;
2use std::collections::HashMap;
3
4mod de;
5
6pub type ValueHash = HashMap<String, Value>;
8pub 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#[macro_export]
57macro_rules! value_hash {
58 ($($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}