1use std::{collections::BTreeSet, fmt, sync::Arc};
2
3use serde::{
4 Deserialize, Deserializer, Serialize, Serializer,
5 de::{DeserializeSeed, Error as DeError, MapAccess, SeqAccess, Visitor},
6};
7use serde_json::{Map as SerdeJsonMap, Value as SerdeJsonValue};
8
9use crate::{
10 CoreError, CoreResult, DbString, db_string::MAX_DB_STRING_BYTES, json_patch::apply_json_patch,
11};
12
13#[derive(Clone, Debug, Eq, PartialEq)]
15pub enum JsonPathSelector {
16 Key(DbString),
18 Index(i64),
22 UnsignedIndex(u64),
24}
25
26#[derive(Clone, Debug, PartialEq)]
32pub struct JsonValue {
33 value: Arc<SerdeJsonValue>,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq)]
41pub struct JsonValueRef<'a> {
42 value: &'a SerdeJsonValue,
43}
44
45impl<'a> JsonValueRef<'a> {
46 #[must_use]
48 pub fn as_serde(self) -> &'a SerdeJsonValue {
49 self.value
50 }
51
52 #[must_use]
54 pub fn to_owned_json_value(self) -> JsonValue {
55 JsonValue {
56 value: Arc::new(self.value.clone()),
57 }
58 }
59}
60
61impl JsonValue {
62 pub fn new(value: SerdeJsonValue) -> CoreResult<Self> {
64 validate_json_value(&value)?;
65 Ok(Self {
66 value: Arc::new(value),
67 })
68 }
69
70 pub fn parse_str(text: &str) -> CoreResult<Self> {
77 let value = parse_json_text(text).map_err(|err| CoreError::JsonParse {
78 message: err.to_string(),
79 })?;
80 Self::new(value)
81 }
82
83 #[must_use]
85 pub fn as_serde(&self) -> &SerdeJsonValue {
86 &self.value
87 }
88
89 #[must_use]
91 pub fn as_arc(&self) -> Arc<SerdeJsonValue> {
92 Arc::clone(&self.value)
93 }
94
95 #[must_use]
97 pub fn to_canonical_string(&self) -> String {
98 let mut output = String::new();
99 write_json_canonical(self.as_serde(), &mut output);
100 output
101 }
102
103 #[must_use]
105 pub fn json_type_name(&self) -> &'static str {
106 match self.as_serde() {
107 SerdeJsonValue::Null => "null",
108 SerdeJsonValue::Bool(_) => "boolean",
109 SerdeJsonValue::Number(_) => "number",
110 SerdeJsonValue::String(_) => "string",
111 SerdeJsonValue::Array(_) => "array",
112 SerdeJsonValue::Object(_) => "object",
113 }
114 }
115
116 #[must_use]
123 pub fn contains(&self, candidate: &Self) -> bool {
124 json_contains_value(self.as_serde(), candidate.as_serde())
125 }
126
127 #[must_use]
133 pub fn path_exists(&self, path: &[JsonPathSelector]) -> bool {
134 select_json_path(self.as_serde(), path).is_some()
135 }
136
137 #[must_use]
143 pub fn path_value(&self, path: &[JsonPathSelector]) -> Option<Self> {
144 self.path_value_ref(path)
145 .map(JsonValueRef::to_owned_json_value)
146 }
147
148 #[must_use]
154 pub fn path_value_ref(&self, path: &[JsonPathSelector]) -> Option<JsonValueRef<'_>> {
155 select_json_path(self.as_serde(), path).map(|value| JsonValueRef { value })
156 }
157
158 #[must_use]
164 pub fn path_contains(&self, path: &[JsonPathSelector], candidate: &Self) -> bool {
165 select_json_path(self.as_serde(), path)
166 .is_some_and(|value| json_contains_value(value, candidate.as_serde()))
167 }
168
169 pub fn merge_patch(&self, patch: &Self) -> CoreResult<Self> {
179 let mut target = self.as_serde().clone();
180 merge_patch_value(&mut target, patch.as_serde());
181 Self::new(target)
182 }
183
184 pub fn apply_patch(&self, patch: &Self) -> CoreResult<Self> {
196 Self::new(apply_json_patch(self.as_serde(), patch.as_serde())?)
197 }
198}
199
200fn select_json_path<'a>(
201 mut current: &'a SerdeJsonValue,
202 path: &[JsonPathSelector],
203) -> Option<&'a SerdeJsonValue> {
204 for selector in path {
205 current = select_json_child(current, selector)?;
206 }
207 Some(current)
208}
209
210fn select_json_child<'a>(
211 current: &'a SerdeJsonValue,
212 selector: &JsonPathSelector,
213) -> Option<&'a SerdeJsonValue> {
214 match (current, selector) {
215 (SerdeJsonValue::Object(values), JsonPathSelector::Key(key)) => values.get(key.as_str()),
216 (SerdeJsonValue::Array(values), JsonPathSelector::Index(index)) => {
217 signed_array_index(*index, values.len()).and_then(|index| values.get(index))
218 }
219 (SerdeJsonValue::Array(values), JsonPathSelector::UnsignedIndex(index)) => {
220 usize::try_from(*index)
221 .ok()
222 .and_then(|index| values.get(index))
223 }
224 _ => None,
225 }
226}
227
228fn signed_array_index(index: i64, len: usize) -> Option<usize> {
229 if index >= 0 {
230 usize::try_from(index).ok().filter(|index| *index < len)
231 } else {
232 let offset = usize::try_from(index.unsigned_abs()).ok()?;
233 (offset <= len).then_some(len - offset)
234 }
235}
236
237impl TryFrom<SerdeJsonValue> for JsonValue {
238 type Error = CoreError;
239
240 fn try_from(value: SerdeJsonValue) -> Result<Self, Self::Error> {
241 Self::new(value)
242 }
243}
244
245impl From<JsonValue> for SerdeJsonValue {
246 fn from(value: JsonValue) -> Self {
247 value.as_serde().clone()
248 }
249}
250
251impl Serialize for JsonValue {
252 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
253 where
254 S: Serializer,
255 {
256 if serializer.is_human_readable() {
257 self.as_serde().serialize(serializer)
258 } else {
259 serializer.serialize_str(&self.to_canonical_string())
260 }
261 }
262}
263
264impl<'de> Deserialize<'de> for JsonValue {
265 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266 where
267 D: Deserializer<'de>,
268 {
269 let value = if deserializer.is_human_readable() {
270 StrictJsonValueSeed.deserialize(deserializer)?
271 } else {
272 let value = String::deserialize(deserializer)?;
273 parse_json_text(&value).map_err(serde::de::Error::custom)?
274 };
275 Self::new(value).map_err(serde::de::Error::custom)
276 }
277}
278
279fn parse_json_text(text: &str) -> Result<SerdeJsonValue, serde_json::Error> {
280 let mut deserializer = serde_json::Deserializer::from_str(text);
281 let value = StrictJsonValueSeed.deserialize(&mut deserializer)?;
282 deserializer.end()?;
283 Ok(value)
284}
285
286struct StrictJsonValueSeed;
287
288impl<'de> DeserializeSeed<'de> for StrictJsonValueSeed {
289 type Value = SerdeJsonValue;
290
291 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
292 where
293 D: Deserializer<'de>,
294 {
295 deserializer.deserialize_any(StrictJsonValueVisitor)
296 }
297}
298
299struct StrictJsonValueVisitor;
300
301impl<'de> Visitor<'de> for StrictJsonValueVisitor {
302 type Value = SerdeJsonValue;
303
304 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
305 formatter.write_str("a JSON value with unique object keys")
306 }
307
308 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
309 Ok(SerdeJsonValue::Bool(value))
310 }
311
312 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
313 Ok(SerdeJsonValue::Number(value.into()))
314 }
315
316 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
317 Ok(SerdeJsonValue::Number(value.into()))
318 }
319
320 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
321 where
322 E: DeError,
323 {
324 let number = serde_json::Number::from_f64(value)
325 .ok_or_else(|| E::custom("JSON number is not finite"))?;
326 Ok(SerdeJsonValue::Number(number))
327 }
328
329 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
330 Ok(SerdeJsonValue::String(value.to_owned()))
331 }
332
333 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
334 Ok(SerdeJsonValue::String(value))
335 }
336
337 fn visit_none<E>(self) -> Result<Self::Value, E> {
338 Ok(SerdeJsonValue::Null)
339 }
340
341 fn visit_unit<E>(self) -> Result<Self::Value, E> {
342 Ok(SerdeJsonValue::Null)
343 }
344
345 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
346 where
347 D: Deserializer<'de>,
348 {
349 StrictJsonValueSeed.deserialize(deserializer)
350 }
351
352 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
353 where
354 A: SeqAccess<'de>,
355 {
356 let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0));
357 while let Some(value) = seq.next_element_seed(StrictJsonValueSeed)? {
358 values.push(value);
359 }
360 Ok(SerdeJsonValue::Array(values))
361 }
362
363 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
364 where
365 A: MapAccess<'de>,
366 {
367 let mut seen = BTreeSet::new();
368 let mut values = SerdeJsonMap::new();
369 while let Some(key) = map.next_key::<String>()? {
370 if !seen.insert(key.clone()) {
371 return Err(A::Error::custom(format!(
372 "duplicate JSON object key '{key}'"
373 )));
374 }
375 let value = map.next_value_seed(StrictJsonValueSeed)?;
376 values.insert(key, value);
377 }
378 Ok(SerdeJsonValue::Object(values))
379 }
380}
381
382fn validate_json_value(value: &SerdeJsonValue) -> CoreResult<()> {
383 match value {
384 SerdeJsonValue::Null | SerdeJsonValue::Bool(_) | SerdeJsonValue::Number(_) => Ok(()),
385 SerdeJsonValue::String(value) => {
386 validate_json_string_len(value.len())?;
387 Ok(())
388 }
389 SerdeJsonValue::Array(values) => {
390 ensure_json_container_len(values.len())?;
391 for value in values {
392 validate_json_value(value)?;
393 }
394 Ok(())
395 }
396 SerdeJsonValue::Object(values) => {
397 ensure_json_container_len(values.len())?;
398 for (key, value) in values {
399 validate_json_string_len(key.len())?;
400 validate_json_value(value)?;
401 }
402 Ok(())
403 }
404 }
405}
406
407fn validate_json_string_len(len: usize) -> CoreResult<()> {
408 if len > MAX_DB_STRING_BYTES {
409 return Err(CoreError::StringTooLong {
410 got: len,
411 max: u32::MAX,
412 });
413 }
414 Ok(())
415}
416
417fn ensure_json_container_len(len: usize) -> CoreResult<()> {
418 if len > u32::MAX as usize {
419 return Err(CoreError::ConstructedValueTooLarge {
420 got: len,
421 max: u32::MAX,
422 });
423 }
424 Ok(())
425}
426
427fn json_contains_value(target: &SerdeJsonValue, candidate: &SerdeJsonValue) -> bool {
428 match (target, candidate) {
429 (SerdeJsonValue::Object(target), SerdeJsonValue::Object(candidate)) => {
430 candidate.iter().all(|(key, value)| {
431 target
432 .get(key)
433 .is_some_and(|found| json_contains_value(found, value))
434 })
435 }
436 (SerdeJsonValue::Array(target), SerdeJsonValue::Array(candidate)) => candidate
437 .iter()
438 .all(|value| target.iter().any(|found| json_contains_value(found, value))),
439 (SerdeJsonValue::Array(target), candidate) => target
440 .iter()
441 .any(|found| json_contains_value(found, candidate)),
442 _ => target == candidate,
443 }
444}
445
446fn merge_patch_value(target: &mut SerdeJsonValue, patch: &SerdeJsonValue) {
447 let SerdeJsonValue::Object(patch) = patch else {
448 *target = patch.clone();
449 return;
450 };
451
452 if !target.is_object() {
453 *target = SerdeJsonValue::Object(SerdeJsonMap::new());
454 }
455 let target = target
456 .as_object_mut()
457 .expect("target was normalized to object");
458
459 for (key, value) in patch {
460 if value.is_null() {
461 target.remove(key);
462 } else {
463 let entry = target.entry(key.clone()).or_insert(SerdeJsonValue::Null);
464 merge_patch_value(entry, value);
465 }
466 }
467}
468
469fn write_json_canonical(value: &SerdeJsonValue, output: &mut String) {
470 match value {
471 SerdeJsonValue::Null => output.push_str("null"),
472 SerdeJsonValue::Bool(value) => output.push_str(if *value { "true" } else { "false" }),
473 SerdeJsonValue::Number(value) => output.push_str(&value.to_string()),
474 SerdeJsonValue::String(value) => {
475 output.push_str(&serde_json::to_string(value).expect("JSON string rendering succeeds"));
476 }
477 SerdeJsonValue::Array(values) => {
478 output.push('[');
479 for (index, value) in values.iter().enumerate() {
480 if index > 0 {
481 output.push(',');
482 }
483 write_json_canonical(value, output);
484 }
485 output.push(']');
486 }
487 SerdeJsonValue::Object(values) => {
488 output.push('{');
489 let mut entries = values.iter().collect::<Vec<_>>();
490 entries.sort_unstable_by(|lhs, rhs| lhs.0.cmp(rhs.0));
491 for (index, (key, value)) in entries.into_iter().enumerate() {
492 if index > 0 {
493 output.push(',');
494 }
495 output.push_str(&serde_json::to_string(key).expect("JSON key rendering succeeds"));
496 output.push(':');
497 write_json_canonical(value, output);
498 }
499 output.push('}');
500 }
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::{JsonPathSelector, JsonValue};
507 use crate::db_string;
508
509 #[test]
510 fn human_readable_serde_preserves_json_shape() {
511 let value = JsonValue::new(serde_json::json!({"b": [2, true], "a": null})).unwrap();
512 let encoded = serde_json::to_value(&value).expect("JSON value serializes");
513 assert_eq!(encoded, serde_json::json!({"a": null, "b": [2, true]}));
514 let decoded: JsonValue = serde_json::from_value(encoded).expect("JSON value deserializes");
515 assert_eq!(decoded, value);
516 }
517
518 #[test]
519 fn parse_str_rejects_duplicate_object_keys() {
520 let err = JsonValue::parse_str(r#"{"a":1,"nested":{"b":1,"b":2}}"#)
521 .expect_err("duplicate JSON object key rejected");
522
523 assert_eq!(err.gqlstatus(), "22018");
524 assert!(err.to_string().contains("duplicate JSON object key 'b'"));
525 }
526
527 #[test]
528 fn human_readable_serde_rejects_duplicate_object_keys() {
529 let err = serde_json::from_str::<JsonValue>(r#"{"a":1,"a":2}"#)
530 .expect_err("duplicate JSON object key rejected");
531
532 assert!(err.to_string().contains("duplicate JSON object key 'a'"));
533 }
534
535 #[test]
536 fn contains_matches_nested_subset_and_array_membership() {
537 let target = JsonValue::new(serde_json::json!({
538 "memory": {"kind": "episodic", "score": 7},
539 "tags": ["agent", "graph", {"scope": "current"}]
540 }))
541 .unwrap();
542
543 assert!(target.contains(
544 &JsonValue::new(serde_json::json!({"memory": {"kind": "episodic"}})).unwrap()
545 ));
546 assert!(target.contains(&JsonValue::new(serde_json::json!({"tags": "graph"})).unwrap()));
547 assert!(target.contains(
548 &JsonValue::new(serde_json::json!({"tags": [{"scope": "current"}, "agent"]})).unwrap()
549 ));
550 assert!(!target.contains(
551 &JsonValue::new(serde_json::json!({"memory": {"kind": "semantic"}})).unwrap()
552 ));
553 }
554
555 #[test]
556 fn path_exists_matches_object_keys_and_array_indexes() {
557 let target = JsonValue::new(serde_json::json!({
558 "memory": {"facts": [{"title": "old"}, {"title": "current"}]}
559 }))
560 .unwrap();
561 let path = [
562 JsonPathSelector::Key(db_string("memory").unwrap()),
563 JsonPathSelector::Key(db_string("facts").unwrap()),
564 JsonPathSelector::Index(1),
565 JsonPathSelector::Key(db_string("title").unwrap()),
566 ];
567
568 assert!(target.path_exists(&path));
569 assert!(target.path_exists(&[
570 JsonPathSelector::Key(db_string("memory").unwrap()),
571 JsonPathSelector::Key(db_string("facts").unwrap()),
572 JsonPathSelector::Index(-1),
573 JsonPathSelector::Key(db_string("title").unwrap()),
574 ]));
575 assert!(!target.path_exists(&[
576 JsonPathSelector::Key(db_string("memory").unwrap()),
577 JsonPathSelector::Key(db_string("facts").unwrap()),
578 JsonPathSelector::UnsignedIndex(9),
579 ]));
580 }
581
582 #[test]
583 fn path_value_returns_selected_json_subvalue() {
584 let target = JsonValue::new(serde_json::json!({
585 "memory": {
586 "facts": [{"title": "old"}, {"title": "current"}],
587 "score": null
588 }
589 }))
590 .unwrap();
591
592 let selected = target
593 .path_value(&[
594 JsonPathSelector::Key(db_string("memory").unwrap()),
595 JsonPathSelector::Key(db_string("facts").unwrap()),
596 JsonPathSelector::Index(-1),
597 JsonPathSelector::Key(db_string("title").unwrap()),
598 ])
599 .expect("path selects a value");
600 assert_eq!(selected.as_serde(), &serde_json::json!("current"));
601
602 let null_value = target
603 .path_value(&[
604 JsonPathSelector::Key(db_string("memory").unwrap()),
605 JsonPathSelector::Key(db_string("score").unwrap()),
606 ])
607 .expect("JSON null is present");
608 assert_eq!(null_value.as_serde(), &serde_json::Value::Null);
609
610 assert!(
611 target
612 .path_value(&[
613 JsonPathSelector::Key(db_string("memory").unwrap()),
614 JsonPathSelector::Key(db_string("missing").unwrap()),
615 ])
616 .is_none()
617 );
618 }
619
620 #[test]
621 fn path_value_ref_borrows_selected_json_subvalue() {
622 let target = JsonValue::new(serde_json::json!({
623 "memory": {"facts": [{"title": "old"}, {"title": "current"}]}
624 }))
625 .unwrap();
626 let path = [
627 JsonPathSelector::Key(db_string("memory").unwrap()),
628 JsonPathSelector::Key(db_string("facts").unwrap()),
629 JsonPathSelector::Index(-1),
630 JsonPathSelector::Key(db_string("title").unwrap()),
631 ];
632
633 let selected = target.path_value_ref(&path).expect("path selects a value");
634
635 assert_eq!(selected.as_serde(), &serde_json::json!("current"));
636 assert_eq!(
637 selected.to_owned_json_value().as_serde(),
638 &serde_json::json!("current")
639 );
640 }
641
642 #[test]
643 fn path_contains_applies_containment_to_selected_subvalue() {
644 let target = JsonValue::new(serde_json::json!({
645 "memory": {
646 "facts": [
647 {"title": "old", "tags": ["archive"]},
648 {"title": "current", "tags": ["agent", {"scope": "fresh"}]}
649 ]
650 }
651 }))
652 .unwrap();
653 let path = [
654 JsonPathSelector::Key(db_string("memory").unwrap()),
655 JsonPathSelector::Key(db_string("facts").unwrap()),
656 JsonPathSelector::Index(-1),
657 ];
658
659 assert!(target.path_contains(
660 &path,
661 &JsonValue::new(serde_json::json!({"tags": [{"scope": "fresh"}]})).unwrap()
662 ));
663 assert!(!target.path_contains(
664 &path,
665 &JsonValue::new(serde_json::json!({"tags": "archive"})).unwrap()
666 ));
667 assert!(!target.path_contains(
668 &[JsonPathSelector::Key(db_string("missing").unwrap())],
669 &JsonValue::new(serde_json::json!(null)).unwrap()
670 ));
671 }
672
673 #[test]
674 fn merge_patch_matches_rfc7396_core_cases() {
675 for (target, patch, expected) in [
676 (r#"{"a":"b"}"#, r#"{"a":"c"}"#, r#"{"a":"c"}"#),
677 (r#"{"a":"b"}"#, r#"{"b":"c"}"#, r#"{"a":"b","b":"c"}"#),
678 (r#"{"a":"b"}"#, r#"{"a":null}"#, r#"{}"#),
679 (r#"{"a":"b","b":"c"}"#, r#"{"a":null}"#, r#"{"b":"c"}"#),
680 (r#"{"a":["b"]}"#, r#"{"a":"c"}"#, r#"{"a":"c"}"#),
681 (r#"{"a":"c"}"#, r#"{"a":["b"]}"#, r#"{"a":["b"]}"#),
682 (
683 r#"{"a":{"b":"c"}}"#,
684 r#"{"a":{"b":"d","c":null}}"#,
685 r#"{"a":{"b":"d"}}"#,
686 ),
687 (r#"{"a":[{"b":"c"}]}"#, r#"{"a":[1]}"#, r#"{"a":[1]}"#),
688 (r#"["a","b"]"#, r#"["c","d"]"#, r#"["c","d"]"#),
689 (r#"{"a":"b"}"#, r#"["c"]"#, r#"["c"]"#),
690 (r#"{"a":"foo"}"#, r#"null"#, r#"null"#),
691 (r#"{"a":"foo"}"#, r#""bar""#, r#""bar""#),
692 (r#"{"e":null}"#, r#"{"a":1}"#, r#"{"a":1,"e":null}"#),
693 ] {
694 let target = JsonValue::parse_str(target).expect("target JSON parses");
695 let patch = JsonValue::parse_str(patch).expect("patch JSON parses");
696 let expected = JsonValue::parse_str(expected).expect("expected JSON parses");
697
698 assert_eq!(
699 target.merge_patch(&patch).expect("merge patch succeeds"),
700 expected
701 );
702 }
703 }
704}