Skip to main content

apollo_federation/query_plan/
serializable_document.rs

1use std::sync::Arc;
2
3use apollo_compiler::ExecutableDocument;
4use apollo_compiler::Schema;
5use apollo_compiler::validation::Valid;
6use apollo_compiler::validation::WithErrors;
7use serde::Deserialize;
8use serde::Serialize;
9
10/// Like `Valid<ExecutableDocument>>` but can be (de)serialized as a string in GraphQL syntax.
11///
12/// The relevant schema is required to parse from string but not available during deserialization,
13/// so this contains a dual (either or both) “string” or “parsed” representation.
14/// Accessing the latter is fallible, and requires an explicit initialization step to provide the schema.
15#[derive(Clone)]
16pub struct SerializableDocument {
17    serialized: String,
18    /// Ideally this would be always present,
19    /// but we don’t have access to the relevant schema during `Deserialize`.
20    parsed: Option<Arc<Valid<ExecutableDocument>>>,
21}
22
23impl SerializableDocument {
24    pub fn from_string(serialized: impl Into<String>) -> Self {
25        Self {
26            serialized: serialized.into(),
27            parsed: None,
28        }
29    }
30
31    pub fn from_parsed(parsed: impl Into<Arc<Valid<ExecutableDocument>>>) -> Self {
32        let parsed = parsed.into();
33        Self {
34            serialized: parsed.serialize().no_indent().to_string(),
35            parsed: Some(parsed),
36        }
37    }
38
39    pub fn as_serialized(&self) -> &str {
40        &self.serialized
41    }
42
43    #[allow(clippy::result_large_err)]
44    pub fn init_parsed(
45        &mut self,
46        subgraph_schema: &Valid<Schema>,
47    ) -> Result<&Arc<Valid<ExecutableDocument>>, WithErrors<ExecutableDocument>> {
48        match &mut self.parsed {
49            Some(parsed) => Ok(parsed),
50            option => {
51                let parsed = Arc::new(ExecutableDocument::parse_and_validate(
52                    subgraph_schema,
53                    &self.serialized,
54                    "operation.graphql",
55                )?);
56                Ok(option.insert(parsed))
57            }
58        }
59    }
60
61    pub fn as_parsed(
62        &self,
63    ) -> Result<&Arc<Valid<ExecutableDocument>>, SerializableDocumentNotInitialized> {
64        self.parsed
65            .as_ref()
66            .ok_or(SerializableDocumentNotInitialized)
67    }
68}
69
70#[derive(Debug, thiserror::Error)]
71#[error("Failed to call `SerializableDocument::init_parsed` after creating a query plan")]
72pub struct SerializableDocumentNotInitialized;
73
74impl Serialize for SerializableDocument {
75    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
76    where
77        S: serde::Serializer,
78    {
79        self.as_serialized().serialize(serializer)
80    }
81}
82
83impl<'de> Deserialize<'de> for SerializableDocument {
84    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85    where
86        D: serde::Deserializer<'de>,
87    {
88        Ok(Self::from_string(String::deserialize(deserializer)?))
89    }
90}
91
92impl PartialEq for SerializableDocument {
93    fn eq(&self, other: &Self) -> bool {
94        self.as_serialized() == other.as_serialized()
95    }
96}
97
98impl std::fmt::Debug for SerializableDocument {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        std::fmt::Debug::fmt(self.as_serialized(), f)
101    }
102}
103
104impl std::fmt::Display for SerializableDocument {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        std::fmt::Display::fmt(self.as_serialized(), f)
107    }
108}