Skip to main content

schema_core/common/
generic_value.rs

1use std::collections::BTreeMap;
2use std::fmt;
3
4use rust_decimal::Decimal;
5use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
6use serde::{Deserialize, Serialize, Serializer};
7
8#[derive(Debug, Clone, Hash, PartialEq, Eq)]
9pub enum GenericValue {
10    Null,
11    Bool(bool),
12    Int(i64),
13    Decimal(Decimal),
14    String(String),
15    Array(Vec<GenericValue>),
16    Map(BTreeMap<String, GenericValue>),
17}
18
19impl GenericValue {
20    /// Whether this value can stand as a single SQL parameter, key, or literal:
21    /// true for the scalar variants, false for `Null` and the composite
22    /// `Array`/`Map`. The one home for that rule — the Postgres source applies
23    /// it when binding params, building keys, and inlining literals. Written as
24    /// an exhaustive match so a new variant cannot be added without classifying
25    /// it here.
26    pub fn is_bindable_scalar(&self) -> bool {
27        match self {
28            GenericValue::Bool(_)
29            | GenericValue::Int(_)
30            | GenericValue::Decimal(_)
31            | GenericValue::String(_) => true,
32            GenericValue::Null | GenericValue::Array(_) | GenericValue::Map(_) => false,
33        }
34    }
35}
36
37/// Serializes to the **natural** JSON shape — `5`, `"x"`, `true`, `null`,
38/// `[…]`, `{…}` — not serde's externally-tagged enum form (`{"Int": 5}`). This
39/// is what makes a serialized `Config` or `IndexMapping` read like the data it
40/// describes; a value's variant is evident from its JSON shape.
41impl Serialize for GenericValue {
42    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
43        match self {
44            GenericValue::Null => serializer.serialize_none(),
45            GenericValue::Bool(value) => serializer.serialize_bool(*value),
46            GenericValue::Int(value) => serializer.serialize_i64(*value),
47            // `Decimal` has an inherent `serialize` (to bytes) that shadows the
48            // trait method, so call the trait method explicitly.
49            GenericValue::Decimal(value) => Serialize::serialize(value, serializer),
50            GenericValue::String(value) => serializer.serialize_str(value),
51            GenericValue::Array(items) => items.serialize(serializer),
52            GenericValue::Map(map) => map.serialize(serializer),
53        }
54    }
55}
56
57/// Deserializes from the same **natural** shape it serializes to, by inspecting
58/// the value's form rather than expecting serde's tagged-enum encoding. This
59/// requires a self-describing format (JSON, MessagePack, …); it is what lets a
60/// compiled config round-trip. A `Decimal` is written as a string by its serde
61/// integration, so a decimal literal round-trips as a [`String`](GenericValue::String)
62/// — a documented edge for the rare decimal `constant` / `default`.
63impl<'de> Deserialize<'de> for GenericValue {
64    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
65        struct GenericValueVisitor;
66
67        impl<'de> Visitor<'de> for GenericValueVisitor {
68            type Value = GenericValue;
69
70            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71                f.write_str("any JSON-like value")
72            }
73
74            fn visit_bool<E>(self, v: bool) -> Result<GenericValue, E> {
75                Ok(GenericValue::Bool(v))
76            }
77
78            fn visit_i64<E>(self, v: i64) -> Result<GenericValue, E> {
79                Ok(GenericValue::Int(v))
80            }
81
82            fn visit_u64<E>(self, v: u64) -> Result<GenericValue, E> {
83                match i64::try_from(v) {
84                    Ok(i) => Ok(GenericValue::Int(i)),
85                    Err(_) => Ok(GenericValue::Decimal(Decimal::from(v))),
86                }
87            }
88
89            fn visit_f64<E: de::Error>(self, v: f64) -> Result<GenericValue, E> {
90                Decimal::try_from(v)
91                    .map(GenericValue::Decimal)
92                    .map_err(E::custom)
93            }
94
95            fn visit_str<E>(self, v: &str) -> Result<GenericValue, E> {
96                Ok(GenericValue::String(v.to_owned()))
97            }
98
99            fn visit_string<E>(self, v: String) -> Result<GenericValue, E> {
100                Ok(GenericValue::String(v))
101            }
102
103            fn visit_none<E>(self) -> Result<GenericValue, E> {
104                Ok(GenericValue::Null)
105            }
106
107            fn visit_unit<E>(self) -> Result<GenericValue, E> {
108                Ok(GenericValue::Null)
109            }
110
111            fn visit_some<D: Deserializer<'de>>(self, d: D) -> Result<GenericValue, D::Error> {
112                GenericValue::deserialize(d)
113            }
114
115            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<GenericValue, A::Error> {
116                let mut items = Vec::new();
117                while let Some(item) = seq.next_element()? {
118                    items.push(item);
119                }
120                Ok(GenericValue::Array(items))
121            }
122
123            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<GenericValue, A::Error> {
124                let mut out = BTreeMap::new();
125                while let Some((key, value)) = map.next_entry()? {
126                    out.insert(key, value);
127                }
128                Ok(GenericValue::Map(out))
129            }
130        }
131
132        deserializer.deserialize_any(GenericValueVisitor)
133    }
134}