1pub mod value;
23
24use std::collections::BTreeMap;
25use std::collections::btree_map::Entry;
26
27use serde::{Deserialize, Serialize};
28use serde_json::{Map, Value as Json};
29use snafu::ResultExt;
30
31use crate::data_type::ConcreteDataType;
32use crate::error::{self, InvalidJson2LayoutSnafu, Result, UnsupportedJsonTypeSnafu};
33use crate::json::value::{JsonValue, JsonVariant, encode_serde_json_as_jsonb};
34use crate::schema::ColumnDefaultConstraint;
35use crate::types::json_type::{JsonNativeType, JsonObjectType};
36use crate::value::{ListValue, StructValue, Value};
37
38pub const JSON2_MAX_STRUCTURED_DEPTH: usize = 50;
40pub const JSON2_REMAINDER_FIELD_NAME: &str = "!__remainder__!";
42
43#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
46pub struct JsonSettings {
47 #[serde(default)]
48 type_hints: Vec<JsonTypeHint>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
53 max_auto_expanded_paths: Option<u32>,
54}
55
56#[derive(Deserialize)]
57struct JsonSettingsSerde {
58 #[serde(default)]
59 type_hints: Vec<JsonTypeHint>,
60 #[serde(default)]
61 max_auto_expanded_paths: Option<u32>,
62}
63
64impl<'de> Deserialize<'de> for JsonSettings {
65 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
66 where
67 D: serde::Deserializer<'de>,
68 {
69 let settings = JsonSettingsSerde::deserialize(deserializer)?;
70 Self::try_new(settings.type_hints, settings.max_auto_expanded_paths)
71 .map_err(serde::de::Error::custom)
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
81pub struct JsonTypeHint {
82 pub path: Vec<String>,
90 #[serde(rename = "type")]
91 pub data_type: ConcreteDataType,
92 pub nullable: bool,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub default_constraint: Option<ColumnDefaultConstraint>,
95 pub inverted_index: bool,
96}
97
98#[derive(Clone, Debug)]
100pub struct JsonContext<'a> {
101 pub path: Vec<String>,
103 pub settings: &'a JsonSettings,
105}
106
107impl JsonSettings {
108 pub fn try_new(
110 type_hints: Vec<JsonTypeHint>,
111 max_auto_expanded_paths: Option<u32>,
112 ) -> Result<Self> {
113 validate_type_hints(&type_hints)?;
114 Ok(Self {
115 type_hints,
116 max_auto_expanded_paths,
117 })
118 }
119
120 pub fn type_hints(&self) -> &[JsonTypeHint] {
121 &self.type_hints
122 }
123
124 pub fn max_auto_expanded_paths(&self) -> Option<u32> {
125 self.max_auto_expanded_paths
126 }
127
128 pub fn into_parts(self) -> (Vec<JsonTypeHint>, Option<u32>) {
129 (self.type_hints, self.max_auto_expanded_paths)
130 }
131
132 pub fn decode(&self, value: Value) -> Result<Json> {
134 let mut context = JsonContext {
135 path: Vec::new(),
136 settings: self,
137 };
138 decode_value_with_context(value, &mut context)
139 }
140
141 pub fn encode(&self, json: Json) -> Result<Value> {
143 if let Json::Object(object) = &json
144 && object.contains_key(JSON2_REMAINDER_FIELD_NAME)
145 {
146 return error::InvalidJsonSnafu {
147 value: format!(
148 "root object cannot contain reserved field '{JSON2_REMAINDER_FIELD_NAME}'"
149 ),
150 }
151 .fail();
152 }
153 let mut context = JsonContext {
154 path: Vec::new(),
155 settings: self,
156 };
157 encode_json_with_context(json, &mut context).map(|v| Value::Json(Box::new(v)))
158 }
159}
160
161fn validate_type_hints(type_hints: &[JsonTypeHint]) -> Result<()> {
162 let mut object = JsonObjectType::new();
163 for hint in type_hints {
164 if hint.path.len() > JSON2_MAX_STRUCTURED_DEPTH {
165 return InvalidJson2LayoutSnafu {
166 reason: format!(
167 "JSON2 type hint path cannot exceed {JSON2_MAX_STRUCTURED_DEPTH} segments"
168 ),
169 }
170 .fail();
171 }
172 if hint
173 .path
174 .first()
175 .is_some_and(|x| x == JSON2_REMAINDER_FIELD_NAME)
176 {
177 return InvalidJson2LayoutSnafu {
178 reason: format!(
179 "JSON2 type hint path cannot be rooted at reserved field '{JSON2_REMAINDER_FIELD_NAME}'"
180 ),
181 }
182 .fail();
183 }
184 let data_type = match &hint.data_type {
185 ConcreteDataType::Boolean(_)
186 | ConcreteDataType::UInt8(_)
187 | ConcreteDataType::UInt16(_)
188 | ConcreteDataType::UInt32(_)
189 | ConcreteDataType::UInt64(_)
190 | ConcreteDataType::Int8(_)
191 | ConcreteDataType::Int16(_)
192 | ConcreteDataType::Int32(_)
193 | ConcreteDataType::Int64(_)
194 | ConcreteDataType::Float32(_)
195 | ConcreteDataType::Float64(_)
196 | ConcreteDataType::String(_) => (&hint.data_type).into(),
197 data_type => {
198 return InvalidJson2LayoutSnafu {
199 reason: format!("unsupported JSON2 type hint data type: {data_type}"),
200 }
201 .fail();
202 }
203 };
204 validate_type_hint(&mut object, &hint.path, data_type)?;
205 }
206 Ok(())
207}
208
209fn validate_type_hint(
210 object: &mut JsonObjectType,
211 path: &[String],
212 data_type: JsonNativeType,
213) -> Result<()> {
214 let Some((name, path)) = path.split_first() else {
215 return InvalidJson2LayoutSnafu {
216 reason: "JSON2 type hint path must not be empty".to_string(),
217 }
218 .fail();
219 };
220 if path.is_empty() {
221 if object.insert(name.clone(), data_type).is_some() {
222 return InvalidJson2LayoutSnafu {
223 reason: format!("duplicate JSON2 type hint path '{name}'"),
224 }
225 .fail();
226 }
227 return Ok(());
228 }
229
230 let child = object
231 .entry(name.clone())
232 .or_insert_with(|| JsonNativeType::Object(JsonObjectType::new()));
233 let JsonNativeType::Object(child) = child else {
234 return InvalidJson2LayoutSnafu {
235 reason: format!("conflicting JSON2 type hint path at '{name}'"),
236 }
237 .fail();
238 };
239 validate_type_hint(child, path, data_type)
240}
241
242impl<'a> JsonContext<'a> {
243 fn type_hint(&self) -> Option<&'a JsonTypeHint> {
244 self.settings
245 .type_hints
246 .iter()
247 .find(|hint| hint.path == self.path)
248 }
249}
250
251fn with_key_context<T>(
252 context: &mut JsonContext,
253 key: &str,
254 f: impl FnOnce(&mut JsonContext) -> Result<T>,
255) -> Result<T> {
256 context.path.push(key.to_string());
257 let result = f(context);
258 context.path.pop();
259 result
260}
261
262fn encode_json_with_context(json: Json, context: &mut JsonContext) -> Result<JsonValue> {
264 if context.path.is_empty() && !matches!(json, Json::Object(_)) {
265 return UnsupportedJsonTypeSnafu.fail();
266 }
267
268 match json {
269 Json::Object(json_object) => encode_json_object_with_context(json_object, context),
270 Json::Array(json_array) => encode_json_array_with_context(json_array, context),
271 _ => encode_json_value_with_context(json, context),
272 }
273}
274
275fn encode_json_object_with_context<'a>(
276 json_object: Map<String, Json>,
277 context: &mut JsonContext<'a>,
278) -> Result<JsonValue> {
279 let mut object = BTreeMap::new();
280 for (key, value) in json_object {
281 let value = with_key_context(context, &key, |context| {
282 if let Some(hint) = context.type_hint() {
283 encode_json_value_with_hint(value, hint, context)
284 } else {
285 encode_json_value_with_context(value, context)
286 }
287 })?;
288
289 object.insert(key, value.into_variant());
290 }
291
292 apply_missing_type_hints(&mut object, context)?;
293
294 Ok(JsonValue::new(JsonVariant::Object(object)))
295}
296
297fn apply_missing_type_hints(
298 object: &mut BTreeMap<String, JsonVariant>,
299 context: &mut JsonContext,
300) -> Result<()> {
301 for hint in &context.settings.type_hints {
302 if hint.path.len() > context.path.len() && hint.path.starts_with(&context.path) {
303 let depth = context.path.len();
304 let key = &hint.path[depth];
305 with_key_context(context, key, |context| {
306 insert_missing_type_hint(object, context, hint, depth)
307 })?;
308 }
309 }
310 Ok(())
311}
312
313fn insert_missing_type_hint(
314 object: &mut BTreeMap<String, JsonVariant>,
315 field_context: &mut JsonContext,
316 hint: &JsonTypeHint,
317 depth: usize,
318) -> Result<()> {
319 let key = &hint.path[depth];
320 let is_leaf = depth + 1 == hint.path.len();
321
322 if is_leaf {
323 if !object.contains_key(key) {
324 let value = encode_missing_type_hint_value(hint, field_context)?;
325 object.insert(key.clone(), value.into_variant());
326 }
327 return Ok(());
328 }
329
330 match object.entry(key.clone()) {
331 Entry::Occupied(mut entry) => match entry.get_mut() {
332 JsonVariant::Object(child) => {
333 insert_missing_type_hint(child, field_context, hint, depth + 1)
334 }
335 _ => error::InvalidJsonSnafu {
336 value: format!(
337 "JSON2 type hint path {} expects object at {}",
338 hint.path.join("."),
339 field_context.path.join(".")
340 ),
341 }
342 .fail(),
343 },
344 Entry::Vacant(entry) => {
345 let mut child = BTreeMap::new();
346 insert_missing_type_hint(&mut child, field_context, hint, depth + 1)?;
347 entry.insert(JsonVariant::Object(child));
348 Ok(())
349 }
350 }
351}
352
353fn encode_missing_type_hint_value(
354 hint: &JsonTypeHint,
355 context: &mut JsonContext,
356) -> Result<JsonValue> {
357 if let Some(default_constraint) = &hint.default_constraint {
358 let value = default_constraint.create_default(&hint.data_type, hint.nullable)?;
359 let json = decode_primitive_value(value)?;
360 return encode_json_value_with_hint(json, hint, context);
361 }
362
363 if hint.nullable {
364 Ok(JsonValue::null())
365 } else {
366 error::InvalidJsonSnafu {
367 value: format!(
368 "missing non-null JSON2 type hint path {}",
369 hint.path.join(".")
370 ),
371 }
372 .fail()
373 }
374}
375
376fn encode_json_value_with_hint(
377 json: Json,
378 hint: &JsonTypeHint,
379 context: &mut JsonContext,
380) -> Result<JsonValue> {
381 if json.is_null() {
382 return if hint.nullable {
383 Ok(JsonValue::null())
384 } else {
385 error::InvalidJsonSnafu {
386 value: format!(
387 "JSON2 type hint path {} is not nullable",
388 context.path.join(".")
389 ),
390 }
391 .fail()
392 };
393 }
394
395 let invalid_type = || {
396 error::InvalidJsonSnafu {
397 value: format!(
398 "JSON value at {} does not match JSON2 type hint {}",
399 context.path.join("."),
400 hint.data_type
401 ),
402 }
403 .fail()
404 };
405
406 match (&hint.data_type, json) {
407 (ConcreteDataType::String(_), Json::String(v)) => Ok(v.into()),
408 (
409 ConcreteDataType::Int8(_)
410 | ConcreteDataType::Int16(_)
411 | ConcreteDataType::Int32(_)
412 | ConcreteDataType::Int64(_),
413 Json::Number(v),
414 ) => match v.as_i64() {
415 Some(v) => Ok(v.into()),
416 None => invalid_type(),
417 },
418 (
419 ConcreteDataType::UInt8(_)
420 | ConcreteDataType::UInt16(_)
421 | ConcreteDataType::UInt32(_)
422 | ConcreteDataType::UInt64(_),
423 Json::Number(v),
424 ) => match v.as_u64() {
425 Some(v) => Ok(v.into()),
426 None => invalid_type(),
427 },
428 (ConcreteDataType::Float32(_) | ConcreteDataType::Float64(_), Json::Number(v)) => {
429 match v.as_f64() {
430 Some(v) => Ok(v.into()),
431 None => invalid_type(),
432 }
433 }
434 (ConcreteDataType::Boolean(_), Json::Bool(v)) => Ok(v.into()),
435 _ => invalid_type(),
436 }
437}
438
439fn encode_json_array_with_context<'a>(
440 json_array: Vec<Json>,
441 context: &mut JsonContext<'a>,
442) -> Result<JsonValue> {
443 let json_array_len = json_array.len();
444 let mut items = Vec::with_capacity(json_array_len);
445
446 for (index, value) in json_array.into_iter().enumerate() {
447 let item_value = with_key_context(context, &index.to_string(), |context| {
448 encode_json_value_with_context(value, context)
449 })?;
450 items.push(item_value);
451 }
452
453 let merged_item_type = if let Some((first, rests)) = items.split_first() {
459 let mut merged = first.json_type().clone();
460 for rest in rests.iter().map(|x| x.json_type()) {
461 if matches!(merged, JsonNativeType::Variant) {
462 break;
463 }
464 merged.merge(rest);
465 }
466 Some(merged)
467 } else {
468 None
469 };
470 if let Some(unified_item_type) = merged_item_type {
471 for item in &mut items {
472 item.try_align(&unified_item_type)?;
473 }
474 }
475 let items = items
476 .into_iter()
477 .map(|x| x.into_variant())
478 .collect::<Vec<_>>();
479 Ok(JsonValue::new(JsonVariant::Array(items)))
480}
481
482fn encode_json_value_with_context(json: Json, context: &mut JsonContext) -> Result<JsonValue> {
484 if context.path.len() >= JSON2_MAX_STRUCTURED_DEPTH
485 && matches!(&json, Json::Object(_) | Json::Array(_))
486 {
487 return Ok(JsonValue::new(JsonVariant::Variant(
488 encode_serde_json_as_jsonb(json),
489 )));
490 }
491
492 match json {
493 Json::Null => Ok(JsonValue::null()),
494 Json::Bool(b) => Ok(b.into()),
495 Json::Number(n) => {
496 if let Some(i) = n.as_i64() {
497 Ok(i.into())
498 } else if let Some(u) = n.as_u64() {
499 if u <= i64::MAX as u64 {
500 Ok((u as i64).into())
501 } else {
502 Ok(u.into())
503 }
504 } else if let Some(f) = n.as_f64() {
505 Ok(f.into())
506 } else {
507 Ok(n.to_string().into())
509 }
510 }
511 Json::String(s) => Ok(s.into()),
512 Json::Array(arr) => encode_json_array_with_context(arr, context),
513 Json::Object(obj) => encode_json_object_with_context(obj, context),
514 }
515}
516
517fn decode_value_with_context(value: Value, context: &mut JsonContext) -> Result<Json> {
519 match value {
520 Value::Struct(struct_value) => decode_struct_with_context(struct_value, context),
521 Value::List(list_value) => decode_list_with_context(list_value, context),
522 _ => decode_primitive_value(value),
523 }
524}
525
526fn decode_struct_with_context<'a>(
528 struct_value: StructValue,
529 context: &mut JsonContext<'a>,
530) -> Result<Json> {
531 let mut json_object = Map::with_capacity(struct_value.len());
532
533 let (items, fields) = struct_value.into_parts();
534
535 for (field, field_value) in fields.fields().iter().zip(items) {
536 let json_value = with_key_context(context, field.name(), |context| {
537 decode_value_with_context(field_value, context)
538 })?;
539 json_object.insert(field.name().to_string(), json_value);
540 }
541
542 Ok(Json::Object(json_object))
543}
544
545fn decode_list_with_context(list_value: ListValue, context: &mut JsonContext) -> Result<Json> {
547 let mut json_array = Vec::with_capacity(list_value.len());
548
549 let data_items = list_value.take_items();
550
551 for (index, item) in data_items.into_iter().enumerate() {
552 let json_value = with_key_context(context, &index.to_string(), |context| {
553 decode_value_with_context(item, context)
554 })?;
555 json_array.push(json_value);
556 }
557
558 Ok(Json::Array(json_array))
559}
560
561fn decode_primitive_value(value: Value) -> Result<Json> {
563 match value {
564 Value::Null => Ok(Json::Null),
565 Value::Boolean(b) => Ok(Json::Bool(b)),
566 Value::UInt8(v) => Ok(Json::from(v)),
567 Value::UInt16(v) => Ok(Json::from(v)),
568 Value::UInt32(v) => Ok(Json::from(v)),
569 Value::UInt64(v) => Ok(Json::from(v)),
570 Value::Int8(v) => Ok(Json::from(v)),
571 Value::Int16(v) => Ok(Json::from(v)),
572 Value::Int32(v) => Ok(Json::from(v)),
573 Value::Int64(v) => Ok(Json::from(v)),
574 Value::Float32(v) => Ok(Json::from(v.0)),
575 Value::Float64(v) => Ok(Json::from(v.0)),
576 Value::String(s) => Ok(Json::String(s.as_utf8().to_string())),
577 Value::Binary(b) => serde_json::to_value(b.as_ref()).context(error::SerializeSnafu),
578 Value::Date(v) => Ok(Json::from(v.val())),
579 Value::Timestamp(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
580 Value::Time(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
581 Value::IntervalYearMonth(v) => {
582 serde_json::to_value(v.to_i32()).context(error::SerializeSnafu)
583 }
584 Value::IntervalDayTime(v) => {
585 serde_json::to_value(v.to_i64()).context(error::SerializeSnafu)
586 }
587 Value::IntervalMonthDayNano(v) => {
588 serde_json::to_value(v.to_i128()).context(error::SerializeSnafu)
589 }
590 Value::Duration(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
591 Value::Decimal128(v) => serde_json::to_value(v.to_string()).context(error::SerializeSnafu),
592 Value::Struct(_) | Value::List(_) | Value::Json(_) => {
593 Err(error::InvalidJsonSnafu {
595 value: "Structured values should be handled by context-aware decoding".to_string(),
596 }
597 .build())
598 }
599 }
600}
601
602#[cfg(test)]
603mod tests {
604 use std::sync::Arc;
605
606 use serde_json::json;
607
608 use super::*;
609 use crate::data_type::ConcreteDataType;
610 use crate::types::{ListType, StructField, StructType};
611
612 fn struct_field_value<'a>(struct_value: &'a StructValue, field_name: &str) -> &'a Value {
613 let index = struct_value
614 .struct_type()
615 .fields()
616 .iter()
617 .position(|field| field.name() == field_name)
618 .expect("field exists");
619 &struct_value.items()[index]
620 }
621
622 #[test]
623 fn test_json_settings_forward_compatibility()
624 -> std::result::Result<(), Box<dyn std::error::Error>> {
625 let json_str = r#"{
626 "type_hints": [
627 {
628 "path": ["user", "age"],
629 "type": {
630 "Int64": {}
631 },
632 "nullable": false,
633 "default_constraint": {
634 "Value": {
635 "Int64": 18
636 }
637 },
638 "inverted_index": true
639 },
640 {
641 "path": ["user", "name"],
642 "type": {
643 "String": {
644 "size_type": "Utf8"
645 }
646 },
647 "nullable": true,
648 "inverted_index": false
649 }
650 ]
651 }"#;
652
653 let deserialized = serde_json::from_str::<JsonSettings>(json_str)?;
654
655 let expected = JsonSettings::try_new(
656 vec![
657 JsonTypeHint {
658 path: vec!["user".to_string(), "age".to_string()],
659 data_type: ConcreteDataType::int64_datatype(),
660 nullable: false,
661 default_constraint: Some(ColumnDefaultConstraint::Value(Value::Int64(18))),
662 inverted_index: true,
663 },
664 JsonTypeHint {
665 path: vec!["user".to_string(), "name".to_string()],
666 data_type: ConcreteDataType::string_datatype(),
667 nullable: true,
668 default_constraint: None,
669 inverted_index: false,
670 },
671 ],
672 None,
673 )?;
674 assert_eq!(deserialized, expected);
675 Ok(())
676 }
677
678 #[test]
679 fn test_json_settings_ser_de() -> std::result::Result<(), Box<dyn std::error::Error>> {
680 let type_hints = vec![
681 JsonTypeHint {
682 path: vec!["user".to_string(), "age".to_string()],
683 data_type: ConcreteDataType::int64_datatype(),
684 nullable: false,
685 default_constraint: Some(ColumnDefaultConstraint::Value(Value::Int64(18))),
686 inverted_index: true,
687 },
688 JsonTypeHint {
689 path: vec!["user".to_string(), "name".to_string()],
690 data_type: ConcreteDataType::string_datatype(),
691 nullable: true,
692 default_constraint: None,
693 inverted_index: false,
694 },
695 ];
696
697 for settings in [
698 JsonSettings {
699 type_hints: type_hints.clone(),
700 max_auto_expanded_paths: None,
701 },
702 JsonSettings {
703 type_hints,
704 max_auto_expanded_paths: Some(1),
705 },
706 ] {
707 let serialized = serde_json::to_string(&settings)?;
708 let deserialized = serde_json::from_str::<JsonSettings>(&serialized)?;
709 assert_eq!(settings, deserialized);
710 }
711 Ok(())
712 }
713
714 #[test]
715 fn test_json_settings_reject_invalid_type_hint_layout() {
716 for type_hints in [
717 json!([{"path": [], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}]),
718 json!([
719 {"path": ["a"], "type": {"Int64": {}}, "nullable": true, "inverted_index": false},
720 {"path": ["a", "b"], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}
721 ]),
722 json!([{"path": [JSON2_REMAINDER_FIELD_NAME], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}]),
723 ] {
724 let settings = json!({"type_hints": type_hints});
725 assert!(serde_json::from_value::<JsonSettings>(settings).is_err());
726 }
727
728 let hint = |path, data_type| JsonTypeHint {
729 path,
730 data_type,
731 nullable: true,
732 default_constraint: None,
733 inverted_index: false,
734 };
735 assert!(
736 JsonSettings::try_new(
737 vec![hint(
738 vec!["nested".to_string(), JSON2_REMAINDER_FIELD_NAME.to_string()],
739 ConcreteDataType::string_datatype(),
740 )],
741 None,
742 )
743 .is_ok()
744 );
745 assert!(
746 JsonSettings::try_new(
747 vec![hint(
748 vec!["nested".to_string(); JSON2_MAX_STRUCTURED_DEPTH + 1],
749 ConcreteDataType::string_datatype(),
750 )],
751 None,
752 )
753 .is_err()
754 );
755 assert!(
756 JsonSettings::try_new(
757 vec![hint(
758 vec!["date".to_string()],
759 ConcreteDataType::date_datatype(),
760 )],
761 None,
762 )
763 .is_err()
764 );
765 }
766
767 #[test]
768 fn test_encode_root_non_object_json() {
769 let settings = JsonSettings::default();
770 let cases = [
771 ("null", Json::Null),
772 ("boolean", Json::Bool(true)),
773 ("integer", Json::from(42)),
774 ("float", Json::from(3.15)),
775 ("string", Json::String("hello".to_string())),
776 ("array", json!([1, 2, 3])),
777 ("mixed array", json!([1, "hello", true, 3.15])),
778 ("empty array", json!([])),
779 ];
780
781 for (name, json) in cases {
782 let err = settings.encode(json).unwrap_err();
783 assert!(
784 matches!(err, crate::error::Error::UnsupportedJsonType { .. }),
785 "{name}: {err:?}"
786 );
787 }
788 }
789
790 #[test]
791 fn test_encode_rejects_reserved_remainder_field() -> Result<()> {
792 let settings = JsonSettings::default();
793 let err = settings
794 .encode(json!({"!__remainder__!": "user-value"}))
795 .unwrap_err();
796 assert!(
797 err.to_string()
798 .contains("root object cannot contain reserved field")
799 );
800
801 settings.encode(json!({"nested": {"!__remainder__!": "user-value"}}))?;
802 Ok(())
803 }
804
805 #[test]
806 fn test_encode_json_object() {
807 let json = json!({
808 "name": "John",
809 "age": 30,
810 "active": true
811 });
812
813 let settings = JsonSettings::default();
814 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
815 let Value::Struct(result) = result else {
816 panic!("Expected Struct value");
817 };
818 assert_eq!(result.items().len(), 3);
819
820 let items = result.items();
821 let struct_type = result.struct_type();
822
823 let fields = struct_type.fields();
825 let field_names: Vec<&str> = fields.iter().map(|f| f.name()).collect();
826 assert!(field_names.contains(&"name"));
827 assert!(field_names.contains(&"age"));
828 assert!(field_names.contains(&"active"));
829
830 for (i, field) in struct_type.fields().iter().enumerate() {
832 match field.name() {
833 "name" => {
834 assert_eq!(items[i], Value::String("John".into()));
835 assert_eq!(field.data_type(), &ConcreteDataType::string_datatype());
836 }
837 "age" => {
838 assert_eq!(items[i], Value::Int64(30));
839 assert_eq!(field.data_type(), &ConcreteDataType::int64_datatype());
840 }
841 "active" => {
842 assert_eq!(items[i], Value::Boolean(true));
843 assert_eq!(field.data_type(), &ConcreteDataType::boolean_datatype());
844 }
845 _ => panic!("Unexpected field: {}", field.name()),
846 }
847 }
848 }
849
850 #[test]
851 fn test_encode_json_nested_object() {
852 let json = json!({
853 "person": {
854 "name": "Alice",
855 "age": 25
856 },
857 "scores": [95, 87, 92]
858 });
859
860 let settings = JsonSettings::default();
861 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
862 let Value::Struct(result) = result else {
863 panic!("Expected Struct value");
864 };
865 assert_eq!(result.items().len(), 2);
866
867 let items = result.items();
868 let struct_type = result.struct_type();
869
870 let person_index = struct_type
872 .fields()
873 .iter()
874 .position(|f| f.name() == "person")
875 .unwrap();
876 if let Value::Struct(person_struct) = &items[person_index] {
877 assert_eq!(person_struct.items().len(), 2);
878 let fields = person_struct.struct_type().fields();
879 let person_fields: Vec<&str> = fields.iter().map(|f| f.name()).collect();
880 assert!(person_fields.contains(&"name"));
881 assert!(person_fields.contains(&"age"));
882 } else {
883 panic!("Expected Struct value for person field");
884 }
885
886 let scores_index = struct_type
888 .fields()
889 .iter()
890 .position(|f| f.name() == "scores")
891 .unwrap();
892 if let Value::List(scores_list) = &items[scores_index] {
893 assert_eq!(scores_list.items().len(), 3);
894 assert_eq!(scores_list.items()[0], Value::Int64(95));
895 assert_eq!(scores_list.items()[1], Value::Int64(87));
896 assert_eq!(scores_list.items()[2], Value::Int64(92));
897 } else {
898 panic!("Expected List value for scores field");
899 }
900 }
901
902 #[test]
903 fn test_encode_json_structured() {
904 let json = json!({
905 "name": "Bob",
906 "age": 35
907 });
908
909 let settings = JsonSettings::default();
910 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
911
912 if let Value::Struct(struct_value) = result {
913 assert_eq!(struct_value.items().len(), 2);
914 let fields = struct_value.struct_type().fields();
915 let field_names: Vec<&str> = fields.iter().map(|f| f.name()).collect();
916 assert!(field_names.contains(&"name"));
917 assert!(field_names.contains(&"age"));
918 } else {
919 panic!("Expected Struct value");
920 }
921 }
922
923 #[test]
924 fn test_encode_json_respects_type_hint() -> Result<()> {
925 let type_hints = vec![JsonTypeHint {
926 path: vec!["age".to_string()],
927 data_type: ConcreteDataType::int64_datatype(),
928 nullable: false,
929 default_constraint: None,
930 inverted_index: false,
931 }];
932 let settings = JsonSettings::try_new(type_hints, None)?;
933
934 let result = settings
935 .encode(json!({
936 "name": "Alice",
937 "age": 42
938 }))
939 .unwrap()
940 .into_json_inner()
941 .unwrap();
942
943 let Value::Struct(struct_value) = result else {
944 panic!("Expected Struct value");
945 };
946 assert_eq!(struct_field_value(&struct_value, "age"), &Value::Int64(42));
947
948 let err = settings
949 .encode(json!({
950 "age": "42"
951 }))
952 .unwrap_err();
953 assert!(err.to_string().contains("does not match JSON2 type hint"));
954 Ok(())
955 }
956
957 #[test]
958 fn test_encode_json_respects_unsigned_type_hint() -> Result<()> {
959 let type_hints = vec![JsonTypeHint {
960 path: vec!["count".to_string()],
961 data_type: ConcreteDataType::uint64_datatype(),
962 nullable: false,
963 default_constraint: None,
964 inverted_index: false,
965 }];
966 let settings = JsonSettings::try_new(type_hints, None)?;
967
968 let result = settings
969 .encode(json!({
970 "count": u64::MAX
971 }))
972 .unwrap()
973 .into_json_inner()
974 .unwrap();
975
976 let Value::Struct(struct_value) = result else {
977 panic!("Expected Struct value");
978 };
979 assert_eq!(
980 struct_field_value(&struct_value, "count"),
981 &Value::UInt64(u64::MAX)
982 );
983
984 let err = settings
985 .encode(json!({
986 "count": -1
987 }))
988 .unwrap_err();
989 assert!(err.to_string().contains("does not match JSON2 type hint"));
990 Ok(())
991 }
992
993 #[test]
994 fn test_encode_json_fills_missing_type_hint_with_default() -> Result<()> {
995 let type_hints = vec![JsonTypeHint {
996 path: vec!["user".to_string(), "age".to_string()],
997 data_type: ConcreteDataType::int64_datatype(),
998 nullable: false,
999 default_constraint: Some(ColumnDefaultConstraint::Value(Value::Int64(7))),
1000 inverted_index: false,
1001 }];
1002 let settings = JsonSettings::try_new(type_hints, None)?;
1003
1004 let result = settings
1005 .encode(json!({}))
1006 .unwrap()
1007 .into_json_inner()
1008 .unwrap();
1009
1010 let Value::Struct(root) = result else {
1011 panic!("Expected Struct value");
1012 };
1013 let Value::Struct(user) = struct_field_value(&root, "user") else {
1014 panic!("Expected user Struct value");
1015 };
1016 assert_eq!(struct_field_value(user, "age"), &Value::Int64(7));
1017 Ok(())
1018 }
1019
1020 #[test]
1021 fn test_encode_json_fills_missing_nullable_type_hint_with_null() -> Result<()> {
1022 let type_hints = vec![JsonTypeHint {
1023 path: vec!["user".to_string(), "name".to_string()],
1024 data_type: ConcreteDataType::string_datatype(),
1025 nullable: true,
1026 default_constraint: None,
1027 inverted_index: false,
1028 }];
1029 let settings = JsonSettings::try_new(type_hints, None)?;
1030
1031 let result = settings
1032 .encode(json!({ "user": {} }))
1033 .unwrap()
1034 .into_json_inner()
1035 .unwrap();
1036
1037 let Value::Struct(root) = result else {
1038 panic!("Expected Struct value");
1039 };
1040 let Value::Struct(user) = struct_field_value(&root, "user") else {
1041 panic!("Expected user Struct value");
1042 };
1043 assert_eq!(struct_field_value(user, "name"), &Value::Null);
1044 Ok(())
1045 }
1046
1047 #[test]
1048 fn test_encode_json_rejects_missing_non_null_type_hint() -> Result<()> {
1049 let type_hints = vec![JsonTypeHint {
1050 path: vec!["user".to_string(), "age".to_string()],
1051 data_type: ConcreteDataType::int64_datatype(),
1052 nullable: false,
1053 default_constraint: None,
1054 inverted_index: false,
1055 }];
1056 let settings = JsonSettings::try_new(type_hints, None)?;
1057
1058 let err = settings.encode(json!({})).unwrap_err();
1059 assert!(
1060 err.to_string()
1061 .contains("missing non-null JSON2 type hint path user.age")
1062 );
1063 Ok(())
1064 }
1065
1066 #[test]
1067 fn test_encode_json_merges_missing_type_hint_prefix() -> Result<()> {
1068 let type_hints = vec![
1069 JsonTypeHint {
1070 path: vec!["user".to_string(), "age".to_string()],
1071 data_type: ConcreteDataType::int64_datatype(),
1072 nullable: false,
1073 default_constraint: Some(ColumnDefaultConstraint::Value(Value::Int64(7))),
1074 inverted_index: false,
1075 },
1076 JsonTypeHint {
1077 path: vec!["user".to_string(), "name".to_string()],
1078 data_type: ConcreteDataType::string_datatype(),
1079 nullable: false,
1080 default_constraint: Some(ColumnDefaultConstraint::Value(Value::String(
1081 "unknown".into(),
1082 ))),
1083 inverted_index: false,
1084 },
1085 ];
1086 let settings = JsonSettings::try_new(type_hints, None)?;
1087
1088 let result = settings.encode(json!({}))?.into_json_inner().unwrap();
1089
1090 let Value::Struct(root) = result else {
1091 panic!("Expected Struct value");
1092 };
1093 let Value::Struct(user) = struct_field_value(&root, "user") else {
1094 panic!("Expected user Struct value");
1095 };
1096 assert_eq!(struct_field_value(user, "age"), &Value::Int64(7));
1097 assert_eq!(
1098 struct_field_value(user, "name"),
1099 &Value::String("unknown".into())
1100 );
1101 Ok(())
1102 }
1103
1104 #[test]
1105 fn test_json_settings_structured() {
1106 let json = json!({
1107 "name": "Eve",
1108 "score": 95
1109 });
1110
1111 let settings = JsonSettings::default();
1112 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
1113
1114 if let Value::Struct(struct_value) = result {
1115 assert_eq!(struct_value.items().len(), 2);
1116 } else {
1117 panic!("Expected Struct value");
1118 }
1119 }
1120
1121 #[cfg(test)]
1122 mod decode_tests {
1123 use ordered_float::OrderedFloat;
1124 use serde_json::json;
1125
1126 use super::*;
1127
1128 #[test]
1129 fn test_decode_primitive_values() {
1130 let settings = JsonSettings::default();
1131
1132 let result = settings.decode(Value::Null).unwrap();
1134 assert_eq!(result, Json::Null);
1135
1136 let result = settings.decode(Value::Boolean(true)).unwrap();
1138 assert_eq!(result, Json::Bool(true));
1139
1140 let result = settings.decode(Value::Int64(42)).unwrap();
1142 assert_eq!(result, Json::from(42));
1143
1144 let result = settings.decode(Value::Float64(OrderedFloat(3.16))).unwrap();
1146 assert_eq!(result, Json::from(3.16));
1147
1148 let result = settings.decode(Value::String("hello".into())).unwrap();
1150 assert_eq!(result, Json::String("hello".to_string()));
1151 }
1152
1153 #[test]
1154 fn test_decode_struct() {
1155 let settings = JsonSettings::default();
1156
1157 let struct_value = StructValue::new(
1158 vec![
1159 Value::String("Alice".into()),
1160 Value::Int64(25),
1161 Value::Boolean(true),
1162 ],
1163 StructType::new(Arc::new(vec![
1164 StructField::new(
1165 "name".to_string(),
1166 ConcreteDataType::string_datatype(),
1167 true,
1168 ),
1169 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1170 StructField::new(
1171 "active".to_string(),
1172 ConcreteDataType::boolean_datatype(),
1173 true,
1174 ),
1175 ])),
1176 );
1177
1178 let result = settings.decode(Value::Struct(struct_value)).unwrap();
1179 let expected = json!({
1180 "name": "Alice",
1181 "age": 25,
1182 "active": true
1183 });
1184 assert_eq!(result, expected);
1185 }
1186
1187 #[test]
1188 fn test_decode_list() {
1189 let settings = JsonSettings::default();
1190
1191 let list_value = ListValue::new(
1192 vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)],
1193 Arc::new(ConcreteDataType::int64_datatype()),
1194 );
1195
1196 let result = settings.decode(Value::List(list_value)).unwrap();
1197 let expected = json!([1, 2, 3]);
1198 assert_eq!(result, expected);
1199 }
1200
1201 #[test]
1202 fn test_decode_nested_structure() {
1203 let settings = JsonSettings::default();
1204
1205 let inner_struct = StructValue::new(
1206 vec![Value::String("Alice".into()), Value::Int64(25)],
1207 StructType::new(Arc::new(vec![
1208 StructField::new(
1209 "name".to_string(),
1210 ConcreteDataType::string_datatype(),
1211 true,
1212 ),
1213 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1214 ])),
1215 );
1216
1217 let score_list_item_type = Arc::new(ConcreteDataType::int64_datatype());
1218 let outer_struct = StructValue::new(
1219 vec![
1220 Value::Struct(inner_struct),
1221 Value::List(ListValue::new(
1222 vec![Value::Int64(95), Value::Int64(87)],
1223 score_list_item_type.clone(),
1224 )),
1225 ],
1226 StructType::new(Arc::new(vec![
1227 StructField::new(
1228 "user".to_string(),
1229 ConcreteDataType::Struct(StructType::new(Arc::new(vec![
1230 StructField::new(
1231 "name".to_string(),
1232 ConcreteDataType::string_datatype(),
1233 true,
1234 ),
1235 StructField::new(
1236 "age".to_string(),
1237 ConcreteDataType::int64_datatype(),
1238 true,
1239 ),
1240 ]))),
1241 true,
1242 ),
1243 StructField::new(
1244 "scores".to_string(),
1245 ConcreteDataType::List(ListType::new(score_list_item_type.clone())),
1246 true,
1247 ),
1248 ])),
1249 );
1250
1251 let result = settings.decode(Value::Struct(outer_struct)).unwrap();
1252 let expected = json!({
1253 "user": {
1254 "name": "Alice",
1255 "age": 25
1256 },
1257 "scores": [95, 87]
1258 });
1259 assert_eq!(result, expected);
1260 }
1261
1262 #[test]
1263 fn test_decode_missing_fields() {
1264 let settings = JsonSettings::default();
1265
1266 let struct_value = StructValue::new(
1268 vec![
1269 Value::String("Bob".into()),
1270 Value::Null, ],
1272 StructType::new(Arc::new(vec![
1273 StructField::new(
1274 "name".to_string(),
1275 ConcreteDataType::string_datatype(),
1276 true,
1277 ),
1278 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1279 ])),
1280 );
1281
1282 let result = settings.decode(Value::Struct(struct_value)).unwrap();
1283 let expected = json!({
1284 "name": "Bob",
1285 "age": null
1286 });
1287 assert_eq!(result, expected);
1288 }
1289 }
1290}