1use chrono::{DateTime, FixedOffset};
2use serde::de::DeserializeOwned;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
14pub enum FactValue {
15 String(String),
17 Integer(i64),
19 Float(f64),
21 Boolean(bool),
23 Time(DateTime<FixedOffset>),
25 Object(HashMap<String, FactValue>),
27 Array(Vec<FactValue>),
29 Null,
31}
32
33impl PartialEq for FactValue {
36 fn eq(&self, other: &Self) -> bool {
37 use FactValue::*;
38 match (self, other) {
39 (String(a), String(b)) => a == b,
40 (Integer(a), Integer(b)) => a == b,
41 (Float(a), Float(b)) => a == b,
42 (Integer(a), Float(b)) | (Float(b), Integer(a)) => *a as f64 == *b,
43 (Boolean(a), Boolean(b)) => a == b,
44 (Time(a), Time(b)) => a == b,
45 (Object(a), Object(b)) => a == b,
46 (Array(a), Array(b)) => a == b,
47 (Null, Null) => true,
48 _ => false,
49 }
50 }
51}
52
53impl FactValue {
54 pub fn as_number(&self) -> Option<f64> {
56 match self {
57 FactValue::Integer(n) => Some(*n as f64),
58 FactValue::Float(n) => Some(*n),
59 _ => None,
60 }
61 }
62
63 pub fn as_integer(&self) -> Option<i64> {
65 match self {
66 FactValue::Integer(n) => Some(*n),
67 _ => None,
68 }
69 }
70
71 pub fn as_string(&self) -> Option<&str> {
73 match self {
74 FactValue::String(s) => Some(s),
75 _ => None,
76 }
77 }
78
79 pub fn as_boolean(&self) -> Option<bool> {
81 match self {
82 FactValue::Boolean(b) => Some(*b),
83 _ => None,
84 }
85 }
86
87 pub fn as_time(&self) -> Option<&DateTime<FixedOffset>> {
89 match self {
90 FactValue::Time(t) => Some(t),
91 _ => None,
92 }
93 }
94
95 pub fn as_object(&self) -> Option<&HashMap<String, FactValue>> {
97 match self {
98 FactValue::Object(obj) => Some(obj),
99 _ => None,
100 }
101 }
102
103 pub fn as_array(&self) -> Option<&[FactValue]> {
105 match self {
106 FactValue::Array(items) => Some(items),
107 _ => None,
108 }
109 }
110
111 pub fn type_name(&self) -> &'static str {
114 match self {
115 FactValue::String(_) => "string",
116 FactValue::Integer(_) => "integer",
117 FactValue::Float(_) => "float",
118 FactValue::Boolean(_) => "boolean",
119 FactValue::Time(_) => "time",
120 FactValue::Object(_) => "object",
121 FactValue::Array(_) => "array",
122 FactValue::Null => "nil",
123 }
124 }
125
126 pub fn is_truthy(&self) -> bool {
129 match self {
130 FactValue::Boolean(b) => *b,
131 FactValue::Integer(n) => *n != 0,
132 FactValue::Float(n) => *n != 0.0,
133 FactValue::String(s) => !s.is_empty(),
134 FactValue::Time(_) => true,
135 FactValue::Array(arr) => !arr.is_empty(),
136 FactValue::Object(obj) => !obj.is_empty(),
137 FactValue::Null => false,
138 }
139 }
140
141 pub fn from_serialize<T: Serialize + ?Sized>(value: &T) -> Result<FactValue, String> {
145 let _converting = serde_time::ConvertingGuard::enter();
146 serde_json::to_value(value)
147 .map(from_json)
148 .map_err(|e| format!("Cannot convert to a fact: {}", e))
149 }
150
151 pub fn deserialize<T: DeserializeOwned>(&self) -> Result<T, String> {
154 serde_json::from_value(to_json(self)).map_err(|e| format!("Cannot convert fact: {}", e))
155 }
156}
157
158fn from_json(value: serde_json::Value) -> FactValue {
159 use serde_json::Value;
160 match value {
161 Value::Null => FactValue::Null,
162 Value::Bool(b) => FactValue::Boolean(b),
163 Value::Number(n) => match n.as_i64() {
164 Some(i) => FactValue::Integer(i),
165 None => FactValue::Float(n.as_f64().unwrap_or(f64::NAN)),
166 },
167 Value::String(s) => match serde_time::parse_marked(&s) {
168 Some(time) => FactValue::Time(time),
169 None => FactValue::String(s),
170 },
171 Value::Array(items) => FactValue::Array(items.into_iter().map(from_json).collect()),
172 Value::Object(obj) => {
173 FactValue::Object(obj.into_iter().map(|(k, v)| (k, from_json(v))).collect())
174 }
175 }
176}
177
178fn to_json(value: &FactValue) -> serde_json::Value {
179 use serde_json::Value;
180 match value {
181 FactValue::Null => Value::Null,
182 FactValue::Boolean(b) => Value::Bool(*b),
183 FactValue::Integer(n) => Value::from(*n),
184 FactValue::Float(n) => serde_json::Number::from_f64(*n).map_or(Value::Null, Value::Number),
185 FactValue::String(s) => Value::String(s.clone()),
186 FactValue::Time(t) => Value::String(t.to_rfc3339()),
187 FactValue::Array(items) => Value::Array(items.iter().map(to_json).collect()),
188 FactValue::Object(obj) => {
189 Value::Object(obj.iter().map(|(k, v)| (k.clone(), to_json(v))).collect())
190 }
191 }
192}
193
194impl Serialize for FactValue {
195 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
196 use serde::ser::{SerializeMap, SerializeSeq};
197 match self {
198 FactValue::Null => serializer.serialize_unit(),
199 FactValue::Boolean(b) => serializer.serialize_bool(*b),
200 FactValue::Integer(n) => serializer.serialize_i64(*n),
201 FactValue::Float(n) => serializer.serialize_f64(*n),
202 FactValue::String(s) => serializer.serialize_str(s),
203 FactValue::Time(t) => serializer.serialize_str(&t.to_rfc3339()),
204 FactValue::Array(items) => {
205 let mut seq = serializer.serialize_seq(Some(items.len()))?;
206 for item in items {
207 seq.serialize_element(item)?;
208 }
209 seq.end()
210 }
211 FactValue::Object(fields) => {
212 let mut map = serializer.serialize_map(Some(fields.len()))?;
213 for (key, value) in fields {
214 map.serialize_entry(key, value)?;
215 }
216 map.end()
217 }
218 }
219 }
220}
221
222impl<'de> Deserialize<'de> for FactValue {
223 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
224 struct Visitor;
225
226 impl<'de> serde::de::Visitor<'de> for Visitor {
227 type Value = FactValue;
228
229 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
230 f.write_str("a string, number, boolean, null, array or map")
231 }
232 fn visit_unit<E>(self) -> Result<FactValue, E> {
233 Ok(FactValue::Null)
234 }
235 fn visit_none<E>(self) -> Result<FactValue, E> {
236 Ok(FactValue::Null)
237 }
238 fn visit_some<D: serde::Deserializer<'de>>(self, d: D) -> Result<FactValue, D::Error> {
239 <FactValue as Deserialize>::deserialize(d)
240 }
241 fn visit_bool<E>(self, b: bool) -> Result<FactValue, E> {
242 Ok(FactValue::Boolean(b))
243 }
244 fn visit_i64<E>(self, n: i64) -> Result<FactValue, E> {
245 Ok(FactValue::Integer(n))
246 }
247 fn visit_u64<E>(self, n: u64) -> Result<FactValue, E> {
248 Ok(i64::try_from(n).map_or(FactValue::Float(n as f64), FactValue::Integer))
249 }
250 fn visit_f64<E>(self, n: f64) -> Result<FactValue, E> {
251 Ok(FactValue::Float(n))
252 }
253 fn visit_str<E>(self, s: &str) -> Result<FactValue, E> {
254 Ok(FactValue::String(s.to_string()))
255 }
256 fn visit_string<E>(self, s: String) -> Result<FactValue, E> {
257 Ok(FactValue::String(s))
258 }
259 fn visit_seq<A: serde::de::SeqAccess<'de>>(
260 self,
261 mut seq: A,
262 ) -> Result<FactValue, A::Error> {
263 let mut items = Vec::new();
264 while let Some(item) = seq.next_element()? {
265 items.push(item);
266 }
267 Ok(FactValue::Array(items))
268 }
269 fn visit_map<A: serde::de::MapAccess<'de>>(
270 self,
271 mut map: A,
272 ) -> Result<FactValue, A::Error> {
273 let mut fields = HashMap::new();
274 while let Some((key, value)) = map.next_entry::<String, FactValue>()? {
275 fields.insert(key, value);
276 }
277 Ok(FactValue::Object(fields))
278 }
279 }
280
281 deserializer.deserialize_any(Visitor)
282 }
283}
284
285impl From<i64> for FactValue {
286 fn from(value: i64) -> Self {
287 FactValue::Integer(value)
288 }
289}
290
291impl From<f64> for FactValue {
292 fn from(value: f64) -> Self {
293 FactValue::Float(value)
294 }
295}
296
297impl From<bool> for FactValue {
298 fn from(value: bool) -> Self {
299 FactValue::Boolean(value)
300 }
301}
302
303impl From<&str> for FactValue {
304 fn from(value: &str) -> Self {
305 FactValue::String(value.to_string())
306 }
307}
308
309impl From<String> for FactValue {
310 fn from(value: String) -> Self {
311 FactValue::String(value)
312 }
313}
314
315impl From<DateTime<FixedOffset>> for FactValue {
316 fn from(value: DateTime<FixedOffset>) -> Self {
317 FactValue::Time(value)
318 }
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct Fact {
324 pub name: String,
326 pub value: FactValue,
328}
329
330impl Fact {
331 pub fn new(name: String, value: FactValue) -> Self {
333 Self { name, value }
334 }
335
336 pub fn get_field(&self, field_name: &str) -> Option<&FactValue> {
338 match &self.value {
339 FactValue::Object(obj) => obj.get(field_name),
340 _ => None,
341 }
342 }
343
344 pub fn set_field(&mut self, field_name: String, value: FactValue) -> Result<(), String> {
346 match &mut self.value {
347 FactValue::Object(obj) => {
348 obj.insert(field_name, value);
349 Ok(())
350 }
351 _ => Err("Cannot set field on non-object fact".to_string()),
352 }
353 }
354
355 pub fn deserialize<T: DeserializeOwned>(&self) -> Result<T, String> {
358 self.value.deserialize()
359 }
360}
361
362impl Fact {
364 pub fn from_object(name: String, obj: HashMap<String, FactValue>) -> Self {
366 Self::new(name, FactValue::Object(obj))
367 }
368
369 pub fn from_serialize<T: Serialize + ?Sized>(name: &str, value: &T) -> Result<Self, String> {
371 Ok(Self::new(
372 name.to_string(),
373 FactValue::from_serialize(value)?,
374 ))
375 }
376
377 pub fn string_fact(name: String, value: String) -> Self {
379 Self::new(name, FactValue::String(value))
380 }
381
382 pub fn integer_fact(name: String, value: i64) -> Self {
384 Self::new(name, FactValue::Integer(value))
385 }
386
387 pub fn number_fact(name: String, value: f64) -> Self {
389 Self::new(name, FactValue::Float(value))
390 }
391
392 pub fn boolean_fact(name: String, value: bool) -> Self {
394 Self::new(name, FactValue::Boolean(value))
395 }
396}
397
398pub mod serde_time {
407 use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone};
408 use serde::de::Error as _;
409 use serde::{Deserialize, Deserializer, Serializer};
410 use std::cell::Cell;
411
412 const MARKER: &str = "\u{1}rust-runes:time\u{1}";
415
416 thread_local! {
417 static CONVERTING: Cell<bool> = const { Cell::new(false) };
418 }
419
420 pub(crate) struct ConvertingGuard(bool);
422
423 impl ConvertingGuard {
424 pub(crate) fn enter() -> Self {
425 ConvertingGuard(CONVERTING.replace(true))
426 }
427 }
428
429 impl Drop for ConvertingGuard {
430 fn drop(&mut self) {
431 CONVERTING.set(self.0);
432 }
433 }
434
435 pub(crate) fn parse_marked(text: &str) -> Option<DateTime<FixedOffset>> {
436 DateTime::parse_from_rfc3339(text.strip_prefix(MARKER)?).ok()
437 }
438
439 pub fn serialize<Tz: TimeZone, S: Serializer>(
441 time: &DateTime<Tz>,
442 serializer: S,
443 ) -> Result<S::Ok, S::Error> {
444 let text = time
445 .fixed_offset()
446 .to_rfc3339_opts(SecondsFormat::AutoSi, true);
447 if CONVERTING.get() {
448 serializer.serialize_str(&format!("{}{}", MARKER, text))
449 } else {
450 serializer.serialize_str(&text)
451 }
452 }
453
454 pub fn deserialize<'de, Tz, D>(deserializer: D) -> Result<DateTime<Tz>, D::Error>
456 where
457 Tz: TimeZone,
458 DateTime<Tz>: From<DateTime<FixedOffset>>,
459 D: Deserializer<'de>,
460 {
461 let text = String::deserialize(deserializer)?;
462 let text = text.strip_prefix(MARKER).unwrap_or(&text);
463 DateTime::parse_from_rfc3339(text)
464 .map(Into::into)
465 .map_err(D::Error::custom)
466 }
467
468 pub mod option {
470 use super::*;
471
472 pub fn serialize<Tz: TimeZone, S: Serializer>(
474 time: &Option<DateTime<Tz>>,
475 serializer: S,
476 ) -> Result<S::Ok, S::Error> {
477 match time {
478 Some(time) => super::serialize(time, serializer),
479 None => serializer.serialize_none(),
480 }
481 }
482
483 pub fn deserialize<'de, Tz, D>(deserializer: D) -> Result<Option<DateTime<Tz>>, D::Error>
485 where
486 Tz: TimeZone,
487 DateTime<Tz>: From<DateTime<FixedOffset>>,
488 D: Deserializer<'de>,
489 {
490 match Option::<String>::deserialize(deserializer)? {
491 Some(text) => {
492 let text = text.strip_prefix(MARKER).unwrap_or(&text);
493 DateTime::parse_from_rfc3339(text)
494 .map(|t| Some(t.into()))
495 .map_err(D::Error::custom)
496 }
497 None => Ok(None),
498 }
499 }
500 }
501}