1use reifydb_value::value::{
5 constraint::{precision::Precision, scale::Scale},
6 digest::Digest,
7 value_type::ValueType,
8};
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError, ser::Error as SerError};
10use serde_json::{Map, Value as JsonValue};
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct WireValueType(pub ValueType);
14
15impl From<ValueType> for WireValueType {
16 fn from(ty: ValueType) -> Self {
17 Self(ty)
18 }
19}
20
21impl From<WireValueType> for ValueType {
22 fn from(wire: WireValueType) -> Self {
23 wire.0
24 }
25}
26
27const ID: &str = "id";
28const UNDERLYING: &str = "underlying";
29const NAME: &str = "name";
30const TYPE: &str = "type";
31const ACCURACY: &str = "accuracy";
32const PRECISION: &str = "precision";
33const SCALE: &str = "scale";
34
35fn scalar_id(ty: &ValueType) -> Option<&'static str> {
36 Some(match ty {
37 ValueType::Boolean => "Boolean",
38 ValueType::Float4 => "Float4",
39 ValueType::Float8 => "Float8",
40 ValueType::Int1 => "Int1",
41 ValueType::Int2 => "Int2",
42 ValueType::Int4 => "Int4",
43 ValueType::Int8 => "Int8",
44 ValueType::Int16 => "Int16",
45 ValueType::Utf8 => "Utf8",
46 ValueType::Uint1 => "Uint1",
47 ValueType::Uint2 => "Uint2",
48 ValueType::Uint4 => "Uint4",
49 ValueType::Uint8 => "Uint8",
50 ValueType::Uint16 => "Uint16",
51 ValueType::Date => "Date",
52 ValueType::DateTime => "DateTime",
53 ValueType::Time => "Time",
54 ValueType::Duration => "Duration",
55 ValueType::IdentityId => "IdentityId",
56 ValueType::Uuid4 => "Uuid4",
57 ValueType::Uuid7 => "Uuid7",
58 ValueType::Blob => "Blob",
59 ValueType::Any => "Any",
60 ValueType::DictionaryId => "DictionaryId",
61 ValueType::Decimal {
62 ..
63 }
64 | ValueType::Option(_)
65 | ValueType::List(_)
66 | ValueType::Record(_)
67 | ValueType::Tuple(_)
68 | ValueType::Digest {
69 ..
70 } => {
71 return None;
72 }
73 })
74}
75
76fn scalar_from_id(id: &str) -> Option<ValueType> {
77 Some(match id {
78 "Boolean" => ValueType::Boolean,
79 "Float4" => ValueType::Float4,
80 "Float8" => ValueType::Float8,
81 "Int1" => ValueType::Int1,
82 "Int2" => ValueType::Int2,
83 "Int4" => ValueType::Int4,
84 "Int8" => ValueType::Int8,
85 "Int16" => ValueType::Int16,
86 "Utf8" => ValueType::Utf8,
87 "Uint1" => ValueType::Uint1,
88 "Uint2" => ValueType::Uint2,
89 "Uint4" => ValueType::Uint4,
90 "Uint8" => ValueType::Uint8,
91 "Uint16" => ValueType::Uint16,
92 "Date" => ValueType::Date,
93 "DateTime" => ValueType::DateTime,
94 "Time" => ValueType::Time,
95 "Duration" => ValueType::Duration,
96 "IdentityId" => ValueType::IdentityId,
97 "Uuid4" => ValueType::Uuid4,
98 "Uuid7" => ValueType::Uuid7,
99 "Blob" => ValueType::Blob,
100 "Any" => ValueType::Any,
101 "DictionaryId" => ValueType::DictionaryId,
102 _ => return None,
103 })
104}
105
106fn family_descriptor(id: &str, precision: Precision, scale: Option<Scale>) -> JsonValue {
107 let mut object = Map::new();
108 object.insert(ID.to_string(), JsonValue::String(id.to_string()));
109 object.insert(PRECISION.to_string(), JsonValue::from(precision.value()));
110 if let Some(scale) = scale {
111 object.insert(SCALE.to_string(), JsonValue::from(scale.value()));
112 }
113 JsonValue::Object(object)
114}
115
116fn family_param(object: &Map<String, JsonValue>, key: &str, default: u8, value: &JsonValue) -> Result<u8, String> {
117 match object.get(key) {
118 None => Ok(default),
119 Some(param) => param
120 .as_u64()
121 .and_then(|param| u8::try_from(param).ok())
122 .ok_or_else(|| format!("`{key}` must be an unsigned 8-bit integer: {value}")),
123 }
124}
125
126fn family_precision(
127 object: &Map<String, JsonValue>,
128 default: Precision,
129 value: &JsonValue,
130) -> Result<Precision, String> {
131 let precision = family_param(object, PRECISION, default.value(), value)?;
132 Precision::try_new(precision).map_err(|error| format!("invalid `{PRECISION}` {precision}: {error}"))
133}
134
135fn descriptor(id: &str, underlying: Option<JsonValue>) -> JsonValue {
136 let mut object = Map::new();
137 object.insert(ID.to_string(), JsonValue::String(id.to_string()));
138 if let Some(underlying) = underlying {
139 object.insert(UNDERLYING.to_string(), underlying);
140 }
141 JsonValue::Object(object)
142}
143
144pub fn to_json(ty: &ValueType) -> JsonValue {
145 if let Some(id) = scalar_id(ty) {
146 return descriptor(id, None);
147 }
148 match ty {
149 ValueType::Decimal {
150 precision,
151 scale,
152 } => family_descriptor("Decimal", *precision, Some(*scale)),
153 ValueType::Option(inner) => descriptor("Option", Some(to_json(inner))),
154 ValueType::List(inner) => descriptor("List", Some(to_json(inner))),
155 ValueType::Tuple(members) => {
156 descriptor("Tuple", Some(JsonValue::Array(members.iter().map(to_json).collect())))
157 }
158 ValueType::Record(fields) => {
159 let entries = fields
160 .iter()
161 .map(|(name, ty)| {
162 let mut field = Map::new();
163 field.insert(NAME.to_string(), JsonValue::String(name.clone()));
164 field.insert(TYPE.to_string(), to_json(ty));
165 JsonValue::Object(field)
166 })
167 .collect();
168 descriptor("Record", Some(JsonValue::Array(entries)))
169 }
170 ValueType::Digest {
171 inner,
172 accuracy,
173 } => {
174 let mut object = descriptor("Digest", Some(to_json(inner)));
175 if let JsonValue::Object(fields) = &mut object {
176 fields.insert(ACCURACY.to_string(), JsonValue::from(*accuracy));
177 }
178 object
179 }
180
181 other => descriptor(&other.to_string(), None),
182 }
183}
184
185pub fn from_json(value: &JsonValue) -> Result<ValueType, String> {
186 let object = value.as_object().ok_or_else(|| format!("expected a type descriptor object, got {value}"))?;
187 let id = object
188 .get(ID)
189 .and_then(JsonValue::as_str)
190 .ok_or_else(|| format!("type descriptor is missing a string `{ID}`: {value}"))?;
191 let underlying = object.get(UNDERLYING);
192
193 if let Some(scalar) = scalar_from_id(id) {
194 return Ok(scalar);
195 }
196
197 let child = || -> Result<ValueType, String> {
198 let underlying = underlying.ok_or_else(|| format!("`{id}` needs an `{UNDERLYING}` type: {value}"))?;
199 from_json(underlying)
200 };
201
202 match id {
203 "Decimal" => {
204 let precision = family_precision(
205 object,
206 ValueType::DECIMAL.precision().expect("decimal carries a precision"),
207 value,
208 )?;
209 let default_scale = ValueType::DECIMAL.scale().expect("decimal carries a scale");
210 let scale = family_param(object, SCALE, default_scale.value(), value)?;
211 let scale = Scale::try_new_with_precision(scale, precision)
212 .map_err(|error| format!("invalid `{SCALE}` {scale}: {error}"))?;
213 Ok(ValueType::decimal(precision, scale))
214 }
215 "Option" => Ok(ValueType::Option(Box::new(child()?))),
216 "List" => Ok(ValueType::List(Box::new(child()?))),
217 "Tuple" => {
218 let members = underlying
219 .and_then(JsonValue::as_array)
220 .ok_or_else(|| format!("`Tuple` needs an `{UNDERLYING}` array: {value}"))?;
221 members.iter().map(from_json).collect::<Result<Vec<_>, _>>().map(ValueType::Tuple)
222 }
223 "Record" => {
224 let fields = underlying
225 .and_then(JsonValue::as_array)
226 .ok_or_else(|| format!("`Record` needs an `{UNDERLYING}` array: {value}"))?;
227 fields.iter()
228 .map(|field| {
229 let name = field
230 .get(NAME)
231 .and_then(JsonValue::as_str)
232 .ok_or_else(|| format!("record field is missing `{NAME}`: {field}"))?;
233 let ty = field
234 .get(TYPE)
235 .ok_or_else(|| format!("record field is missing `{TYPE}`: {field}"))?;
236 Ok((name.to_string(), from_json(ty)?))
237 })
238 .collect::<Result<Vec<_>, String>>()
239 .map(ValueType::Record)
240 }
241 "Digest" => {
242 let inner = child()?;
243 let accuracy = object
244 .get(ACCURACY)
245 .and_then(JsonValue::as_u64)
246 .and_then(|accuracy| u32::try_from(accuracy).ok())
247 .ok_or_else(|| format!("`Digest` needs an unsigned 32-bit `{ACCURACY}`: {value}"))?;
248 Digest::new(inner.clone(), accuracy)
249 .map_err(|error| format!("invalid `Digest` type: {error}"))?;
250 Ok(ValueType::Digest {
251 inner: Box::new(inner),
252 accuracy,
253 })
254 }
255 unknown => Err(format!("unknown type id `{unknown}`")),
256 }
257}
258
259impl Serialize for WireValueType {
260 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
261 to_json(&self.0).serialize(serializer).map_err(S::Error::custom)
262 }
263}
264
265impl<'de> Deserialize<'de> for WireValueType {
266 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
267 let value = JsonValue::deserialize(deserializer)?;
268 from_json(&value).map(WireValueType).map_err(D::Error::custom)
269 }
270}