apollo_router/
json_ext.rs

1//! Performance oriented JSON manipulation.
2
3#![allow(missing_docs)] // FIXME
4
5use std::cmp::min;
6use std::fmt;
7
8use num_traits::ToPrimitive;
9use once_cell::sync::Lazy;
10use regex::Captures;
11use regex::Regex;
12use serde::Deserialize;
13use serde::Serialize;
14use serde_json_bytes::ByteString;
15use serde_json_bytes::Entry;
16use serde_json_bytes::Map;
17pub(crate) use serde_json_bytes::Value;
18
19use crate::error::FetchError;
20use crate::spec::Schema;
21use crate::spec::TYPENAME;
22
23/// A JSON object.
24pub(crate) type Object = Map<ByteString, Value>;
25
26const FRAGMENT_PREFIX: &str = "... on ";
27
28static TYPE_CONDITIONS_REGEX: Lazy<Regex> = Lazy::new(|| {
29    Regex::new(r"\|\[(?<condition>.+?)?\]")
30        .expect("this regex to check for type conditions is valid")
31});
32
33/// Extract the condition list from the regex captures.
34fn extract_matched_conditions(caps: &Captures) -> TypeConditions {
35    caps.name("condition")
36        .map(|c| c.as_str().split(',').map(|s| s.to_string()).collect())
37        .unwrap_or_default()
38}
39
40fn split_path_element_and_type_conditions(s: &str) -> (String, Option<TypeConditions>) {
41    let mut type_conditions = None;
42    let path_element = TYPE_CONDITIONS_REGEX.replace(s, |caps: &Captures| {
43        type_conditions = Some(extract_matched_conditions(caps));
44        ""
45    });
46    (path_element.to_string(), type_conditions)
47}
48
49macro_rules! extract_key_value_from_object {
50    ($object:expr, $key:literal, $pattern:pat => $var:ident) => {{
51        match $object.remove($key) {
52            Some($pattern) => Ok(Some($var)),
53            None | Some(crate::json_ext::Value::Null) => Ok(None),
54            _ => Err(concat!("invalid type for key: ", $key)),
55        }
56    }};
57    ($object:expr, $key:literal) => {{
58        match $object.remove($key) {
59            None | Some(crate::json_ext::Value::Null) => None,
60            Some(value) => Some(value),
61        }
62    }};
63}
64
65macro_rules! ensure_array {
66    ($value:expr) => {{
67        match $value {
68            crate::json_ext::Value::Array(a) => Ok(a),
69            _ => Err("invalid type, expected an array"),
70        }
71    }};
72}
73
74macro_rules! ensure_object {
75    ($value:expr) => {{
76        match $value {
77            crate::json_ext::Value::Object(o) => Ok(o),
78            _ => Err("invalid type, expected an object"),
79        }
80    }};
81}
82
83#[doc(hidden)]
84/// Extension trait for [`serde_json::Value`].
85pub(crate) trait ValueExt {
86    /// Deep merge the JSON objects, array and override the values in `&mut self` if they already
87    /// exists.
88    #[track_caller]
89    fn deep_merge(&mut self, other: Self);
90
91    /// Deep merge two JSON objects, overwriting values in `self` if it has the same key as `other`.
92    /// For GraphQL response objects, this uses schema information to avoid overwriting a concrete
93    /// `__typename` with an interface name.
94    #[track_caller]
95    fn type_aware_deep_merge(&mut self, other: Self, schema: &Schema);
96
97    /// Returns `true` if the values are equal and the objects are ordered the same.
98    ///
99    /// **Note:** this is recursive.
100    #[cfg(test)]
101    fn eq_and_ordered(&self, other: &Self) -> bool;
102
103    /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the
104    /// values in `self`.
105    #[track_caller]
106    #[cfg(test)]
107    fn is_subset(&self, superset: &Value) -> bool;
108
109    /// Create a `Value` by inserting a value at a subpath.
110    ///
111    /// This will create objects, arrays and null nodes as needed if they
112    /// are not present: the resulting Value is meant to be merged with an
113    /// existing one that contains those nodes.
114    #[track_caller]
115    fn from_path(path: &Path, value: Value) -> Value;
116
117    /// Insert a `Value` at a `Path`
118    #[track_caller]
119    fn insert(&mut self, path: &Path, value: Value) -> Result<(), FetchError>;
120
121    /// Get a `Value` from a `Path`
122    #[track_caller]
123    fn get_path<'a>(&'a self, schema: &Schema, path: &'a Path) -> Result<&'a Value, FetchError>;
124
125    /// Select all values matching a `Path`.
126    ///
127    /// the function passed as argument will be called with the values found and their Path
128    /// if it encounters an invalid value, it will ignore it and continue
129    #[track_caller]
130    fn select_values_and_paths<'a, F>(&'a self, schema: &Schema, path: &'a Path, f: F)
131    where
132        F: FnMut(&Path, &'a Value);
133
134    /// Select all values matching a `Path`, and allows to mutate those values.
135    ///
136    /// The behavior of the method is otherwise the same as it's non-mutable counterpart
137    #[track_caller]
138    fn select_values_and_paths_mut<'a, F>(&'a mut self, schema: &Schema, path: &'a Path, f: F)
139    where
140        F: FnMut(&Path, &'a mut Value);
141
142    #[track_caller]
143    fn is_valid_float_input(&self) -> bool;
144
145    #[track_caller]
146    fn is_valid_int_input(&self) -> bool;
147
148    #[track_caller]
149    fn is_valid_id_input(&self) -> bool;
150
151    /// Returns whether this value is an object that matches the provided type.
152    ///
153    /// More precisely, this checks that this value is an object, looks at
154    /// its `__typename` field and checks if that  `__typename` is either
155    /// `maybe_type` or a subtype of it.
156    ///
157    /// If the value is not an object, this will always return `false`, but
158    /// if the value is an object with no `__typename` field, then we default
159    /// to `true` (meaning that the absences of way to check, we assume the
160    /// value is of your expected type).
161    ///
162    /// TODO: in theory, this later default behaviour shouldn't matter since
163    /// we should avoid calling this in cases where the `__typename` is
164    /// unknown, but it is currently *relied* on due to some not-quite-right
165    /// behaviour. See the comment in `ExecutionService.call` around the call
166    /// to `select_values_and_paths` for details (the later relies on this
167    /// function to handle `PathElement::Fragment`).
168    #[track_caller]
169    fn is_object_of_type(&self, schema: &Schema, maybe_type: &str) -> bool;
170
171    fn as_i32(&self) -> Option<i32>;
172}
173
174impl ValueExt for Value {
175    fn deep_merge(&mut self, other: Self) {
176        match (self, other) {
177            (Value::Object(a), Value::Object(b)) => {
178                for (key, value) in b.into_iter() {
179                    match a.entry(key) {
180                        Entry::Vacant(e) => {
181                            e.insert(value);
182                        }
183                        Entry::Occupied(e) => {
184                            e.into_mut().deep_merge(value);
185                        }
186                    }
187                }
188            }
189            (Value::Array(a), Value::Array(mut b)) => {
190                for (b_value, a_value) in b.drain(..min(a.len(), b.len())).zip(a.iter_mut()) {
191                    a_value.deep_merge(b_value);
192                }
193
194                a.extend(b);
195            }
196            (_, Value::Null) => {}
197            (Value::Object(_), Value::Array(_)) => {
198                failfast_debug!("trying to replace an object with an array");
199            }
200            (Value::Array(_), Value::Object(_)) => {
201                failfast_debug!("trying to replace an array with an object");
202            }
203            (a, b) => {
204                if b != Value::Null {
205                    *a = b;
206                }
207            }
208        }
209    }
210
211    fn type_aware_deep_merge(&mut self, other: Self, schema: &Schema) {
212        match (self, other) {
213            (Value::Object(a), Value::Object(b)) => {
214                for (key, value) in b.into_iter() {
215                    let k = key.clone();
216                    match a.entry(key) {
217                        Entry::Vacant(e) => {
218                            e.insert(value);
219                        }
220                        Entry::Occupied(e) => match (e.into_mut(), value) {
221                            (Value::String(type1), Value::String(type2))
222                                if k.as_str() == TYPENAME =>
223                            {
224                                // If type1 is a subtype of type2, we skip this overwrite to preserve the more specific `__typename`
225                                // in the response. Ideally, we could use `Schema::is_implementation`, but that looks to be buggy
226                                // and does not catch the problem we are trying to resolve.
227                                if !schema.is_subtype(type2.as_str(), type1.as_str()) {
228                                    *type1 = type2;
229                                }
230                            }
231                            (t, s) => t.type_aware_deep_merge(s, schema),
232                        },
233                    }
234                }
235            }
236            (Value::Array(a), Value::Array(mut b)) => {
237                for (b_value, a_value) in b.drain(..min(a.len(), b.len())).zip(a.iter_mut()) {
238                    a_value.type_aware_deep_merge(b_value, schema);
239                }
240
241                a.extend(b);
242            }
243            (_, Value::Null) => {}
244            (Value::Object(_), Value::Array(_)) => {
245                failfast_debug!("trying to replace an object with an array");
246            }
247            (Value::Array(_), Value::Object(_)) => {
248                failfast_debug!("trying to replace an array with an object");
249            }
250            (a, b) => {
251                if b != Value::Null {
252                    *a = b;
253                }
254            }
255        }
256    }
257
258    #[cfg(test)]
259    fn eq_and_ordered(&self, other: &Self) -> bool {
260        match (self, other) {
261            (Value::Object(a), Value::Object(b)) => {
262                let mut it_a = a.iter();
263                let mut it_b = b.iter();
264
265                loop {
266                    match (it_a.next(), it_b.next()) {
267                        (Some(_), None) | (None, Some(_)) => break false,
268                        (None, None) => break true,
269                        (Some((field_a, value_a)), Some((field_b, value_b)))
270                            if field_a == field_b && ValueExt::eq_and_ordered(value_a, value_b) =>
271                        {
272                            continue;
273                        }
274                        (Some(_), Some(_)) => break false,
275                    }
276                }
277            }
278            (Value::Array(a), Value::Array(b)) => {
279                let mut it_a = a.iter();
280                let mut it_b = b.iter();
281
282                loop {
283                    match (it_a.next(), it_b.next()) {
284                        (Some(_), None) | (None, Some(_)) => break false,
285                        (None, None) => break true,
286                        (Some(value_a), Some(value_b))
287                            if ValueExt::eq_and_ordered(value_a, value_b) =>
288                        {
289                            continue;
290                        }
291                        (Some(_), Some(_)) => break false,
292                    }
293                }
294            }
295            (a, b) => a == b,
296        }
297    }
298
299    #[cfg(test)]
300    fn is_subset(&self, superset: &Value) -> bool {
301        match (self, superset) {
302            (Value::Object(subset), Value::Object(superset)) => {
303                subset.iter().all(|(key, value)| {
304                    if let Some(other) = superset.get(key) {
305                        value.is_subset(other)
306                    } else {
307                        false
308                    }
309                })
310            }
311            (Value::Array(subset), Value::Array(superset)) => {
312                subset.len() == superset.len()
313                    && subset.iter().enumerate().all(|(index, value)| {
314                        if let Some(other) = superset.get(index) {
315                            value.is_subset(other)
316                        } else {
317                            false
318                        }
319                    })
320            }
321            (a, b) => a == b,
322        }
323    }
324
325    #[track_caller]
326    fn from_path(path: &Path, value: Value) -> Value {
327        let mut res_value = Value::default();
328        let mut current_node = &mut res_value;
329
330        for p in path.iter() {
331            match p {
332                // Type conditions don't matter here since we're just creating a default value.
333                PathElement::Flatten(_) => {
334                    return res_value;
335                }
336
337                &PathElement::Index(index) => match current_node {
338                    Value::Array(a) => {
339                        for _ in 0..index {
340                            a.push(Value::default());
341                        }
342                        a.push(Value::default());
343                        current_node = a
344                            .get_mut(index)
345                            .expect("we just created the value at that index");
346                    }
347                    Value::Null => {
348                        let mut a = Vec::new();
349                        for _ in 0..index {
350                            a.push(Value::default());
351                        }
352                        a.push(Value::default());
353
354                        *current_node = Value::Array(a);
355                        current_node = current_node
356                            .as_array_mut()
357                            .expect("current_node was just set to a Value::Array")
358                            .get_mut(index)
359                            .expect("we just created the value at that index");
360                    }
361                    other => unreachable!("unreachable node: {:?}", other),
362                },
363                // Type conditions don't matter here since we're just creating a default value.
364                PathElement::Key(k, _) => {
365                    let mut m = Map::new();
366                    m.insert(k.as_str(), Value::default());
367
368                    *current_node = Value::Object(m);
369                    current_node = current_node
370                        .as_object_mut()
371                        .expect("current_node was just set to a Value::Object")
372                        .get_mut(k.as_str())
373                        .expect("the value at that key was just inserted");
374                }
375                PathElement::Fragment(_) => {}
376            }
377        }
378
379        *current_node = value;
380        res_value
381    }
382
383    /// Insert a `Value` at a `Path`
384    #[track_caller]
385    fn insert(&mut self, path: &Path, mut value: Value) -> Result<(), FetchError> {
386        let mut current_node = self;
387
388        for p in path.iter() {
389            match p {
390                PathElement::Flatten(type_conditions) => {
391                    value = filter_type_conditions(value, type_conditions);
392                    if current_node.is_null() {
393                        let a = Vec::new();
394                        *current_node = Value::Array(a);
395                    } else if !current_node.is_array() {
396                        return Err(FetchError::ExecutionPathNotFound {
397                            reason: "expected an array".to_string(),
398                        });
399                    }
400                }
401
402                &PathElement::Index(index) => match current_node {
403                    Value::Array(a) => {
404                        // add more elements if the index is after the end
405                        for _ in a.len()..index + 1 {
406                            a.push(Value::default());
407                        }
408                        current_node = a
409                            .get_mut(index)
410                            .expect("we just created the value at that index");
411                    }
412                    Value::Null => {
413                        let mut a = Vec::new();
414                        for _ in 0..index + 1 {
415                            a.push(Value::default());
416                        }
417
418                        *current_node = Value::Array(a);
419                        current_node = current_node
420                            .as_array_mut()
421                            .expect("current_node was just set to a Value::Array")
422                            .get_mut(index)
423                            .expect("we just created the value at that index");
424                    }
425                    _other => {
426                        return Err(FetchError::ExecutionPathNotFound {
427                            reason: "expected an array".to_string(),
428                        });
429                    }
430                },
431                PathElement::Key(k, type_conditions) => {
432                    value = filter_type_conditions(value, type_conditions);
433                    match current_node {
434                        Value::Object(o) => {
435                            current_node = o
436                                .get_mut(k.as_str())
437                                .expect("the value at that key was just inserted");
438                        }
439                        Value::Null => {
440                            let mut m = Map::new();
441                            m.insert(k.as_str(), Value::default());
442
443                            *current_node = Value::Object(m);
444                            current_node = current_node
445                                .as_object_mut()
446                                .expect("current_node was just set to a Value::Object")
447                                .get_mut(k.as_str())
448                                .expect("the value at that key was just inserted");
449                        }
450                        _other => {
451                            return Err(FetchError::ExecutionPathNotFound {
452                                reason: "expected an object".to_string(),
453                            });
454                        }
455                    }
456                }
457                PathElement::Fragment(_) => {}
458            }
459        }
460
461        *current_node = value;
462        Ok(())
463    }
464
465    /// Get a `Value` from a `Path`
466    #[track_caller]
467    fn get_path<'a>(&'a self, schema: &Schema, path: &'a Path) -> Result<&'a Value, FetchError> {
468        let mut res = Err(FetchError::ExecutionPathNotFound {
469            reason: "value not found".to_string(),
470        });
471        iterate_path(
472            schema,
473            &mut Path::default(),
474            &path.0,
475            self,
476            &mut |_path, value| {
477                res = Ok(value);
478            },
479        );
480        res
481    }
482
483    #[track_caller]
484    fn select_values_and_paths<'a, F>(&'a self, schema: &Schema, path: &'a Path, mut f: F)
485    where
486        F: FnMut(&Path, &'a Value),
487    {
488        iterate_path(schema, &mut Path::default(), &path.0, self, &mut f)
489    }
490
491    #[track_caller]
492    fn select_values_and_paths_mut<'a, F>(&'a mut self, schema: &Schema, path: &'a Path, mut f: F)
493    where
494        F: FnMut(&Path, &'a mut Value),
495    {
496        iterate_path_mut(schema, &mut Path::default(), &path.0, self, &mut f)
497    }
498
499    #[track_caller]
500    fn is_valid_id_input(&self) -> bool {
501        // https://spec.graphql.org/October2021/#sec-ID.Input-Coercion
502        match self {
503            // Any string and integer values are accepted
504            Value::String(_) => true,
505            Value::Number(n) => n.is_i64() || n.is_u64(),
506            _ => false,
507        }
508    }
509
510    #[track_caller]
511    fn is_valid_float_input(&self) -> bool {
512        // https://spec.graphql.org/draft/#sec-Float.Input-Coercion
513        match self {
514            // When expected as an input type, both integer and float input values are accepted.
515            Value::Number(n) if n.is_f64() => true,
516            // Integer input values are coerced to Float by adding an empty fractional part, for example 1.0 for the integer input value 1.
517            Value::Number(n) => n.is_i64(),
518            // All other input values, including strings with numeric content, must raise a request error indicating an incorrect type.
519            _ => false,
520        }
521    }
522
523    #[track_caller]
524    fn is_valid_int_input(&self) -> bool {
525        // https://spec.graphql.org/June2018/#sec-Int
526        // The Int scalar type represents a signed 32‐bit numeric non‐fractional value.
527        // When expected as an input type, only integer input values are accepted.
528        // All other input values, including strings with numeric content, must raise a query error indicating an incorrect type.
529        self.as_i64().and_then(|x| i32::try_from(x).ok()).is_some()
530            || self.as_u64().and_then(|x| i32::try_from(x).ok()).is_some()
531    }
532
533    #[track_caller]
534    fn is_object_of_type(&self, schema: &Schema, maybe_type: &str) -> bool {
535        self.is_object()
536            && self
537                .get(TYPENAME)
538                .and_then(|v| v.as_str())
539                .is_none_or(|typename| {
540                    typename == maybe_type || schema.is_subtype(maybe_type, typename)
541                })
542    }
543
544    fn as_i32(&self) -> Option<i32> {
545        self.as_i64()?.to_i32()
546    }
547}
548
549fn filter_type_conditions(value: Value, type_conditions: &Option<TypeConditions>) -> Value {
550    if let Some(tc) = type_conditions {
551        match value {
552            Value::Object(ref o) => {
553                if let Some(Value::String(type_name)) = &o.get("__typename") {
554                    if !tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
555                        return Value::Null;
556                    }
557                }
558            }
559            Value::Array(v) => {
560                return Value::Array(
561                    v.into_iter()
562                        .map(|v| filter_type_conditions(v, type_conditions))
563                        .collect(),
564                );
565            }
566            _ => {}
567        }
568    }
569    value
570}
571
572fn iterate_path<'a, F>(
573    schema: &Schema,
574    parent: &mut Path,
575    path: &'a [PathElement],
576    data: &'a Value,
577    f: &mut F,
578) where
579    F: FnMut(&Path, &'a Value),
580{
581    match path.first() {
582        None => f(parent, data),
583        Some(PathElement::Flatten(type_conditions)) => {
584            if let Some(array) = data.as_array() {
585                for (i, value) in array.iter().enumerate() {
586                    if let Some(tc) = type_conditions {
587                        if !tc.is_empty() {
588                            if let Value::Object(o) = value {
589                                if let Some(Value::String(type_name)) = o.get("__typename") {
590                                    if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
591                                        parent.push(PathElement::Index(i));
592                                        iterate_path(schema, parent, &path[1..], value, f);
593                                        parent.pop();
594                                    }
595                                }
596                            }
597
598                            if let Value::Array(array) = value {
599                                for (i, value) in array.iter().enumerate() {
600                                    if let Value::Object(o) = value {
601                                        if let Some(Value::String(type_name)) = o.get("__typename")
602                                        {
603                                            if tc.iter().any(|tc| tc.as_str() == type_name.as_str())
604                                            {
605                                                parent.push(PathElement::Index(i));
606                                                iterate_path(schema, parent, &path[1..], value, f);
607                                                parent.pop();
608                                            }
609                                        }
610                                    }
611                                }
612                            }
613                        }
614                    } else {
615                        parent.push(PathElement::Index(i));
616                        iterate_path(schema, parent, &path[1..], value, f);
617                        parent.pop();
618                    }
619                }
620            }
621        }
622        Some(PathElement::Index(i)) => {
623            if let Value::Array(a) = data {
624                if let Some(value) = a.get(*i) {
625                    parent.push(PathElement::Index(*i));
626                    iterate_path(schema, parent, &path[1..], value, f);
627                    parent.pop();
628                }
629            }
630        }
631        Some(PathElement::Key(k, type_conditions)) => {
632            if let Some(tc) = type_conditions {
633                if !tc.is_empty() {
634                    if let Value::Object(o) = data {
635                        if let Some(value) = o.get(k.as_str()) {
636                            if let Some(Value::String(type_name)) = value.get("__typename") {
637                                if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
638                                    parent.push(PathElement::Key(k.to_string(), None));
639                                    iterate_path(schema, parent, &path[1..], value, f);
640                                    parent.pop();
641                                }
642                            }
643                        }
644                    } else if let Value::Array(array) = data {
645                        for (i, value) in array.iter().enumerate() {
646                            if let Value::Object(o) = value {
647                                if let Some(Value::String(type_name)) = o.get("__typename") {
648                                    if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
649                                        parent.push(PathElement::Index(i));
650                                        iterate_path(schema, parent, path, value, f);
651                                        parent.pop();
652                                    }
653                                }
654                            }
655                        }
656                    }
657                }
658            } else if let Value::Object(o) = data {
659                if let Some(value) = o.get(k.as_str()) {
660                    parent.push(PathElement::Key(k.to_string(), None));
661                    iterate_path(schema, parent, &path[1..], value, f);
662                    parent.pop();
663                }
664            } else if let Value::Array(array) = data {
665                for (i, value) in array.iter().enumerate() {
666                    parent.push(PathElement::Index(i));
667                    iterate_path(schema, parent, path, value, f);
668                    parent.pop();
669                }
670            }
671        }
672        Some(PathElement::Fragment(name)) => {
673            if data.is_object_of_type(schema, name) {
674                // Note that (not unlike `Flatten`) we do not include the fragment in the `parent`
675                // path, because we want that path to be a "pure" response path. Fragments in path
676                // are used to essentially create a type-based choice in a "selection" path, but
677                // `parent` is a direct path to a specific position in the value and do not need
678                // fragments.
679                iterate_path(schema, parent, &path[1..], data, f);
680            } else if let Value::Array(array) = data {
681                for (i, value) in array.iter().enumerate() {
682                    parent.push(PathElement::Index(i));
683                    iterate_path(schema, parent, path, value, f);
684                    parent.pop();
685                }
686            }
687        }
688    }
689}
690
691fn iterate_path_mut<'a, F>(
692    schema: &Schema,
693    parent: &mut Path,
694    path: &'a [PathElement],
695    data: &'a mut Value,
696    f: &mut F,
697) where
698    F: FnMut(&Path, &'a mut Value),
699{
700    match path.first() {
701        None => f(parent, data),
702        Some(PathElement::Flatten(type_conditions)) => {
703            if let Some(array) = data.as_array_mut() {
704                for (i, value) in array.iter_mut().enumerate() {
705                    if let Some(tc) = type_conditions {
706                        if !tc.is_empty() {
707                            if let Value::Object(o) = value {
708                                if let Some(Value::String(type_name)) = o.get("__typename") {
709                                    if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
710                                        parent.push(PathElement::Index(i));
711                                        iterate_path_mut(schema, parent, &path[1..], value, f);
712                                        parent.pop();
713                                    }
714                                }
715                            }
716                        }
717                    } else {
718                        parent.push(PathElement::Index(i));
719                        iterate_path_mut(schema, parent, &path[1..], value, f);
720                        parent.pop();
721                    }
722                }
723            }
724        }
725        Some(PathElement::Index(i)) => {
726            if let Value::Array(a) = data {
727                if let Some(value) = a.get_mut(*i) {
728                    parent.push(PathElement::Index(*i));
729                    iterate_path_mut(schema, parent, &path[1..], value, f);
730                    parent.pop();
731                }
732            }
733        }
734        Some(PathElement::Key(k, type_conditions)) => {
735            if let Some(tc) = type_conditions {
736                if !tc.is_empty() {
737                    if let Value::Object(o) = data {
738                        if let Some(value) = o.get_mut(k.as_str()) {
739                            if let Some(Value::String(type_name)) = value.get("__typename") {
740                                if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
741                                    parent.push(PathElement::Key(k.to_string(), None));
742                                    iterate_path_mut(schema, parent, &path[1..], value, f);
743                                    parent.pop();
744                                }
745                            }
746                        }
747                    } else if let Value::Array(array) = data {
748                        for (i, value) in array.iter_mut().enumerate() {
749                            if let Value::Object(o) = value {
750                                if let Some(Value::String(type_name)) = o.get("__typename") {
751                                    if tc.iter().any(|tc| tc.as_str() == type_name.as_str()) {
752                                        parent.push(PathElement::Index(i));
753                                        iterate_path_mut(schema, parent, path, value, f);
754                                        parent.pop();
755                                    }
756                                }
757                            }
758                        }
759                    }
760                }
761            } else if let Value::Object(o) = data {
762                if let Some(value) = o.get_mut(k.as_str()) {
763                    parent.push(PathElement::Key(k.to_string(), None));
764                    iterate_path_mut(schema, parent, &path[1..], value, f);
765                    parent.pop();
766                }
767            } else if let Value::Array(array) = data {
768                for (i, value) in array.iter_mut().enumerate() {
769                    parent.push(PathElement::Index(i));
770                    iterate_path_mut(schema, parent, path, value, f);
771                    parent.pop();
772                }
773            }
774        }
775        Some(PathElement::Fragment(name)) => {
776            if data.is_object_of_type(schema, name) {
777                // Note that (not unlike `Flatten`) we do not include the fragment in the `parent`
778                // path, because we want that path to be a "pure" response path. Fragments in path
779                // are used to essentially create a type-based choice in a "selection" path, but
780                // `parent` is a direct path to a specific position in the value and do not need
781                // fragments.
782                iterate_path_mut(schema, parent, &path[1..], data, f);
783            } else if let Value::Array(array) = data {
784                for (i, value) in array.iter_mut().enumerate() {
785                    parent.push(PathElement::Index(i));
786                    iterate_path_mut(schema, parent, path, value, f);
787                    parent.pop();
788                }
789            }
790        }
791    }
792}
793
794/// A GraphQL path element that is composes of strings or numbers.
795/// e.g `/book/3/name`
796#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
797#[serde(untagged)]
798pub enum PathElement {
799    /// A path element that given an array will flatmap the content.
800    #[serde(
801        deserialize_with = "deserialize_flatten",
802        serialize_with = "serialize_flatten"
803    )]
804    Flatten(Option<TypeConditions>),
805
806    /// An index path element.
807    Index(usize),
808
809    /// A fragment application
810    #[serde(
811        deserialize_with = "deserialize_fragment",
812        serialize_with = "serialize_fragment"
813    )]
814    Fragment(String),
815
816    /// A key path element.
817    #[serde(deserialize_with = "deserialize_key", serialize_with = "serialize_key")]
818    Key(String, Option<TypeConditions>),
819}
820
821type TypeConditions = Vec<String>;
822
823#[derive(Clone, Debug, Eq, PartialEq)]
824pub enum ResponsePathElement<'a> {
825    /// An index path element.
826    Index(usize),
827
828    /// A key path element.
829    Key(&'a str),
830}
831
832fn deserialize_flatten<'de, D>(deserializer: D) -> Result<Option<TypeConditions>, D::Error>
833where
834    D: serde::Deserializer<'de>,
835{
836    deserializer.deserialize_str(FlattenVisitor)
837}
838
839struct FlattenVisitor;
840
841impl serde::de::Visitor<'_> for FlattenVisitor {
842    type Value = Option<TypeConditions>;
843
844    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
845        write!(
846            formatter,
847            "a string that is '@', potentially followed by type conditions"
848        )
849    }
850
851    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
852    where
853        E: serde::de::Error,
854    {
855        let (path_element, type_conditions) = split_path_element_and_type_conditions(s);
856        if path_element == "@" {
857            Ok(type_conditions)
858        } else {
859            Err(serde::de::Error::invalid_value(
860                serde::de::Unexpected::Str(s),
861                &self,
862            ))
863        }
864    }
865}
866
867fn serialize_flatten<S>(
868    type_conditions: &Option<TypeConditions>,
869    serializer: S,
870) -> Result<S::Ok, S::Error>
871where
872    S: serde::Serializer,
873{
874    let tc_string = if let Some(c) = type_conditions {
875        format!("|[{}]", c.join(","))
876    } else {
877        "".to_string()
878    };
879    let res = format!("@{}", tc_string);
880    serializer.serialize_str(res.as_str())
881}
882
883fn deserialize_key<'de, D>(deserializer: D) -> Result<(String, Option<TypeConditions>), D::Error>
884where
885    D: serde::Deserializer<'de>,
886{
887    deserializer.deserialize_str(KeyVisitor)
888}
889
890struct KeyVisitor;
891
892impl serde::de::Visitor<'_> for KeyVisitor {
893    type Value = (String, Option<TypeConditions>);
894
895    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
896        write!(
897            formatter,
898            "a string, potentially followed by type conditions"
899        )
900    }
901
902    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
903    where
904        E: serde::de::Error,
905    {
906        Ok(split_path_element_and_type_conditions(s))
907    }
908}
909
910fn serialize_key<S>(
911    key: &String,
912    type_conditions: &Option<TypeConditions>,
913    serializer: S,
914) -> Result<S::Ok, S::Error>
915where
916    S: serde::Serializer,
917{
918    let tc_string = if let Some(c) = type_conditions {
919        format!("|[{}]", c.join(","))
920    } else {
921        "".to_string()
922    };
923    let res = format!("{}{}", key, tc_string);
924    serializer.serialize_str(res.as_str())
925}
926
927fn deserialize_fragment<'de, D>(deserializer: D) -> Result<String, D::Error>
928where
929    D: serde::Deserializer<'de>,
930{
931    deserializer.deserialize_str(FragmentVisitor)
932}
933
934struct FragmentVisitor;
935
936impl serde::de::Visitor<'_> for FragmentVisitor {
937    type Value = String;
938
939    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
940        write!(formatter, "a string that begins with '... on '")
941    }
942
943    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
944    where
945        E: serde::de::Error,
946    {
947        s.strip_prefix(FRAGMENT_PREFIX)
948            .map(|v| v.to_string())
949            .ok_or_else(|| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self))
950    }
951}
952
953fn serialize_fragment<S>(name: &String, serializer: S) -> Result<S::Ok, S::Error>
954where
955    S: serde::Serializer,
956{
957    serializer.serialize_str(format!("{FRAGMENT_PREFIX}{name}").as_str())
958}
959
960fn flatten_from_str(s: &str) -> Result<PathElement, String> {
961    let (path_element, type_conditions) = split_path_element_and_type_conditions(s);
962    if path_element != "@" {
963        return Err("invalid flatten".to_string());
964    }
965    Ok(PathElement::Flatten(type_conditions))
966}
967
968fn key_from_str(s: &str) -> Result<PathElement, String> {
969    let (key, type_conditions) = split_path_element_and_type_conditions(s);
970    Ok(PathElement::Key(key, type_conditions))
971}
972
973/// A path into the result document.
974///
975/// This can be composed of strings and numbers
976#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
977#[serde(transparent)]
978pub struct Path(pub Vec<PathElement>);
979
980impl Path {
981    pub fn from_slice<T: AsRef<str>>(s: &[T]) -> Self {
982        Self(
983            s.iter()
984                .map(|x| x.as_ref())
985                .map(|s| {
986                    if let Ok(index) = s.parse::<usize>() {
987                        PathElement::Index(index)
988                    } else if s.contains('@') {
989                        flatten_from_str(s).unwrap_or(PathElement::Flatten(None))
990                    } else {
991                        s.strip_prefix(FRAGMENT_PREFIX).map_or_else(
992                            || key_from_str(s).unwrap_or(PathElement::Key(s.to_string(), None)),
993                            |name| PathElement::Fragment(name.to_string()),
994                        )
995                    }
996                })
997                .collect(),
998        )
999    }
1000
1001    pub fn from_response_slice(s: &[ResponsePathElement]) -> Self {
1002        Self(
1003            s.iter()
1004                .map(|x| match x {
1005                    ResponsePathElement::Index(index) => PathElement::Index(*index),
1006                    ResponsePathElement::Key(s) => PathElement::Key(s.to_string(), None),
1007                })
1008                .collect(),
1009        )
1010    }
1011
1012    pub fn iter(&self) -> impl Iterator<Item = &PathElement> {
1013        self.0.iter()
1014    }
1015
1016    pub fn is_empty(&self) -> bool {
1017        self.0.is_empty()
1018    }
1019
1020    pub fn len(&self) -> usize {
1021        self.0.len()
1022    }
1023
1024    pub fn empty() -> Path {
1025        Path(Default::default())
1026    }
1027
1028    pub fn parent(&self) -> Option<Path> {
1029        if self.is_empty() {
1030            None
1031        } else {
1032            Some(Path(self.iter().take(self.len() - 1).cloned().collect()))
1033        }
1034    }
1035
1036    pub fn join(&self, other: impl AsRef<Self>) -> Self {
1037        let other = other.as_ref();
1038        let mut new = Vec::with_capacity(self.len() + other.len());
1039        new.extend(self.iter().cloned());
1040        new.extend(other.iter().cloned());
1041        Path(new)
1042    }
1043
1044    pub fn push(&mut self, element: PathElement) {
1045        self.0.push(element)
1046    }
1047
1048    pub fn pop(&mut self) -> Option<PathElement> {
1049        self.0.pop()
1050    }
1051
1052    pub fn last(&self) -> Option<&PathElement> {
1053        self.0.last()
1054    }
1055
1056    pub fn last_key(&mut self) -> Option<String> {
1057        self.0.last().and_then(|elem| match elem {
1058            PathElement::Key(key, type_conditions) => {
1059                let mut tc = String::new();
1060                if let Some(c) = type_conditions {
1061                    tc = format!("|[{}]", c.join(","));
1062                };
1063                Some(format!("{}{}", key, tc))
1064            }
1065            _ => None,
1066        })
1067    }
1068
1069    pub fn starts_with(&self, other: &Path) -> bool {
1070        self.0.starts_with(&other.0[..])
1071    }
1072
1073    // Removes the empty key if at root (used for TypedConditions)
1074    pub fn remove_empty_key_root(&self) -> Self {
1075        if let Some(PathElement::Key(k, type_conditions)) = self.0.first() {
1076            if k.is_empty() && type_conditions.is_none() {
1077                return Path(self.iter().skip(1).cloned().collect());
1078            }
1079        }
1080
1081        self.clone()
1082    }
1083
1084    // Checks whether self and other are equal if PathElement::Flatten and PathElement::Index are
1085    // treated as equal
1086    pub fn equal_if_flattened(&self, other: &Path) -> bool {
1087        if self.len() != other.len() {
1088            return false;
1089        }
1090
1091        for (elem1, elem2) in self.iter().zip(other.iter()) {
1092            let equal_elements = match (elem1, elem2) {
1093                (PathElement::Index(_), PathElement::Flatten(_)) => true,
1094                (PathElement::Flatten(_), PathElement::Index(_)) => true,
1095                (elem1, elem2) => elem1 == elem2,
1096            };
1097            if !equal_elements {
1098                return false;
1099            }
1100        }
1101
1102        true
1103    }
1104}
1105
1106impl FromIterator<PathElement> for Path {
1107    fn from_iter<T: IntoIterator<Item = PathElement>>(iter: T) -> Self {
1108        Path(iter.into_iter().collect())
1109    }
1110}
1111
1112impl AsRef<Path> for Path {
1113    fn as_ref(&self) -> &Path {
1114        self
1115    }
1116}
1117
1118impl<T> From<T> for Path
1119where
1120    T: AsRef<str>,
1121{
1122    fn from(s: T) -> Self {
1123        Self(
1124            s.as_ref()
1125                .split('/')
1126                .map(|s| {
1127                    if let Ok(index) = s.parse::<usize>() {
1128                        PathElement::Index(index)
1129                    } else if s.contains('@') {
1130                        flatten_from_str(s).unwrap()
1131                    } else {
1132                        s.strip_prefix(FRAGMENT_PREFIX).map_or_else(
1133                            || key_from_str(s).unwrap_or(PathElement::Key(s.to_string(), None)),
1134                            |name| PathElement::Fragment(name.to_string()),
1135                        )
1136                    }
1137                })
1138                .collect(),
1139        )
1140    }
1141}
1142
1143impl fmt::Display for Path {
1144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1145        for element in self.iter() {
1146            write!(f, "/")?;
1147            match element {
1148                PathElement::Index(index) => write!(f, "{index}")?,
1149                PathElement::Key(key, type_conditions) => {
1150                    write!(f, "{key}")?;
1151                    if let Some(c) = type_conditions {
1152                        write!(f, "|[{}]", c.join(","))?;
1153                    };
1154                }
1155                PathElement::Flatten(type_conditions) => {
1156                    write!(f, "@")?;
1157                    if let Some(c) = type_conditions {
1158                        write!(f, "|[{}]", c.join(","))?;
1159                    };
1160                }
1161                PathElement::Fragment(name) => {
1162                    write!(f, "{FRAGMENT_PREFIX}{name}")?;
1163                }
1164            }
1165        }
1166        Ok(())
1167    }
1168}
1169
1170#[cfg(test)]
1171mod tests {
1172    use serde_json_bytes::json;
1173
1174    use super::*;
1175
1176    macro_rules! assert_is_subset {
1177        ($a:expr, $b:expr $(,)?) => {
1178            assert!($a.is_subset(&$b));
1179        };
1180    }
1181
1182    macro_rules! assert_is_not_subset {
1183        ($a:expr, $b:expr $(,)?) => {
1184            assert!(!$a.is_subset(&$b));
1185        };
1186    }
1187
1188    /// Functions that walk on path needs a schema to handle potential fragment (type conditions) in
1189    /// the path, and so we use the following simple schema for tests. Note however that tests that
1190    /// don't use fragments in the path essentially ignore this schema.
1191    fn test_schema() -> Schema {
1192        Schema::parse(
1193            r#"
1194           schema
1195             @link(url: "https://specs.apollo.dev/link/v1.0")
1196             @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1197           {
1198             query: Query
1199           }
1200
1201           directive @join__graph(name: String!, url: String!) on ENUM_VALUE
1202           directive @link( url: String as: String for: link__Purpose import: [link__Import]) repeatable on SCHEMA
1203           scalar link__Import
1204
1205           enum join__Graph {
1206             FAKE @join__graph(name:"fake" url: "http://localhost:4001/fake")
1207           }
1208
1209           enum link__Purpose {
1210             SECURITY
1211             EXECUTION
1212           }
1213
1214           type Query {
1215             i: [I]
1216           }
1217
1218           interface I {
1219             x: Int
1220           }
1221
1222           type A implements I {
1223             x: Int
1224           }
1225
1226           type B {
1227             y: Int
1228           }
1229        "#,
1230            &Default::default(),
1231        )
1232        .unwrap()
1233    }
1234
1235    fn select_values<'a>(
1236        schema: &Schema,
1237        path: &'a Path,
1238        data: &'a Value,
1239    ) -> Result<Vec<&'a Value>, FetchError> {
1240        let mut v = Vec::new();
1241        data.select_values_and_paths(schema, path, |_path, value| {
1242            v.push(value);
1243        });
1244        Ok(v)
1245    }
1246
1247    #[test]
1248    fn test_get_at_path() {
1249        let schema = test_schema();
1250        let json = json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}});
1251        let path = Path::from("obj/arr/1/prop1");
1252        let result = select_values(&schema, &path, &json).unwrap();
1253        assert_eq!(result, vec![&Value::Number(2.into())]);
1254    }
1255
1256    #[test]
1257    fn test_get_at_path_flatmap() {
1258        let schema = test_schema();
1259        let json = json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}});
1260        let path = Path::from("obj/arr/@");
1261        let result = select_values(&schema, &path, &json).unwrap();
1262        assert_eq!(result, vec![&json!({"prop1":1}), &json!({"prop1":2})]);
1263    }
1264
1265    #[test]
1266    fn test_get_at_path_flatmap_nested() {
1267        let schema = test_schema();
1268        let json = json!({
1269            "obj": {
1270                "arr": [
1271                    {
1272                        "prop1": [
1273                            {"prop2": {"prop3": 1}, "prop4": -1},
1274                            {"prop2": {"prop3": 2}, "prop4": -2},
1275                        ],
1276                    },
1277                    {
1278                        "prop1": [
1279                            {"prop2": {"prop3": 3}, "prop4": -3},
1280                            {"prop2": {"prop3": 4}, "prop4": -4},
1281                        ],
1282                    },
1283                ],
1284            },
1285        });
1286        let path = Path::from("obj/arr/@/prop1/@/prop2");
1287        let result = select_values(&schema, &path, &json).unwrap();
1288        assert_eq!(
1289            result,
1290            vec![
1291                &json!({"prop3":1}),
1292                &json!({"prop3":2}),
1293                &json!({"prop3":3}),
1294                &json!({"prop3":4}),
1295            ],
1296        );
1297    }
1298
1299    #[test]
1300    fn test_deep_merge() {
1301        let mut json = json!({"obj":{"arr":[{"prop1":1},{"prop2":2}]}});
1302        json.deep_merge(json!({"obj":{"arr":[{"prop1":2,"prop3":3},{"prop4":4}]}}));
1303        assert_eq!(
1304            json,
1305            json!({"obj":{"arr":[{"prop1":2, "prop3":3},{"prop2":2, "prop4":4}]}})
1306        );
1307    }
1308
1309    #[test]
1310    fn interface_typename_merging() {
1311        let schema = Schema::parse(
1312                r#"
1313            schema
1314                @link(url: "https://specs.apollo.dev/link/v1.0")
1315                @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
1316            {
1317                query: Query
1318            }
1319            directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
1320            directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
1321            directive @join__graph(name: String!, url: String!) on ENUM_VALUE
1322
1323            scalar link__Import
1324            scalar join__FieldSet
1325
1326            enum link__Purpose {
1327                SECURITY
1328                EXECUTION
1329            }
1330
1331            enum join__Graph {
1332                TEST @join__graph(name: "test", url: "http://localhost:4001/graphql")
1333            }
1334
1335            interface I {
1336                s: String
1337            }
1338
1339            type C implements I {
1340                s: String
1341            }
1342
1343            type Query {
1344                i: I
1345            }
1346        "#,
1347            &Default::default(),
1348        )
1349        .expect("valid schema");
1350        let mut response1 = json!({
1351            "__typename": "C"
1352        });
1353        let response2 = json!({
1354            "__typename": "I",
1355            "s": "data"
1356        });
1357
1358        response1.type_aware_deep_merge(response2, &schema);
1359
1360        assert_eq!(
1361            response1,
1362            json!({
1363                "__typename": "C",
1364                "s": "data"
1365            })
1366        );
1367    }
1368
1369    #[test]
1370    fn test_is_subset_eq() {
1371        assert_is_subset!(
1372            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1373            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1374        );
1375    }
1376
1377    #[test]
1378    fn test_is_subset_missing_pop() {
1379        assert_is_subset!(
1380            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1381            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1382        );
1383    }
1384
1385    #[test]
1386    fn test_is_subset_array_lengths_differ() {
1387        assert_is_not_subset!(
1388            json!({"obj":{"arr":[{"prop1":1}]}}),
1389            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1390        );
1391    }
1392
1393    #[test]
1394    fn test_is_subset_extra_prop() {
1395        assert_is_not_subset!(
1396            json!({"obj":{"arr":[{"prop1":1,"prop3":3},{"prop4":4}]}}),
1397            json!({"obj":{"arr":[{"prop1":1},{"prop4":4}]}}),
1398        );
1399    }
1400
1401    #[test]
1402    fn eq_and_ordered() {
1403        // test not objects
1404        assert!(json!([1, 2, 3]).eq_and_ordered(&json!([1, 2, 3])));
1405        assert!(!json!([1, 3, 2]).eq_and_ordered(&json!([1, 2, 3])));
1406
1407        // test objects not nested
1408        assert!(json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1409        assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":3})));
1410        assert!(!json!({"foo":1,"bar":2}).eq_and_ordered(&json!({"foo":1,"bar":2,"baz":3})));
1411        assert!(!json!({"foo":1,"bar":2,"baz":3}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1412        assert!(!json!({"bar":2,"foo":1}).eq_and_ordered(&json!({"foo":1,"bar":2})));
1413
1414        // test objects nested
1415        assert!(json!({"baz":{"foo":1,"bar":2}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}})));
1416        assert!(
1417            !json!({"baz":{"bar":2,"foo":1}}).eq_and_ordered(&json!({"baz":{"foo":1,"bar":2}}))
1418        );
1419        assert!(!json!([1,{"bar":2,"foo":1},2]).eq_and_ordered(&json!([1,{"foo":1,"bar":2},2])));
1420    }
1421
1422    #[test]
1423    fn test_from_path() {
1424        let json = json!([{"prop1":1},{"prop1":2}]);
1425        let path = Path::from("obj/arr");
1426        let result = Value::from_path(&path, json);
1427        assert_eq!(result, json!({"obj":{"arr":[{"prop1":1},{"prop1":2}]}}));
1428    }
1429
1430    #[test]
1431    fn test_from_path_index() {
1432        let json = json!({"prop1":1});
1433        let path = Path::from("obj/arr/1");
1434        let result = Value::from_path(&path, json);
1435        assert_eq!(result, json!({"obj":{"arr":[null, {"prop1":1}]}}));
1436    }
1437
1438    #[test]
1439    fn test_from_path_flatten() {
1440        let json = json!({"prop1":1});
1441        let path = Path::from("obj/arr/@/obj2");
1442        let result = Value::from_path(&path, json);
1443        assert_eq!(result, json!({"obj":{"arr":null}}));
1444    }
1445
1446    #[test]
1447    fn test_is_object_of_type() {
1448        let schema = test_schema();
1449
1450        // Basic matching
1451        assert!(json!({ "__typename": "A", "x": "42"}).is_object_of_type(&schema, "A"));
1452
1453        // Matching with subtyping
1454        assert!(json!({ "__typename": "A", "x": "42"}).is_object_of_type(&schema, "I"));
1455
1456        // Matching when missing __typename (see comment on the method declaration).
1457        assert!(json!({ "x": "42"}).is_object_of_type(&schema, "A"));
1458
1459        // Non-matching because not an object
1460        assert!(!json!([{ "__typename": "A", "x": "42"}]).is_object_of_type(&schema, "A"));
1461        assert!(!json!("foo").is_object_of_type(&schema, "I"));
1462        assert!(!json!(42).is_object_of_type(&schema, "I"));
1463
1464        // Non-matching because not of the asked type.
1465        assert!(!json!({ "__typename": "B", "y": "42"}).is_object_of_type(&schema, "A"));
1466        assert!(!json!({ "__typename": "B", "y": "42"}).is_object_of_type(&schema, "I"));
1467    }
1468
1469    #[test]
1470    fn test_get_at_path_with_conditions() {
1471        let schema = test_schema();
1472        let json = json!({
1473            "i": [
1474                {
1475                    "__typename": "A",
1476                    "x": 0,
1477                },
1478                {
1479                    "__typename": "B",
1480                    "y": 1,
1481                },
1482                {
1483                    "__typename": "B",
1484                    "y": 2,
1485                },
1486                {
1487                    "__typename": "A",
1488                    "x": 3,
1489                },
1490            ],
1491        });
1492        let path = Path::from("i/... on A");
1493        let result = select_values(&schema, &path, &json).unwrap();
1494        assert_eq!(
1495            result,
1496            vec![
1497                &json!({
1498                    "__typename": "A",
1499                    "x": 0,
1500                }),
1501                &json!({
1502                    "__typename": "A",
1503                    "x": 3,
1504                }),
1505            ],
1506        );
1507    }
1508
1509    #[test]
1510    fn path_serde_json() {
1511        let path: Path = serde_json::from_str(
1512            r#"[
1513            "k",
1514            "... on T",
1515            "@",
1516            "arr",
1517            3
1518        ]"#,
1519        )
1520        .unwrap();
1521        assert_eq!(
1522            path.0,
1523            vec![
1524                PathElement::Key("k".to_string(), None),
1525                PathElement::Fragment("T".to_string()),
1526                PathElement::Flatten(None),
1527                PathElement::Key("arr".to_string(), None),
1528                PathElement::Index(3),
1529            ]
1530        );
1531
1532        assert_eq!(
1533            serde_json::to_string(&path).unwrap(),
1534            "[\"k\",\"... on T\",\"@\",\"arr\",3]",
1535        );
1536    }
1537}