1use crate::DEFAULT_SCHEMA;
21use hedl_core::convert::parse_reference;
22use hedl_core::{Document, Item, MatrixList, Node, Value};
23use hedl_core::lex::{parse_expression_token, singularize_and_capitalize};
24use hedl_core::lex::Tensor;
25use serde_json::{Map, Value as JsonValue};
26use std::collections::{BTreeMap, HashMap};
27
28pub const DEFAULT_MAX_DEPTH: usize = 10_000;
34
35pub const DEFAULT_MAX_ARRAY_SIZE: usize = 10_000_000;
40
41pub const DEFAULT_MAX_STRING_LENGTH: usize = 100 * 1024 * 1024;
46
47pub const DEFAULT_MAX_OBJECT_SIZE: usize = 100_000;
52
53#[derive(Debug, Clone, thiserror::Error)]
55pub enum JsonConversionError {
56 #[error("JSON parse error: {0}")]
58 ParseError(String),
59
60 #[error("Root must be a JSON object, found {0}")]
62 InvalidRoot(String),
63
64 #[error("Invalid number: {0}")]
66 InvalidNumber(String),
67
68 #[error("Invalid expression: {0}")]
70 InvalidExpression(String),
71
72 #[error("Invalid tensor element - must be number or array")]
74 InvalidTensor,
75
76 #[error("Nested objects not allowed in scalar context")]
78 NestedObject,
79
80 #[error("Invalid reference: {0}")]
82 InvalidReference(String),
83
84 #[error("Maximum recursion depth ({0}) exceeded - possible deeply nested structure")]
86 MaxDepthExceeded(usize),
87
88 #[error("Maximum array size ({0}) exceeded - array has {1} elements")]
90 MaxArraySizeExceeded(usize, usize),
91
92 #[error("Maximum string length ({0}) exceeded - string has {1} characters")]
94 MaxStringLengthExceeded(usize, usize),
95
96 #[error("Maximum object size ({0}) exceeded - object has {1} keys")]
98 MaxObjectSizeExceeded(usize, usize),
99}
100
101impl From<serde_json::Error> for JsonConversionError {
102 fn from(err: serde_json::Error) -> Self {
103 JsonConversionError::ParseError(err.to_string())
104 }
105}
106
107#[derive(Debug, Clone)]
154pub struct FromJsonConfig {
155 pub default_type_name: String,
157
158 pub version: (u32, u32),
160
161 pub max_depth: Option<usize>,
166
167 pub max_array_size: Option<usize>,
173
174 pub max_string_length: Option<usize>,
181
182 pub max_object_size: Option<usize>,
188}
189
190impl Default for FromJsonConfig {
191 fn default() -> Self {
192 Self {
193 default_type_name: "Item".to_string(),
194 version: (1, 0),
195 max_depth: Some(DEFAULT_MAX_DEPTH),
196 max_array_size: Some(DEFAULT_MAX_ARRAY_SIZE),
197 max_string_length: Some(DEFAULT_MAX_STRING_LENGTH),
198 max_object_size: Some(DEFAULT_MAX_OBJECT_SIZE),
199 }
200 }
201}
202
203
204impl FromJsonConfig {
205 pub fn builder() -> FromJsonConfigBuilder {
218 FromJsonConfigBuilder::default()
219 }
220}
221
222impl hedl_core::convert::ImportConfig for FromJsonConfig {
223 fn default_type_name(&self) -> &str {
224 &self.default_type_name
225 }
226
227 fn version(&self) -> (u32, u32) {
228 self.version
229 }
230}
231
232#[derive(Debug, Clone)]
262pub struct FromJsonConfigBuilder {
263 default_type_name: String,
264 version: (u32, u32),
265 max_depth: Option<usize>,
266 max_array_size: Option<usize>,
267 max_string_length: Option<usize>,
268 max_object_size: Option<usize>,
269}
270
271impl Default for FromJsonConfigBuilder {
272 fn default() -> Self {
273 Self {
274 default_type_name: "Item".to_string(),
275 version: (1, 0),
276 max_depth: Some(DEFAULT_MAX_DEPTH),
277 max_array_size: Some(DEFAULT_MAX_ARRAY_SIZE),
278 max_string_length: Some(DEFAULT_MAX_STRING_LENGTH),
279 max_object_size: Some(DEFAULT_MAX_OBJECT_SIZE),
280 }
281 }
282}
283
284impl FromJsonConfigBuilder {
285 pub fn default_type_name(mut self, name: impl Into<String>) -> Self {
287 self.default_type_name = name.into();
288 self
289 }
290
291 pub fn version(mut self, major: u32, minor: u32) -> Self {
293 self.version = (major, minor);
294 self
295 }
296
297 pub fn max_depth(mut self, limit: usize) -> Self {
301 self.max_depth = Some(limit);
302 self
303 }
304
305 pub fn max_array_size(mut self, limit: usize) -> Self {
309 self.max_array_size = Some(limit);
310 self
311 }
312
313 pub fn max_string_length(mut self, limit: usize) -> Self {
317 self.max_string_length = Some(limit);
318 self
319 }
320
321 pub fn max_object_size(mut self, limit: usize) -> Self {
325 self.max_object_size = Some(limit);
326 self
327 }
328
329 pub fn unlimited(mut self) -> Self {
334 self.max_depth = None;
335 self.max_array_size = None;
336 self.max_string_length = None;
337 self.max_object_size = None;
338 self
339 }
340
341 pub fn build(self) -> FromJsonConfig {
343 FromJsonConfig {
344 default_type_name: self.default_type_name,
345 version: self.version,
346 max_depth: self.max_depth,
347 max_array_size: self.max_array_size,
348 max_string_length: self.max_string_length,
349 max_object_size: self.max_object_size,
350 }
351 }
352}
353
354type SchemaCache = HashMap<Vec<String>, Vec<String>>;
366
367pub fn from_json(json: &str, config: &FromJsonConfig) -> Result<Document, JsonConversionError> {
389 let value: JsonValue = serde_json::from_str(json)?;
390 from_json_value(&value, config)
391}
392
393pub fn from_json_value(
416 value: &JsonValue,
417 config: &FromJsonConfig,
418) -> Result<Document, JsonConversionError> {
419 let mut structs = BTreeMap::new();
420 let mut schema_cache = SchemaCache::new();
421 let root = match value {
422 JsonValue::Object(map) => json_object_to_root(map, config, &mut structs, &mut schema_cache, 0)?,
423 _ => {
424 return Err(JsonConversionError::InvalidRoot(format!(
425 "{:?}",
426 value
427 )))
428 }
429 };
430
431 Ok(Document {
432 version: config.version,
433 aliases: BTreeMap::new(),
434 structs,
435 nests: BTreeMap::new(),
436 root,
437 })
438}
439
440pub fn from_json_value_owned(
472 value: JsonValue,
473 config: &FromJsonConfig,
474) -> Result<Document, JsonConversionError> {
475 let mut structs = BTreeMap::new();
476 let mut schema_cache = SchemaCache::new();
477 let root = match value {
478 JsonValue::Object(map) => json_object_to_root_owned(map, config, &mut structs, &mut schema_cache, 0)?,
479 _ => {
480 return Err(JsonConversionError::InvalidRoot(
481 "Root must be an object".to_string()
482 ))
483 }
484 };
485
486 Ok(Document {
487 version: config.version,
488 aliases: BTreeMap::new(),
489 structs,
490 nests: BTreeMap::new(),
491 root,
492 })
493}
494
495
496fn process_json_object_inner(
504 map: &Map<String, JsonValue>,
505 config: &FromJsonConfig,
506 structs: &mut BTreeMap<String, Vec<String>>,
507 schema_cache: &mut SchemaCache,
508 depth: usize,
509) -> Result<BTreeMap<String, Item>, JsonConversionError> {
510 if let Some(max_size) = config.max_object_size {
512 if map.len() > max_size {
513 return Err(JsonConversionError::MaxObjectSizeExceeded(
514 max_size,
515 map.len(),
516 ));
517 }
518 }
519
520 let mut result = BTreeMap::new();
524
525 for (key, value) in map {
526 if key.starts_with("__") {
528 continue;
529 }
530
531 let item = json_value_to_item(value, key, config, structs, schema_cache, depth)?;
532 result.insert(key.clone(), item);
533 }
534
535 Ok(result)
536}
537
538fn json_object_to_root(
539 map: &Map<String, JsonValue>,
540 config: &FromJsonConfig,
541 structs: &mut BTreeMap<String, Vec<String>>,
542 schema_cache: &mut SchemaCache,
543 depth: usize,
544) -> Result<BTreeMap<String, Item>, JsonConversionError> {
545 process_json_object_inner(map, config, structs, schema_cache, depth)
546}
547
548fn json_object_to_root_owned(
550 map: Map<String, JsonValue>,
551 config: &FromJsonConfig,
552 structs: &mut BTreeMap<String, Vec<String>>,
553 schema_cache: &mut SchemaCache,
554 depth: usize,
555) -> Result<BTreeMap<String, Item>, JsonConversionError> {
556 if let Some(max_size) = config.max_object_size {
558 if map.len() > max_size {
559 return Err(JsonConversionError::MaxObjectSizeExceeded(
560 max_size,
561 map.len(),
562 ));
563 }
564 }
565
566 let mut result = BTreeMap::new();
567
568 for (key, value) in map {
569 if key.starts_with("__") {
571 continue;
572 }
573
574 let item = json_value_to_item_owned(value, &key, config, structs, schema_cache, depth)?;
575 result.insert(key, item);
576 }
577
578 Ok(result)
579}
580
581fn json_object_to_item_map(
582 map: &Map<String, JsonValue>,
583 config: &FromJsonConfig,
584 structs: &mut BTreeMap<String, Vec<String>>,
585 schema_cache: &mut SchemaCache,
586 depth: usize,
587) -> Result<BTreeMap<String, Item>, JsonConversionError> {
588 process_json_object_inner(map, config, structs, schema_cache, depth)
589}
590
591fn json_value_to_item(
592 value: &JsonValue,
593 key: &str,
594 config: &FromJsonConfig,
595 structs: &mut BTreeMap<String, Vec<String>>,
596 schema_cache: &mut SchemaCache,
597 depth: usize,
598) -> Result<Item, JsonConversionError> {
599 if let Some(max_depth) = config.max_depth {
601 if depth >= max_depth {
602 return Err(JsonConversionError::MaxDepthExceeded(max_depth));
603 }
604 }
605
606 match value {
607 JsonValue::Null => Ok(Item::Scalar(Value::Null)),
608 JsonValue::Bool(b) => Ok(Item::Scalar(Value::Bool(*b))),
609 JsonValue::Number(n) => {
610 if let Some(i) = n.as_i64() {
611 Ok(Item::Scalar(Value::Int(i)))
612 } else if let Some(f) = n.as_f64() {
613 Ok(Item::Scalar(Value::Float(f)))
614 } else {
615 Err(JsonConversionError::InvalidNumber(n.to_string()))
616 }
617 }
618 JsonValue::String(s) => {
619 if let Some(max_len) = config.max_string_length {
621 if s.len() > max_len {
622 return Err(JsonConversionError::MaxStringLengthExceeded(
623 max_len,
624 s.len(),
625 ));
626 }
627 }
628
629 if s.starts_with("$(") && s.ends_with(')') {
631 let expr = parse_expression_token(s)
632 .map_err(|e| JsonConversionError::InvalidExpression(e.to_string()))?;
633 Ok(Item::Scalar(Value::Expression(expr)))
634 } else {
635 Ok(Item::Scalar(Value::String(s.clone())))
640 }
641 }
642 JsonValue::Array(arr) => {
643 if let Some(max_size) = config.max_array_size {
645 if arr.len() > max_size {
646 return Err(JsonConversionError::MaxArraySizeExceeded(
647 max_size,
648 arr.len(),
649 ));
650 }
651 }
652
653 if arr.is_empty() {
655 let type_name = singularize_and_capitalize(key);
656 let schema: Vec<String> = DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect();
657 let mut list = MatrixList::new(type_name.clone(), schema.clone());
658 list.count_hint = Some(0);
659 structs.insert(type_name, schema);
660 Ok(Item::List(list))
661 } else if is_tensor_array(arr) {
662 let tensor = json_array_to_tensor(arr, config, depth + 1)?;
664 Ok(Item::Scalar(Value::Tensor(tensor)))
665 } else if is_object_array(arr) {
666 let list = json_array_to_matrix_list(arr, key, config, structs, schema_cache, depth + 1)?;
668 Ok(Item::List(list))
669 } else {
670 let tensor = json_array_to_tensor(arr, config, depth + 1)?;
672 Ok(Item::Scalar(Value::Tensor(tensor)))
673 }
674 }
675 JsonValue::Object(obj) => {
676 if let Some(JsonValue::String(r)) = obj.get("@ref") {
678 return Ok(Item::Scalar(Value::Reference(
679 parse_reference(r).map_err(JsonConversionError::InvalidReference)?,
680 )));
681 }
682 let item_map = json_object_to_item_map(obj, config, structs, schema_cache, depth + 1)?;
684 Ok(Item::Object(item_map))
685 }
686 }
687}
688
689fn json_value_to_item_owned(
691 value: JsonValue,
692 key: &str,
693 config: &FromJsonConfig,
694 structs: &mut BTreeMap<String, Vec<String>>,
695 schema_cache: &mut SchemaCache,
696 depth: usize,
697) -> Result<Item, JsonConversionError> {
698 if let Some(max_depth) = config.max_depth {
700 if depth >= max_depth {
701 return Err(JsonConversionError::MaxDepthExceeded(max_depth));
702 }
703 }
704
705 match value {
706 JsonValue::Null => Ok(Item::Scalar(Value::Null)),
707 JsonValue::Bool(b) => Ok(Item::Scalar(Value::Bool(b))),
708 JsonValue::Number(n) => {
709 if let Some(i) = n.as_i64() {
710 Ok(Item::Scalar(Value::Int(i)))
711 } else if let Some(f) = n.as_f64() {
712 Ok(Item::Scalar(Value::Float(f)))
713 } else {
714 Err(JsonConversionError::InvalidNumber(n.to_string()))
715 }
716 }
717 JsonValue::String(s) => {
718 if let Some(max_len) = config.max_string_length {
720 if s.len() > max_len {
721 return Err(JsonConversionError::MaxStringLengthExceeded(
722 max_len,
723 s.len(),
724 ));
725 }
726 }
727
728 if s.starts_with("$(") && s.ends_with(')') {
730 let expr = parse_expression_token(&s)
731 .map_err(|e| JsonConversionError::InvalidExpression(e.to_string()))?;
732 Ok(Item::Scalar(Value::Expression(expr)))
733 } else {
734 Ok(Item::Scalar(Value::String(s)))
736 }
737 }
738 JsonValue::Array(arr) => {
739 if let Some(max_size) = config.max_array_size {
741 if arr.len() > max_size {
742 return Err(JsonConversionError::MaxArraySizeExceeded(
743 max_size,
744 arr.len(),
745 ));
746 }
747 }
748
749 if arr.is_empty() {
751 let type_name = singularize_and_capitalize(key);
752 let schema: Vec<String> = DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect();
753 let mut list = MatrixList::new(type_name.clone(), schema.clone());
754 list.count_hint = Some(0);
755 structs.insert(type_name, schema);
756 Ok(Item::List(list))
757 } else if is_tensor_array(&arr) {
758 let tensor = json_array_to_tensor_owned(arr, config, depth + 1)?;
760 Ok(Item::Scalar(Value::Tensor(tensor)))
761 } else if is_object_array(&arr) {
762 let list = json_array_to_matrix_list(&arr, key, config, structs, schema_cache, depth + 1)?;
764 Ok(Item::List(list))
765 } else {
766 let tensor = json_array_to_tensor_owned(arr, config, depth + 1)?;
768 Ok(Item::Scalar(Value::Tensor(tensor)))
769 }
770 }
771 JsonValue::Object(obj) => {
772 if let Some(JsonValue::String(r)) = obj.get("@ref") {
774 return Ok(Item::Scalar(Value::Reference(
775 parse_reference(r).map_err(JsonConversionError::InvalidReference)?,
776 )));
777 }
778 let item_map = json_object_to_item_map(&obj, config, structs, schema_cache, depth + 1)?;
780 Ok(Item::Object(item_map))
781 }
782 }
783}
784
785fn is_tensor_array(arr: &[JsonValue]) -> bool {
786 !arr.is_empty()
788 && arr
789 .iter()
790 .all(|v| matches!(v, JsonValue::Number(_) | JsonValue::Array(_)))
791}
792
793fn is_object_array(arr: &[JsonValue]) -> bool {
794 !arr.is_empty() && arr.iter().all(|v| matches!(v, JsonValue::Object(_)))
795}
796
797fn json_array_to_tensor(
798 arr: &[JsonValue],
799 config: &FromJsonConfig,
800 depth: usize,
801) -> Result<Tensor, JsonConversionError> {
802 if let Some(max_depth) = config.max_depth {
804 if depth >= max_depth {
805 return Err(JsonConversionError::MaxDepthExceeded(max_depth));
806 }
807 }
808
809 let mut items = Vec::with_capacity(arr.len());
812
813 for v in arr.iter() {
814 let tensor = match v {
815 JsonValue::Number(n) => n
816 .as_f64()
817 .map(Tensor::Scalar)
818 .ok_or_else(|| JsonConversionError::InvalidNumber(n.to_string()))?,
819 JsonValue::Array(nested) => json_array_to_tensor(nested, config, depth + 1)?,
820 _ => return Err(JsonConversionError::InvalidTensor),
821 };
822 items.push(tensor);
823 }
824
825 Ok(Tensor::Array(items))
826}
827
828fn json_array_to_tensor_owned(
830 arr: Vec<JsonValue>,
831 config: &FromJsonConfig,
832 depth: usize,
833) -> Result<Tensor, JsonConversionError> {
834 if let Some(max_depth) = config.max_depth {
836 if depth >= max_depth {
837 return Err(JsonConversionError::MaxDepthExceeded(max_depth));
838 }
839 }
840
841 let mut items = Vec::with_capacity(arr.len());
844
845 for v in arr.into_iter() {
846 let tensor = match v {
847 JsonValue::Number(n) => n
848 .as_f64()
849 .map(Tensor::Scalar)
850 .ok_or_else(|| JsonConversionError::InvalidNumber(n.to_string()))?,
851 JsonValue::Array(nested) => json_array_to_tensor_owned(nested, config, depth + 1)?,
852 _ => return Err(JsonConversionError::InvalidTensor),
853 };
854 items.push(tensor);
855 }
856
857 Ok(Tensor::Array(items))
858}
859
860#[allow(clippy::only_used_in_recursion)]
861fn json_array_to_matrix_list(
862 arr: &[JsonValue],
863 key: &str,
864 config: &FromJsonConfig,
865 structs: &mut BTreeMap<String, Vec<String>>,
866 schema_cache: &mut SchemaCache,
867 depth: usize,
868) -> Result<MatrixList, JsonConversionError> {
869 if let Some(max_depth) = config.max_depth {
871 if depth >= max_depth {
872 return Err(JsonConversionError::MaxDepthExceeded(max_depth));
873 }
874 }
875 let type_name = singularize_and_capitalize(key);
877
878 let schema: Vec<String> = if let Some(JsonValue::Object(first)) = arr.first() {
880 let inferred = if let Some(JsonValue::Array(schema_arr)) = first.get("__hedl_schema") {
882 schema_arr
883 .iter()
884 .filter_map(|v| v.as_str().map(String::from))
885 .collect()
886 } else {
887 let mut cache_key: Vec<String> = first
889 .keys()
890 .filter(|k| {
891 if k.starts_with("__") {
892 return false;
893 }
894 if let Some(JsonValue::Array(arr)) = first.get(*k) {
896 !is_object_array(arr)
897 } else {
898 true
899 }
900 })
901 .cloned()
902 .collect();
903 cache_key.sort();
904
905 if let Some(cached_schema) = schema_cache.get(&cache_key) {
907 cached_schema.clone()
908 } else {
909 let mut keys = cache_key.clone();
911
912 if let Some(pos) = keys.iter().position(|k| k == "id") {
914 keys.remove(pos);
915 keys.insert(0, "id".to_string());
916 }
917
918 schema_cache.insert(cache_key, keys.clone());
920 keys
921 }
922 };
923 if inferred.is_empty() {
925 DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect()
926 } else {
927 inferred
928 }
929 } else {
930 DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect()
931 };
932
933 structs.insert(type_name.clone(), schema.clone());
935
936 let mut rows = Vec::with_capacity(arr.len());
939
940 for item in arr.iter() {
941 if let JsonValue::Object(obj) = item {
942 let id = obj
944 .get(&schema[0])
945 .and_then(|v| v.as_str())
946 .unwrap_or("")
947 .to_string();
948
949 let mut fields = Vec::with_capacity(schema.len());
952 for col in &schema {
953 let value = obj
954 .get(col)
955 .map(|v| json_to_value(v, config))
956 .transpose()?
957 .unwrap_or(Value::Null);
958 fields.push(value);
959 }
960
961 let mut children: BTreeMap<String, Vec<Node>> = BTreeMap::new();
963 for (child_key, child_value) in obj.iter() {
964 if let JsonValue::Array(child_arr) = child_value {
965 if is_object_array(child_arr) {
966 let child_list = json_array_to_matrix_list(
968 child_arr,
969 child_key,
970 config,
971 structs,
972 schema_cache,
973 depth + 1,
974 )?;
975 children.insert(child_key.clone(), child_list.rows);
976 }
977 }
978 }
979
980 let node = Node {
981 type_name: type_name.clone(),
982 id,
983 fields,
984 children,
985 child_count: None,
986 };
987
988 rows.push(node);
989 }
990 }
991
992 let count_hint = Some(arr.len());
994
995 Ok(MatrixList {
996 type_name,
997 schema,
998 rows,
999 count_hint,
1000 })
1001}
1002
1003fn json_to_value(value: &JsonValue, config: &FromJsonConfig) -> Result<Value, JsonConversionError> {
1004 Ok(match value {
1005 JsonValue::Null => Value::Null,
1006 JsonValue::Bool(b) => Value::Bool(*b),
1007 JsonValue::Number(n) => {
1008 if let Some(i) = n.as_i64() {
1009 Value::Int(i)
1010 } else if let Some(f) = n.as_f64() {
1011 Value::Float(f)
1012 } else {
1013 return Err(JsonConversionError::InvalidNumber(n.to_string()));
1014 }
1015 }
1016 JsonValue::String(s) => {
1017 if let Some(max_len) = config.max_string_length {
1019 if s.len() > max_len {
1020 return Err(JsonConversionError::MaxStringLengthExceeded(
1021 max_len,
1022 s.len(),
1023 ));
1024 }
1025 }
1026
1027 if s.starts_with("$(") && s.ends_with(')') {
1029 let expr = parse_expression_token(s)
1030 .map_err(|e| JsonConversionError::InvalidExpression(e.to_string()))?;
1031 Value::Expression(expr)
1032 } else {
1033 Value::String(s.clone())
1034 }
1035 }
1036 JsonValue::Array(arr) => {
1037 if let Some(max_size) = config.max_array_size {
1039 if arr.len() > max_size {
1040 return Err(JsonConversionError::MaxArraySizeExceeded(
1041 max_size,
1042 arr.len(),
1043 ));
1044 }
1045 }
1046
1047 if is_object_array(arr) {
1050 Value::Null } else if is_tensor_array(arr) {
1052 let tensor = json_array_to_tensor(arr, config, 0)?;
1053 Value::Tensor(tensor)
1054 } else if arr.is_empty() {
1055 Value::Tensor(Tensor::Array(vec![]))
1057 } else {
1058 let tensor = json_array_to_tensor(arr, config, 0)?;
1060 Value::Tensor(tensor)
1061 }
1062 }
1063 JsonValue::Object(obj) => {
1064 if let Some(JsonValue::String(r)) = obj.get("@ref") {
1065 Value::Reference(
1066 parse_reference(r).map_err(JsonConversionError::InvalidReference)?,
1067 )
1068 } else {
1069 return Err(JsonConversionError::NestedObject);
1070 }
1071 }
1072 })
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078 use serde_json::json;
1079
1080 #[test]
1083 fn test_from_json_config_default() {
1084 let config = FromJsonConfig::default();
1085 assert_eq!(config.default_type_name, "Item");
1086 assert_eq!(config.version, (1, 0));
1087 assert_eq!(config.max_depth, Some(DEFAULT_MAX_DEPTH));
1088 assert_eq!(config.max_array_size, Some(DEFAULT_MAX_ARRAY_SIZE));
1089 assert_eq!(config.max_string_length, Some(DEFAULT_MAX_STRING_LENGTH));
1090 assert_eq!(config.max_object_size, Some(DEFAULT_MAX_OBJECT_SIZE));
1091 assert_eq!(config.max_depth, Some(10_000));
1093 assert_eq!(config.max_array_size, Some(10_000_000));
1094 assert_eq!(config.max_string_length, Some(100 * 1024 * 1024));
1095 assert_eq!(config.max_object_size, Some(100_000));
1096 }
1097
1098 #[test]
1099 fn test_from_json_config_debug() {
1100 let config = FromJsonConfig::default();
1101 let debug = format!("{:?}", config);
1102 assert!(debug.contains("FromJsonConfig"));
1103 assert!(debug.contains("default_type_name"));
1104 assert!(debug.contains("version"));
1105 }
1106
1107 #[test]
1108 fn test_from_json_config_clone() {
1109 let config = FromJsonConfig {
1110 default_type_name: "Custom".to_string(),
1111 version: (2, 1),
1112 max_depth: Some(50),
1113 max_array_size: Some(10_000),
1114 max_string_length: Some(1_000_000),
1115 max_object_size: Some(1_000),
1116 };
1117 let cloned = config.clone();
1118 assert_eq!(cloned.default_type_name, "Custom");
1119 assert_eq!(cloned.version, (2, 1));
1120 assert_eq!(cloned.max_depth, Some(50));
1121 }
1122
1123 #[test]
1126 fn test_builder_default() {
1127 let config = FromJsonConfig::builder().build();
1128 assert_eq!(config.default_type_name, "Item");
1129 assert_eq!(config.version, (1, 0));
1130 assert_eq!(config.max_depth, Some(DEFAULT_MAX_DEPTH));
1131 assert_eq!(config.max_array_size, Some(DEFAULT_MAX_ARRAY_SIZE));
1132 assert_eq!(config.max_string_length, Some(DEFAULT_MAX_STRING_LENGTH));
1133 assert_eq!(config.max_object_size, Some(DEFAULT_MAX_OBJECT_SIZE));
1134 }
1135
1136 #[test]
1137 fn test_builder_custom_limits() {
1138 let config = FromJsonConfig::builder()
1139 .max_depth(1_000)
1140 .max_array_size(100_000)
1141 .max_string_length(10 * 1024 * 1024)
1142 .max_object_size(5_000)
1143 .build();
1144
1145 assert_eq!(config.max_depth, Some(1_000));
1146 assert_eq!(config.max_array_size, Some(100_000));
1147 assert_eq!(config.max_string_length, Some(10 * 1024 * 1024));
1148 assert_eq!(config.max_object_size, Some(5_000));
1149 }
1150
1151 #[test]
1152 fn test_builder_unlimited() {
1153 let config = FromJsonConfig::builder()
1154 .unlimited()
1155 .build();
1156
1157 assert_eq!(config.max_depth, None);
1158 assert_eq!(config.max_array_size, None);
1159 assert_eq!(config.max_string_length, None);
1160 assert_eq!(config.max_object_size, None);
1161 }
1162
1163 #[test]
1164 fn test_builder_custom_type_and_version() {
1165 let config = FromJsonConfig::builder()
1166 .default_type_name("CustomType")
1167 .version(2, 1)
1168 .build();
1169
1170 assert_eq!(config.default_type_name, "CustomType");
1171 assert_eq!(config.version, (2, 1));
1172 }
1173
1174 #[test]
1175 fn test_builder_chaining() {
1176 let config = FromJsonConfig::builder()
1177 .default_type_name("Entity")
1178 .version(1, 5)
1179 .max_depth(500)
1180 .max_array_size(50_000)
1181 .max_string_length(5 * 1024 * 1024)
1182 .max_object_size(2_500)
1183 .build();
1184
1185 assert_eq!(config.default_type_name, "Entity");
1186 assert_eq!(config.version, (1, 5));
1187 assert_eq!(config.max_depth, Some(500));
1188 assert_eq!(config.max_array_size, Some(50_000));
1189 assert_eq!(config.max_string_length, Some(5 * 1024 * 1024));
1190 assert_eq!(config.max_object_size, Some(2_500));
1191 }
1192
1193 #[test]
1196 fn test_parse_reference_qualified() {
1197 let r = parse_reference("@User:123").unwrap();
1198 assert_eq!(r.type_name, Some("User".to_string()));
1199 assert_eq!(r.id, "123");
1200 }
1201
1202 #[test]
1203 fn test_parse_reference_local() {
1204 let r = parse_reference("@123").unwrap();
1205 assert_eq!(r.type_name, None);
1206 assert_eq!(r.id, "123");
1207 }
1208
1209 #[test]
1210 fn test_parse_reference_invalid() {
1211 let result = parse_reference("User:123");
1212 assert!(result.is_err());
1213 }
1214
1215 #[test]
1218 fn test_is_tensor_array_numbers() {
1219 let arr = vec![json!(1), json!(2), json!(3)];
1220 assert!(is_tensor_array(&arr));
1221 }
1222
1223 #[test]
1224 fn test_is_tensor_array_nested() {
1225 let arr = vec![json!([1, 2]), json!([3, 4])];
1226 assert!(is_tensor_array(&arr));
1227 }
1228
1229 #[test]
1230 fn test_is_tensor_array_empty() {
1231 let arr: Vec<JsonValue> = vec![];
1232 assert!(!is_tensor_array(&arr));
1233 }
1234
1235 #[test]
1236 fn test_is_tensor_array_with_strings() {
1237 let arr = vec![json!(1), json!("not a tensor")];
1238 assert!(!is_tensor_array(&arr));
1239 }
1240
1241 #[test]
1242 fn test_is_tensor_array_with_objects() {
1243 let arr = vec![json!({"id": 1})];
1244 assert!(!is_tensor_array(&arr));
1245 }
1246
1247 #[test]
1250 fn test_is_object_array_true() {
1251 let arr = vec![json!({"id": 1}), json!({"id": 2})];
1252 assert!(is_object_array(&arr));
1253 }
1254
1255 #[test]
1256 fn test_is_object_array_empty() {
1257 let arr: Vec<JsonValue> = vec![];
1258 assert!(!is_object_array(&arr));
1259 }
1260
1261 #[test]
1262 fn test_is_object_array_mixed() {
1263 let arr = vec![json!({"id": 1}), json!(123)];
1264 assert!(!is_object_array(&arr));
1265 }
1266
1267 #[test]
1270 fn test_json_array_to_tensor_1d() {
1271 let arr = vec![json!(1.0), json!(2.0), json!(3.0)];
1272 let config = FromJsonConfig::default();
1273 let tensor = json_array_to_tensor(&arr, &config, 0).unwrap();
1274 assert_eq!(tensor.flatten(), vec![1.0, 2.0, 3.0]);
1275 }
1276
1277 #[test]
1278 fn test_json_array_to_tensor_2d() {
1279 let arr = vec![json!([1.0, 2.0]), json!([3.0, 4.0])];
1280 let config = FromJsonConfig::default();
1281 let tensor = json_array_to_tensor(&arr, &config, 0).unwrap();
1282 assert_eq!(tensor.flatten(), vec![1.0, 2.0, 3.0, 4.0]);
1283 }
1284
1285 #[test]
1286 fn test_json_array_to_tensor_invalid_element() {
1287 let arr = vec![json!(1.0), json!("not a number")];
1288 let config = FromJsonConfig::default();
1289 let result = json_array_to_tensor(&arr, &config, 0);
1290 assert!(result.is_err());
1291 }
1292
1293 #[test]
1296 fn test_json_to_value_null() {
1297 let config = FromJsonConfig::default();
1298 let result = json_to_value(&JsonValue::Null, &config).unwrap();
1299 assert!(matches!(result, Value::Null));
1300 }
1301
1302 #[test]
1303 fn test_json_to_value_bool() {
1304 let config = FromJsonConfig::default();
1305 let result = json_to_value(&json!(true), &config).unwrap();
1306 assert!(matches!(result, Value::Bool(true)));
1307
1308 let result = json_to_value(&json!(false), &config).unwrap();
1309 assert!(matches!(result, Value::Bool(false)));
1310 }
1311
1312 #[test]
1313 fn test_json_to_value_int() {
1314 let config = FromJsonConfig::default();
1315 let result = json_to_value(&json!(42), &config).unwrap();
1316 assert!(matches!(result, Value::Int(42)));
1317 }
1318
1319 #[test]
1320 fn test_json_to_value_float() {
1321 let config = FromJsonConfig::default();
1322 let result = json_to_value(&json!(3.5), &config).unwrap();
1323 if let Value::Float(f) = result {
1324 assert!((f - 3.5).abs() < 0.001);
1325 } else {
1326 panic!("Expected Float");
1327 }
1328 }
1329
1330 #[test]
1331 fn test_json_to_value_string() {
1332 let config = FromJsonConfig::default();
1333 let result = json_to_value(&json!("hello"), &config).unwrap();
1334 assert!(matches!(result, Value::String(s) if s == "hello"));
1335 }
1336
1337 #[test]
1338 fn test_json_to_value_expression() {
1339 let config = FromJsonConfig::default();
1340 let result = json_to_value(&json!("$(foo)"), &config).unwrap();
1341 assert!(matches!(result, Value::Expression(_)));
1342 }
1343
1344 #[test]
1345 fn test_json_to_value_tensor() {
1346 let config = FromJsonConfig::default();
1347 let result = json_to_value(&json!([1.0, 2.0, 3.0]), &config).unwrap();
1348 if let Value::Tensor(t) = result {
1349 assert_eq!(t.flatten(), vec![1.0, 2.0, 3.0]);
1350 } else {
1351 panic!("Expected Tensor");
1352 }
1353 }
1354
1355 #[test]
1356 fn test_json_to_value_reference() {
1357 let config = FromJsonConfig::default();
1358 let result = json_to_value(&json!({"@ref": "@User:123"}), &config).unwrap();
1359 if let Value::Reference(r) = result {
1360 assert_eq!(r.type_name, Some("User".to_string()));
1361 assert_eq!(r.id, "123");
1362 } else {
1363 panic!("Expected Reference");
1364 }
1365 }
1366
1367 #[test]
1368 fn test_json_to_value_nested_object_error() {
1369 let config = FromJsonConfig::default();
1370 let result = json_to_value(&json!({"key": "value"}), &config);
1371 assert!(result.is_err());
1372 }
1373
1374 #[test]
1377 fn test_from_json_empty_object() {
1378 let json = "{}";
1379 let config = FromJsonConfig::default();
1380 let doc = from_json(json, &config).unwrap();
1381 assert!(doc.root.is_empty());
1382 assert_eq!(doc.version, (1, 0));
1383 }
1384
1385 #[test]
1386 fn test_from_json_simple_scalars() {
1387 let json = r#"{"name": "test", "count": 42, "active": true}"#;
1388 let config = FromJsonConfig::default();
1389 let doc = from_json(json, &config).unwrap();
1390 assert!(doc.root.contains_key("name"));
1391 assert!(doc.root.contains_key("count"));
1392 assert!(doc.root.contains_key("active"));
1393 }
1394
1395 #[test]
1396 fn test_from_json_nested_object() {
1397 let json = r#"{"outer": {"inner": "value"}}"#;
1398 let config = FromJsonConfig::default();
1399 let doc = from_json(json, &config).unwrap();
1400 if let Item::Object(obj) = &doc.root["outer"] {
1401 assert!(obj.contains_key("inner"));
1402 } else {
1403 panic!("Expected Object");
1404 }
1405 }
1406
1407 #[test]
1408 fn test_from_json_array_of_objects() {
1409 let json = r#"{"users": [{"id": "1", "name": "Alice"}]}"#;
1410 let config = FromJsonConfig::default();
1411 let doc = from_json(json, &config).unwrap();
1412 if let Item::List(list) = &doc.root["users"] {
1413 assert_eq!(list.type_name, "User");
1414 assert_eq!(list.rows.len(), 1);
1415 } else {
1416 panic!("Expected List");
1417 }
1418 }
1419
1420 #[test]
1421 fn test_from_json_tensor() {
1422 let json = r#"{"data": [1, 2, 3]}"#;
1423 let config = FromJsonConfig::default();
1424 let doc = from_json(json, &config).unwrap();
1425 if let Item::Scalar(Value::Tensor(t)) = &doc.root["data"] {
1426 assert_eq!(t.flatten(), vec![1.0, 2.0, 3.0]);
1427 } else {
1428 panic!("Expected Tensor");
1429 }
1430 }
1431
1432 #[test]
1433 fn test_from_json_invalid_json() {
1434 let json = "not valid json";
1435 let config = FromJsonConfig::default();
1436 let result = from_json(json, &config);
1437 assert!(result.is_err());
1438 }
1439
1440 #[test]
1441 fn test_from_json_non_object_root() {
1442 let json = "[1, 2, 3]";
1443 let config = FromJsonConfig::default();
1444 let result = from_json(json, &config);
1445 assert!(result.is_err());
1446 }
1447
1448 #[test]
1451 fn test_from_json_value_simple() {
1452 let value = json!({"key": 42});
1453 let config = FromJsonConfig::default();
1454 let doc = from_json_value(&value, &config).unwrap();
1455 if let Item::Scalar(Value::Int(n)) = &doc.root["key"] {
1456 assert_eq!(*n, 42);
1457 } else {
1458 panic!("Expected Int");
1459 }
1460 }
1461
1462 #[test]
1465 fn test_json_value_to_item_null() {
1466 let config = FromJsonConfig::default();
1467 let mut structs = BTreeMap::new();
1468 let mut schema_cache = SchemaCache::new();
1469 let result = json_value_to_item(&JsonValue::Null, "test", &config, &mut structs, &mut schema_cache, 0).unwrap();
1470 assert!(matches!(result, Item::Scalar(Value::Null)));
1471 }
1472
1473 #[test]
1474 fn test_json_value_to_item_bool() {
1475 let config = FromJsonConfig::default();
1476 let mut structs = BTreeMap::new();
1477 let mut schema_cache = SchemaCache::new();
1478 let result = json_value_to_item(&json!(true), "test", &config, &mut structs, &mut schema_cache, 0).unwrap();
1479 assert!(matches!(result, Item::Scalar(Value::Bool(true))));
1480 }
1481
1482 #[test]
1483 fn test_json_value_to_item_empty_array() {
1484 let config = FromJsonConfig::default();
1485 let mut structs = BTreeMap::new();
1486 let mut schema_cache = SchemaCache::new();
1487 let result = json_value_to_item(&json!([]), "items", &config, &mut structs, &mut schema_cache, 0).unwrap();
1488 if let Item::List(list) = result {
1489 assert!(list.rows.is_empty());
1490 assert_eq!(list.type_name, "Item");
1491 } else {
1492 panic!("Expected List");
1493 }
1494 }
1495
1496 #[test]
1499 fn test_schema_inference_id_first() {
1500 let json = r#"{"users": [{"name": "Alice", "id": "1", "age": 30}]}"#;
1501 let config = FromJsonConfig::default();
1502 let doc = from_json(json, &config).unwrap();
1503 if let Item::List(list) = &doc.root["users"] {
1504 assert_eq!(list.schema[0], "id"); } else {
1506 panic!("Expected List");
1507 }
1508 }
1509
1510 #[test]
1511 fn test_struct_registration() {
1512 let json = r#"{"users": [{"id": "1"}]}"#;
1513 let config = FromJsonConfig::default();
1514 let doc = from_json(json, &config).unwrap();
1515 assert!(doc.structs.contains_key("User"));
1516 }
1517
1518 #[test]
1521 fn test_max_depth_exceeded() {
1522 let json = r#"{"a":1}"#;
1525
1526 let config = FromJsonConfig {
1527 default_type_name: "Item".to_string(),
1528 version: (1, 0),
1529 max_depth: Some(0), max_array_size: Some(100_000),
1531 max_string_length: Some(10_000_000),
1532 max_object_size: Some(10_000),
1533 };
1534
1535 let result = from_json(json, &config);
1536 assert!(result.is_err(), "Expected error for depth 0");
1537 let err_msg = result.unwrap_err().to_string();
1538 assert!(err_msg.contains("Maximum recursion depth"));
1539 }
1540
1541 #[test]
1542 fn test_max_array_size_exceeded() {
1543 let config = FromJsonConfig {
1544 default_type_name: "Item".to_string(),
1545 version: (1, 0),
1546 max_depth: Some(100),
1547 max_array_size: Some(10), max_string_length: Some(10_000_000),
1549 max_object_size: Some(10_000),
1550 };
1551
1552 let json = r#"{"items": [1,2,3,4,5,6,7,8,9,10,11]}"#;
1554 let result = from_json(json, &config);
1555 assert!(result.is_err());
1556 let err_msg = result.unwrap_err().to_string();
1557 assert!(err_msg.contains("Maximum array size"));
1558 }
1559
1560 #[test]
1561 fn test_max_string_length_exceeded() {
1562 let config = FromJsonConfig {
1563 default_type_name: "Item".to_string(),
1564 version: (1, 0),
1565 max_depth: Some(100),
1566 max_array_size: Some(100_000),
1567 max_string_length: Some(100), max_object_size: Some(10_000),
1569 };
1570
1571 let long_string = "a".repeat(101);
1573 let json = format!(r#"{{"text": "{}"}}"#, long_string);
1574 let result = from_json(&json, &config);
1575 assert!(result.is_err());
1576 let err_msg = result.unwrap_err().to_string();
1577 assert!(err_msg.contains("Maximum string length"));
1578 }
1579
1580 #[test]
1581 fn test_max_object_size_exceeded() {
1582 let config = FromJsonConfig {
1583 default_type_name: "Item".to_string(),
1584 version: (1, 0),
1585 max_depth: Some(100),
1586 max_array_size: Some(100_000),
1587 max_string_length: Some(10_000_000),
1588 max_object_size: Some(5), };
1590
1591 let json = r#"{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6}"#;
1593 let result = from_json(json, &config);
1594 assert!(result.is_err());
1595 let err_msg = result.unwrap_err().to_string();
1596 assert!(err_msg.contains("Maximum object size"));
1597 }
1598
1599 #[test]
1600 fn test_limits_disabled() {
1601 let config = FromJsonConfig {
1602 default_type_name: "Item".to_string(),
1603 version: (1, 0),
1604 max_depth: None, max_array_size: None,
1606 max_string_length: None,
1607 max_object_size: None,
1608 };
1609
1610 let long_string = "a".repeat(1000);
1612 let json = format!(r#"{{"text": "{}"}}"#, long_string);
1613 let result = from_json(&json, &config);
1614 assert!(result.is_ok());
1615 }
1616
1617 #[test]
1618 fn test_error_message_quality() {
1619 let config = FromJsonConfig::default();
1620
1621 let result1 = from_json("not json", &config);
1623 assert!(result1.unwrap_err().to_string().contains("JSON parse error"));
1624
1625 let result2 = from_json("[1,2,3]", &config);
1626 assert!(result2.unwrap_err().to_string().contains("Root must be"));
1627
1628 let result3 = from_json(r#"{"ref": {"@ref": "bad"}}"#, &config);
1629 assert!(result3.is_err()); }
1631}
1632
1633#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1641#[derive(Default)]
1642pub enum ErrorTolerance {
1643 #[default]
1645 StopOnFirst,
1646
1647 MaxErrors(usize),
1649
1650 CollectAll,
1652
1653 SkipInvalidItems,
1655}
1656
1657
1658#[derive(Debug, Clone, PartialEq, Eq)]
1660pub struct ErrorLocation {
1661 pub path: String,
1663
1664 pub depth: usize,
1666}
1667
1668impl ErrorLocation {
1669 fn root() -> Self {
1670 Self {
1671 path: "$".to_string(),
1672 depth: 0,
1673 }
1674 }
1675
1676 fn child(&self, key: &str) -> Self {
1677 Self {
1678 path: format!("{}.{}", self.path, key),
1679 depth: self.depth + 1,
1680 }
1681 }
1682
1683 fn index(&self, idx: usize) -> Self {
1684 Self {
1685 path: format!("{}[{}]", self.path, idx),
1686 depth: self.depth + 1,
1687 }
1688 }
1689}
1690
1691#[derive(Debug, Clone)]
1693pub struct ParseError {
1694 pub error: JsonConversionError,
1696
1697 pub location: ErrorLocation,
1699
1700 pub is_fatal: bool,
1702}
1703
1704impl ParseError {
1705 fn new(error: JsonConversionError, location: ErrorLocation, is_fatal: bool) -> Self {
1706 Self {
1707 error,
1708 location,
1709 is_fatal,
1710 }
1711 }
1712}
1713
1714#[derive(Debug, Clone)]
1716#[derive(Default)]
1717pub struct PartialConfig {
1718 pub from_json_config: FromJsonConfig,
1720
1721 pub tolerance: ErrorTolerance,
1723
1724 pub include_partial_on_fatal: bool,
1726
1727 pub replace_invalid_with_null: bool,
1729}
1730
1731
1732impl PartialConfig {
1733 pub fn builder() -> PartialConfigBuilder {
1735 PartialConfigBuilder::default()
1736 }
1737}
1738
1739#[derive(Debug, Clone)]
1741#[derive(Default)]
1742pub struct PartialConfigBuilder {
1743 from_json_config: FromJsonConfig,
1744 tolerance: ErrorTolerance,
1745 include_partial_on_fatal: bool,
1746 replace_invalid_with_null: bool,
1747}
1748
1749
1750impl PartialConfigBuilder {
1751 pub fn from_json_config(mut self, config: FromJsonConfig) -> Self {
1753 self.from_json_config = config;
1754 self
1755 }
1756
1757 pub fn tolerance(mut self, tolerance: ErrorTolerance) -> Self {
1759 self.tolerance = tolerance;
1760 self
1761 }
1762
1763 pub fn include_partial_on_fatal(mut self, value: bool) -> Self {
1765 self.include_partial_on_fatal = value;
1766 self
1767 }
1768
1769 pub fn replace_invalid_with_null(mut self, value: bool) -> Self {
1771 self.replace_invalid_with_null = value;
1772 self
1773 }
1774
1775 pub fn build(self) -> PartialConfig {
1777 PartialConfig {
1778 from_json_config: self.from_json_config,
1779 tolerance: self.tolerance,
1780 include_partial_on_fatal: self.include_partial_on_fatal,
1781 replace_invalid_with_null: self.replace_invalid_with_null,
1782 }
1783 }
1784}
1785
1786#[derive(Debug)]
1788pub struct PartialResult {
1789 pub document: Option<Document>,
1791
1792 pub errors: Vec<ParseError>,
1794
1795 pub stopped_early: bool,
1797}
1798
1799impl PartialResult {
1800 pub fn is_complete(&self) -> bool {
1802 self.errors.is_empty() && self.document.is_some()
1803 }
1804
1805 pub fn is_failed(&self) -> bool {
1807 self.errors.iter().any(|e| e.is_fatal) || self.document.is_none()
1808 }
1809
1810 pub fn into_result(self) -> Result<Document, Vec<ParseError>> {
1812 if self.errors.is_empty() {
1813 self.document.ok_or_else(Vec::new)
1814 } else {
1815 Err(self.errors)
1816 }
1817 }
1818}
1819
1820struct ErrorContext {
1822 errors: Vec<ParseError>,
1823 config: PartialConfig,
1824 stopped: bool,
1825}
1826
1827impl ErrorContext {
1828 fn new(config: PartialConfig) -> Self {
1829 Self {
1830 errors: Vec::new(),
1831 config,
1832 stopped: false,
1833 }
1834 }
1835
1836 fn record_error(&mut self, error: JsonConversionError, location: ErrorLocation, is_fatal: bool) -> bool {
1838 if self.stopped {
1839 return false;
1840 }
1841
1842 let parse_error = ParseError::new(error, location, is_fatal);
1843 self.errors.push(parse_error);
1844
1845 let should_stop = match self.config.tolerance {
1847 ErrorTolerance::StopOnFirst => true,
1848 ErrorTolerance::MaxErrors(max) => self.errors.len() >= max,
1849 ErrorTolerance::CollectAll => false,
1850 ErrorTolerance::SkipInvalidItems => is_fatal,
1851 };
1852
1853 if should_stop {
1854 self.stopped = true;
1855 }
1856
1857 !should_stop
1858 }
1859
1860 fn should_continue(&self) -> bool {
1861 !self.stopped
1862 }
1863}
1864
1865pub fn partial_parse_json(json: &str, config: &PartialConfig) -> PartialResult {
1885 let value = match serde_json::from_str::<JsonValue>(json) {
1887 Ok(v) => v,
1888 Err(e) => {
1889 return PartialResult {
1891 document: None,
1892 errors: vec![ParseError::new(
1893 JsonConversionError::ParseError(e.to_string()),
1894 ErrorLocation::root(),
1895 true,
1896 )],
1897 stopped_early: false,
1898 };
1899 }
1900 };
1901
1902 partial_parse_json_value(&value, config)
1903}
1904
1905pub fn partial_parse_json_value(value: &JsonValue, config: &PartialConfig) -> PartialResult {
1907 let mut context = ErrorContext::new(config.clone());
1908 let mut structs = BTreeMap::new();
1909 let mut schema_cache = SchemaCache::new();
1910
1911 let root = match value {
1913 JsonValue::Object(map) => {
1914 match partial_json_object_to_root(
1915 map,
1916 &config.from_json_config,
1917 &mut structs,
1918 &mut schema_cache,
1919 0,
1920 &ErrorLocation::root(),
1921 &mut context,
1922 ) {
1923 Ok(root) => Some(root),
1924 Err(_) => {
1925 if config.include_partial_on_fatal {
1926 Some(BTreeMap::new())
1927 } else {
1928 None
1929 }
1930 }
1931 }
1932 }
1933 _ => {
1934 context.record_error(
1935 JsonConversionError::InvalidRoot(format!("{:?}", value)),
1936 ErrorLocation::root(),
1937 true,
1938 );
1939 None
1940 }
1941 };
1942
1943 let document = root.map(|root| Document {
1944 version: config.from_json_config.version,
1945 aliases: BTreeMap::new(),
1946 structs,
1947 nests: BTreeMap::new(),
1948 root,
1949 });
1950
1951 PartialResult {
1952 document,
1953 errors: context.errors,
1954 stopped_early: context.stopped,
1955 }
1956}
1957
1958fn partial_json_object_to_root(
1960 map: &Map<String, JsonValue>,
1961 config: &FromJsonConfig,
1962 structs: &mut BTreeMap<String, Vec<String>>,
1963 schema_cache: &mut SchemaCache,
1964 depth: usize,
1965 location: &ErrorLocation,
1966 context: &mut ErrorContext,
1967) -> Result<BTreeMap<String, Item>, JsonConversionError> {
1968 if let Some(max_size) = config.max_object_size {
1970 if map.len() > max_size {
1971 let err = JsonConversionError::MaxObjectSizeExceeded(max_size, map.len());
1972 context.record_error(err.clone(), location.clone(), false);
1973 return Err(err);
1974 }
1975 }
1976
1977 let mut result = BTreeMap::new();
1978
1979 for (key, value) in map {
1980 if !context.should_continue() {
1981 break;
1982 }
1983
1984 if key.starts_with("__") {
1986 continue;
1987 }
1988
1989 let item_location = location.child(key);
1990 match partial_json_value_to_item(
1991 value,
1992 key,
1993 config,
1994 structs,
1995 schema_cache,
1996 depth,
1997 &item_location,
1998 context,
1999 ) {
2000 Ok(item) => {
2001 result.insert(key.clone(), item);
2002 }
2003 Err(_) => {
2004 if context.config.replace_invalid_with_null {
2006 result.insert(key.clone(), Item::Scalar(Value::Null));
2007 }
2008 }
2010 }
2011 }
2012
2013 Ok(result)
2014}
2015
2016fn partial_json_value_to_item(
2018 value: &JsonValue,
2019 key: &str,
2020 config: &FromJsonConfig,
2021 structs: &mut BTreeMap<String, Vec<String>>,
2022 schema_cache: &mut SchemaCache,
2023 depth: usize,
2024 location: &ErrorLocation,
2025 context: &mut ErrorContext,
2026) -> Result<Item, JsonConversionError> {
2027 if let Some(max_depth) = config.max_depth {
2029 if depth >= max_depth {
2030 let err = JsonConversionError::MaxDepthExceeded(max_depth);
2031 context.record_error(err.clone(), location.clone(), false);
2032 return Err(err);
2033 }
2034 }
2035
2036 match value {
2037 JsonValue::Null => Ok(Item::Scalar(Value::Null)),
2038 JsonValue::Bool(b) => Ok(Item::Scalar(Value::Bool(*b))),
2039 JsonValue::Number(n) => {
2040 if let Some(i) = n.as_i64() {
2041 Ok(Item::Scalar(Value::Int(i)))
2042 } else if let Some(f) = n.as_f64() {
2043 Ok(Item::Scalar(Value::Float(f)))
2044 } else {
2045 let err = JsonConversionError::InvalidNumber(n.to_string());
2046 context.record_error(err.clone(), location.clone(), false);
2047 Err(err)
2048 }
2049 }
2050 JsonValue::String(s) => {
2051 if let Some(max_len) = config.max_string_length {
2053 if s.len() > max_len {
2054 let err = JsonConversionError::MaxStringLengthExceeded(max_len, s.len());
2055 context.record_error(err.clone(), location.clone(), false);
2056 return Err(err);
2057 }
2058 }
2059
2060 if s.starts_with("$(") && s.ends_with(')') {
2062 match parse_expression_token(s) {
2063 Ok(expr) => Ok(Item::Scalar(Value::Expression(expr))),
2064 Err(e) => {
2065 let err = JsonConversionError::InvalidExpression(e.to_string());
2066 context.record_error(err.clone(), location.clone(), false);
2067 Err(err)
2068 }
2069 }
2070 } else {
2071 Ok(Item::Scalar(Value::String(s.clone())))
2072 }
2073 }
2074 JsonValue::Array(arr) => {
2075 if let Some(max_size) = config.max_array_size {
2077 if arr.len() > max_size {
2078 let err = JsonConversionError::MaxArraySizeExceeded(max_size, arr.len());
2079 context.record_error(err.clone(), location.clone(), false);
2080 return Err(err);
2081 }
2082 }
2083
2084 if arr.is_empty() {
2086 let type_name = singularize_and_capitalize(key);
2087 let schema: Vec<String> = DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect();
2088 let mut list = MatrixList::new(type_name.clone(), schema.clone());
2089 list.count_hint = Some(0);
2090 structs.insert(type_name, schema);
2091 Ok(Item::List(list))
2092 } else if is_tensor_array(arr) {
2093 match partial_json_array_to_tensor(arr, config, depth + 1, location, context) {
2094 Ok(tensor) => Ok(Item::Scalar(Value::Tensor(tensor))),
2095 Err(err) => Err(err),
2096 }
2097 } else if is_object_array(arr) {
2098 match partial_json_array_to_matrix_list(
2099 arr,
2100 key,
2101 config,
2102 structs,
2103 schema_cache,
2104 depth + 1,
2105 location,
2106 context,
2107 ) {
2108 Ok(list) => Ok(Item::List(list)),
2109 Err(err) => Err(err),
2110 }
2111 } else {
2112 match partial_json_array_to_tensor(arr, config, depth + 1, location, context) {
2114 Ok(tensor) => Ok(Item::Scalar(Value::Tensor(tensor))),
2115 Err(err) => Err(err),
2116 }
2117 }
2118 }
2119 JsonValue::Object(obj) => {
2120 if let Some(JsonValue::String(r)) = obj.get("@ref") {
2122 match parse_reference(r) {
2123 Ok(reference) => Ok(Item::Scalar(Value::Reference(reference))),
2124 Err(e) => {
2125 let err = JsonConversionError::InvalidReference(e);
2126 context.record_error(err.clone(), location.clone(), false);
2127 Err(err)
2128 }
2129 }
2130 } else {
2131 match partial_json_object_to_item_map(
2133 obj,
2134 config,
2135 structs,
2136 schema_cache,
2137 depth + 1,
2138 location,
2139 context,
2140 ) {
2141 Ok(item_map) => Ok(Item::Object(item_map)),
2142 Err(err) => Err(err),
2143 }
2144 }
2145 }
2146 }
2147}
2148
2149fn partial_json_object_to_item_map(
2151 map: &Map<String, JsonValue>,
2152 config: &FromJsonConfig,
2153 structs: &mut BTreeMap<String, Vec<String>>,
2154 schema_cache: &mut SchemaCache,
2155 depth: usize,
2156 location: &ErrorLocation,
2157 context: &mut ErrorContext,
2158) -> Result<BTreeMap<String, Item>, JsonConversionError> {
2159 if let Some(max_size) = config.max_object_size {
2161 if map.len() > max_size {
2162 let err = JsonConversionError::MaxObjectSizeExceeded(max_size, map.len());
2163 context.record_error(err.clone(), location.clone(), false);
2164 return Err(err);
2165 }
2166 }
2167
2168 let mut result = BTreeMap::new();
2169
2170 for (key, value) in map {
2171 if !context.should_continue() {
2172 break;
2173 }
2174
2175 if key.starts_with("__") {
2176 continue;
2177 }
2178
2179 let item_location = location.child(key);
2180 match partial_json_value_to_item(
2181 value,
2182 key,
2183 config,
2184 structs,
2185 schema_cache,
2186 depth,
2187 &item_location,
2188 context,
2189 ) {
2190 Ok(item) => {
2191 result.insert(key.clone(), item);
2192 }
2193 Err(_) => {
2194 if context.config.replace_invalid_with_null {
2195 result.insert(key.clone(), Item::Scalar(Value::Null));
2196 }
2197 }
2198 }
2199 }
2200
2201 Ok(result)
2202}
2203
2204fn partial_json_array_to_tensor(
2206 arr: &[JsonValue],
2207 config: &FromJsonConfig,
2208 depth: usize,
2209 location: &ErrorLocation,
2210 context: &mut ErrorContext,
2211) -> Result<Tensor, JsonConversionError> {
2212 if let Some(max_depth) = config.max_depth {
2214 if depth >= max_depth {
2215 let err = JsonConversionError::MaxDepthExceeded(max_depth);
2216 context.record_error(err.clone(), location.clone(), false);
2217 return Err(err);
2218 }
2219 }
2220
2221 let mut items = Vec::with_capacity(arr.len());
2222
2223 for (idx, v) in arr.iter().enumerate() {
2224 if !context.should_continue() {
2225 break;
2226 }
2227
2228 let elem_location = location.index(idx);
2229 let tensor = match v {
2230 JsonValue::Number(n) => {
2231 match n.as_f64() {
2232 Some(f) => Ok(Tensor::Scalar(f)),
2233 None => {
2234 let err = JsonConversionError::InvalidNumber(n.to_string());
2235 context.record_error(err.clone(), elem_location, false);
2236 Err(err)
2237 }
2238 }
2239 }
2240 JsonValue::Array(nested) => {
2241 partial_json_array_to_tensor(nested, config, depth + 1, &elem_location, context)
2242 }
2243 _ => {
2244 let err = JsonConversionError::InvalidTensor;
2245 context.record_error(err.clone(), elem_location, false);
2246 Err(err)
2247 }
2248 };
2249
2250 match tensor {
2251 Ok(t) => items.push(t),
2252 Err(_) => {
2253 if context.config.replace_invalid_with_null {
2254 items.push(Tensor::Scalar(0.0));
2255 }
2256 }
2258 }
2259 }
2260
2261 Ok(Tensor::Array(items))
2262}
2263
2264#[allow(clippy::too_many_arguments)]
2266fn partial_json_array_to_matrix_list(
2267 arr: &[JsonValue],
2268 key: &str,
2269 config: &FromJsonConfig,
2270 structs: &mut BTreeMap<String, Vec<String>>,
2271 schema_cache: &mut SchemaCache,
2272 depth: usize,
2273 location: &ErrorLocation,
2274 context: &mut ErrorContext,
2275) -> Result<MatrixList, JsonConversionError> {
2276 if let Some(max_depth) = config.max_depth {
2278 if depth >= max_depth {
2279 let err = JsonConversionError::MaxDepthExceeded(max_depth);
2280 context.record_error(err.clone(), location.clone(), false);
2281 return Err(err);
2282 }
2283 }
2284
2285 let type_name = singularize_and_capitalize(key);
2286
2287 let schema: Vec<String> = if let Some(JsonValue::Object(first)) = arr.first() {
2289 if let Some(JsonValue::Array(schema_arr)) = first.get("__hedl_schema") {
2290 schema_arr
2291 .iter()
2292 .filter_map(|v| v.as_str().map(String::from))
2293 .collect()
2294 } else {
2295 let mut cache_key: Vec<String> = first
2296 .keys()
2297 .filter(|k| {
2298 if k.starts_with("__") {
2299 return false;
2300 }
2301 if let Some(JsonValue::Array(arr)) = first.get(*k) {
2302 !is_object_array(arr)
2303 } else {
2304 true
2305 }
2306 })
2307 .cloned()
2308 .collect();
2309 cache_key.sort();
2310
2311 if let Some(cached_schema) = schema_cache.get(&cache_key) {
2312 cached_schema.clone()
2313 } else {
2314 let mut keys = cache_key.clone();
2315 if let Some(pos) = keys.iter().position(|k| k == "id") {
2316 keys.remove(pos);
2317 keys.insert(0, "id".to_string());
2318 }
2319 schema_cache.insert(cache_key, keys.clone());
2320 keys
2321 }
2322 }
2323 } else {
2324 DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect()
2325 };
2326
2327 let schema = if schema.is_empty() {
2328 DEFAULT_SCHEMA.iter().map(|s| s.to_string()).collect()
2329 } else {
2330 schema
2331 };
2332
2333 structs.insert(type_name.clone(), schema.clone());
2334
2335 let mut rows = Vec::with_capacity(arr.len());
2336
2337 for (idx, item) in arr.iter().enumerate() {
2338 if !context.should_continue() {
2339 break;
2340 }
2341
2342 let row_location = location.index(idx);
2343
2344 if let JsonValue::Object(obj) = item {
2345 let id = obj
2346 .get(&schema[0])
2347 .and_then(|v| v.as_str())
2348 .unwrap_or("")
2349 .to_string();
2350
2351 let mut fields = Vec::with_capacity(schema.len());
2352 for col in &schema {
2353 match obj.get(col) {
2354 Some(v) => {
2355 match partial_json_to_value(v, config, &row_location.child(col), context) {
2356 Ok(value) => fields.push(value),
2357 Err(_) => {
2358 if context.config.replace_invalid_with_null {
2359 fields.push(Value::Null);
2360 } else {
2361 fields.push(Value::Null);
2362 }
2363 }
2364 }
2365 }
2366 None => fields.push(Value::Null),
2367 }
2368 }
2369
2370 let mut children: BTreeMap<String, Vec<Node>> = BTreeMap::new();
2372 for (child_key, child_value) in obj.iter() {
2373 if !context.should_continue() {
2374 break;
2375 }
2376
2377 if let JsonValue::Array(child_arr) = child_value {
2378 if is_object_array(child_arr) {
2379 let child_location = row_location.child(child_key);
2380 match partial_json_array_to_matrix_list(
2381 child_arr,
2382 child_key,
2383 config,
2384 structs,
2385 schema_cache,
2386 depth + 1,
2387 &child_location,
2388 context,
2389 ) {
2390 Ok(child_list) => {
2391 children.insert(child_key.clone(), child_list.rows);
2392 }
2393 Err(_) => {
2394 }
2396 }
2397 }
2398 }
2399 }
2400
2401 let node = Node {
2402 type_name: type_name.clone(),
2403 id,
2404 fields,
2405 children,
2406 child_count: None,
2407 };
2408
2409 rows.push(node);
2410 } else {
2411 let err = JsonConversionError::InvalidRoot("Expected object in array".to_string());
2413 context.record_error(err, row_location, false);
2414
2415 if context.config.tolerance == ErrorTolerance::SkipInvalidItems {
2417 continue;
2418 }
2419 }
2420 }
2421
2422 let count_hint = Some(rows.len());
2423
2424 Ok(MatrixList {
2425 type_name,
2426 schema,
2427 rows,
2428 count_hint,
2429 })
2430}
2431
2432fn partial_json_to_value(
2434 value: &JsonValue,
2435 config: &FromJsonConfig,
2436 location: &ErrorLocation,
2437 context: &mut ErrorContext,
2438) -> Result<Value, JsonConversionError> {
2439 match value {
2440 JsonValue::Null => Ok(Value::Null),
2441 JsonValue::Bool(b) => Ok(Value::Bool(*b)),
2442 JsonValue::Number(n) => {
2443 if let Some(i) = n.as_i64() {
2444 Ok(Value::Int(i))
2445 } else if let Some(f) = n.as_f64() {
2446 Ok(Value::Float(f))
2447 } else {
2448 let err = JsonConversionError::InvalidNumber(n.to_string());
2449 context.record_error(err.clone(), location.clone(), false);
2450 Err(err)
2451 }
2452 }
2453 JsonValue::String(s) => {
2454 if let Some(max_len) = config.max_string_length {
2456 if s.len() > max_len {
2457 let err = JsonConversionError::MaxStringLengthExceeded(max_len, s.len());
2458 context.record_error(err.clone(), location.clone(), false);
2459 return Err(err);
2460 }
2461 }
2462
2463 if s.starts_with("$(") && s.ends_with(')') {
2465 match parse_expression_token(s) {
2466 Ok(expr) => Ok(Value::Expression(expr)),
2467 Err(e) => {
2468 let err = JsonConversionError::InvalidExpression(e.to_string());
2469 context.record_error(err.clone(), location.clone(), false);
2470 Err(err)
2471 }
2472 }
2473 } else {
2474 Ok(Value::String(s.clone()))
2475 }
2476 }
2477 JsonValue::Array(arr) => {
2478 if let Some(max_size) = config.max_array_size {
2480 if arr.len() > max_size {
2481 let err = JsonConversionError::MaxArraySizeExceeded(max_size, arr.len());
2482 context.record_error(err.clone(), location.clone(), false);
2483 return Err(err);
2484 }
2485 }
2486
2487 if is_object_array(arr) {
2488 Ok(Value::Null) } else if is_tensor_array(arr) {
2490 match partial_json_array_to_tensor(arr, config, 0, location, context) {
2491 Ok(tensor) => Ok(Value::Tensor(tensor)),
2492 Err(err) => Err(err),
2493 }
2494 } else if arr.is_empty() {
2495 Ok(Value::Tensor(Tensor::Array(vec![])))
2496 } else {
2497 match partial_json_array_to_tensor(arr, config, 0, location, context) {
2498 Ok(tensor) => Ok(Value::Tensor(tensor)),
2499 Err(err) => Err(err),
2500 }
2501 }
2502 }
2503 JsonValue::Object(obj) => {
2504 if let Some(JsonValue::String(r)) = obj.get("@ref") {
2505 match parse_reference(r) {
2506 Ok(reference) => Ok(Value::Reference(reference)),
2507 Err(e) => {
2508 let err = JsonConversionError::InvalidReference(e);
2509 context.record_error(err.clone(), location.clone(), false);
2510 Err(err)
2511 }
2512 }
2513 } else {
2514 let err = JsonConversionError::NestedObject;
2515 context.record_error(err.clone(), location.clone(), false);
2516 Err(err)
2517 }
2518 }
2519 }
2520}