1mod definitions;
2mod directives;
3mod enums;
4mod extensions;
5mod field_types;
6mod fields;
7mod ids;
8mod keys;
9mod linked_schemas;
10mod strings;
11mod top;
12mod unions;
13mod view;
14mod walker;
15
16pub(crate) use self::{
17 definitions::{DefinitionId, DefinitionKind, DefinitionWalker},
18 directives::*,
19 extensions::*,
20 field_types::*,
21 fields::*,
22 ids::*,
23 keys::*,
24 linked_schemas::*,
25 strings::{StringId, StringWalker},
26 top::*,
27 view::View,
28 walker::Walker,
29};
30
31use crate::VecExt;
32use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
33
34pub struct Subgraphs {
36 pub(super) strings: strings::Strings,
37 subgraphs: Vec<Subgraph>,
38 definitions: definitions::Definitions,
39 directives: directives::Directives,
40 enums: enums::Enums,
41 fields: fields::Fields,
42 field_types: field_types::FieldTypes,
43 keys: keys::Keys,
44 unions: unions::Unions,
45 linked_schemas: linked_schemas::LinkedSchemas,
46
47 ingestion_diagnostics: crate::Diagnostics,
48
49 extensions: Vec<ExtensionRecord>,
50
51 definition_names: BTreeMap<(StringId, SubgraphId), DefinitionId>,
59}
60
61impl Default for Subgraphs {
62 fn default() -> Self {
63 let mut strings = strings::Strings::default();
64 BUILTIN_SCALARS.into_iter().for_each(|scalar| {
65 strings.intern(scalar);
66 });
67
68 let composite_schema_extension = ExtensionRecord {
69 url: strings.intern("https://specs.grafbase.com/composite-schema/v1"),
70 name: strings.intern("_composite_schema"),
72 };
73
74 Self {
75 strings,
76 subgraphs: Default::default(),
77 definitions: Default::default(),
78 directives: Default::default(),
79 enums: Default::default(),
80 fields: Default::default(),
81 field_types: Default::default(),
82 keys: Default::default(),
83 unions: Default::default(),
84 ingestion_diagnostics: Default::default(),
85 definition_names: Default::default(),
86 linked_schemas: Default::default(),
87 extensions: vec![composite_schema_extension],
88 }
89 }
90}
91
92const BUILTIN_SCALARS: [&str; 5] = ["ID", "String", "Boolean", "Int", "Float"];
93
94#[derive(Debug)]
96pub struct IngestError {
97 error: cynic_parser::Error,
98 report: String,
99}
100
101impl std::error::Error for IngestError {
102 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
103 self.error.source()
104 }
105}
106
107impl std::fmt::Display for IngestError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 if f.alternate() {
110 return self.report.fmt(f);
111 }
112 std::fmt::Display::fmt(&self.error, f)
113 }
114}
115
116impl Subgraphs {
117 pub fn ingest(&mut self, subgraph_schema: &cynic_parser::TypeSystemDocument, name: &str, url: Option<&str>) {
119 crate::ingest_subgraph::ingest_subgraph(subgraph_schema, name, url, self);
120 }
121
122 pub fn ingest_str(&mut self, subgraph_schema: &str, name: &str, url: Option<&str>) -> Result<(), IngestError> {
124 let subgraph_schema =
125 cynic_parser::parse_type_system_document(subgraph_schema).map_err(|error| IngestError {
126 report: error.to_report(subgraph_schema).to_string(),
127 error,
128 })?;
129 crate::ingest_subgraph::ingest_subgraph(&subgraph_schema, name, url, self);
130 Ok(())
131 }
132
133 #[cfg(feature = "grafbase-extensions")]
135 pub fn ingest_loaded_extensions(&mut self, extensions: impl IntoIterator<Item = crate::LoadedExtension>) {
136 self.extensions
137 .extend(extensions.into_iter().map(|ext| ExtensionRecord {
138 url: self.strings.intern(ext.url),
139 name: self.strings.intern(ext.name),
140 }));
141 }
142
143 pub fn is_empty(&self) -> bool {
145 self.subgraphs.is_empty()
146 }
147
148 pub(crate) fn iter_definition_groups<'a>(&'a self, mut compose_fn: impl FnMut(&[DefinitionWalker<'a>])) {
152 let mut key = None;
153 let mut buf = Vec::new();
154
155 for ((name, subgraph), definition) in &self.definition_names {
156 if Some(name) != key {
157 compose_fn(&buf);
159 buf.clear();
160 key = Some(name);
161 }
162
163 if self.is_root_type(*subgraph, *definition) {
166 continue; }
168
169 buf.push(self.walk(*definition));
170 }
171
172 compose_fn(&buf)
173 }
174
175 pub(crate) fn push_ingestion_diagnostic(&mut self, subgraph: SubgraphId, message: String) {
176 self.ingestion_diagnostics
177 .push_fatal(format!("[{}]: {message}", self.walk_subgraph(subgraph).name().as_str()));
178 }
179
180 pub(crate) fn push_ingestion_warning(&mut self, subgraph: SubgraphId, message: String) {
181 self.ingestion_diagnostics
182 .push_warning(format!("[{}]: {message}", self.walk_subgraph(subgraph).name().as_str()));
183 }
184
185 pub(crate) fn walk<Id>(&self, id: Id) -> Walker<'_, Id> {
186 Walker { id, subgraphs: self }
187 }
188
189 pub(crate) fn iter_builtin_scalars(&self) -> impl Iterator<Item = StringWalker<'_>> + '_ {
191 BUILTIN_SCALARS
192 .into_iter()
193 .map(|name| self.strings.lookup(name).expect("all built in scalars to be interned"))
194 .map(|string| self.walk(string))
195 }
196
197 pub(crate) fn emit_ingestion_diagnostics(&self, diagnostics: &mut crate::Diagnostics) {
198 diagnostics.clone_all_from(&self.ingestion_diagnostics);
199 }
200}