1use super::functions::*;
6use std::collections::HashMap;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub enum GQLIntrospectionKind {
11 Scalar,
12 Object,
13 Interface,
14 Union,
15 Enum,
16 InputObject,
17 List,
18 NonNull,
19}
20#[allow(dead_code)]
21pub struct GQLRateLimitDirective {
22 pub max_calls: u32,
23 pub window_seconds: u32,
24 pub per: GQLRateLimitPer,
25}
26impl GQLRateLimitDirective {
27 #[allow(dead_code)]
28 pub fn new(max_calls: u32, window_seconds: u32) -> Self {
29 GQLRateLimitDirective {
30 max_calls,
31 window_seconds,
32 per: GQLRateLimitPer::User,
33 }
34 }
35 #[allow(dead_code)]
36 pub fn emit(&self) -> String {
37 let per = match self.per {
38 GQLRateLimitPer::Ip => "IP",
39 GQLRateLimitPer::User => "USER",
40 GQLRateLimitPer::Global => "GLOBAL",
41 };
42 format!(
43 "@rateLimit(limit: {}, duration: {}, per: \"{}\")",
44 self.max_calls, self.window_seconds, per
45 )
46 }
47}
48#[allow(dead_code)]
49#[derive(Debug, Clone)]
50pub struct GQLInputObject {
51 pub name: String,
52 pub fields: Vec<GQLInputField>,
53 pub description: Option<String>,
54 pub directives: Vec<String>,
55}
56impl GQLInputObject {
57 #[allow(dead_code)]
58 pub fn new(name: impl Into<String>) -> Self {
59 GQLInputObject {
60 name: name.into(),
61 fields: Vec::new(),
62 description: None,
63 directives: Vec::new(),
64 }
65 }
66 #[allow(dead_code)]
67 pub fn add_field(&mut self, field: GQLInputField) {
68 self.fields.push(field);
69 }
70 #[allow(dead_code)]
71 pub fn emit(&self) -> String {
72 let mut out = String::new();
73 if let Some(ref d) = self.description {
74 out.push_str(&format!("\"\"\"\n{}\n\"\"\"\n", d));
75 }
76 out.push_str(&format!("input {} {{\n", self.name));
77 for f in &self.fields {
78 if let Some(ref d) = f.description {
79 out.push_str(&format!(" \"\"\"{}\"\"\"\n", d));
80 }
81 out.push_str(&format!(" {}: {}", f.name, emit_gql_type(&f.ty)));
82 if let Some(ref dv) = f.default_value {
83 out.push_str(&format!(" = {}", dv));
84 }
85 out.push('\n');
86 }
87 out.push('}');
88 out
89 }
90}
91#[allow(dead_code)]
92#[derive(Debug, Clone)]
93pub struct GQLErrorLocation {
94 pub line: u32,
95 pub column: u32,
96}
97#[allow(dead_code)]
98#[derive(Debug, Clone)]
99pub struct GQLPersistedQuery {
100 pub hash: String,
101 pub query: String,
102 pub version: u32,
103}
104impl GQLPersistedQuery {
105 #[allow(dead_code)]
106 pub fn new(query: impl Into<String>) -> Self {
107 let q = query.into();
108 let hash = format!("{:x}", q.len() * 31 + 7);
109 GQLPersistedQuery {
110 hash,
111 query: q,
112 version: 1,
113 }
114 }
115 #[allow(dead_code)]
116 pub fn emit_apq_extension(&self) -> String {
117 format!(
118 "{{\"persistedQuery\":{{\"version\":{},\"sha256Hash\":\"{}\"}}}}",
119 self.version, self.hash
120 )
121 }
122}
123#[allow(dead_code)]
124pub struct GQLTypeNameMap {
125 pub(super) mapping: std::collections::HashMap<String, String>,
126}
127impl GQLTypeNameMap {
128 #[allow(dead_code)]
129 pub fn new() -> Self {
130 let mut m = std::collections::HashMap::new();
131 m.insert("Int".to_string(), "i32".to_string());
132 m.insert("Float".to_string(), "f64".to_string());
133 m.insert("String".to_string(), "String".to_string());
134 m.insert("Boolean".to_string(), "bool".to_string());
135 m.insert("ID".to_string(), "String".to_string());
136 GQLTypeNameMap { mapping: m }
137 }
138 #[allow(dead_code)]
139 pub fn add_mapping(&mut self, gql: impl Into<String>, rust: impl Into<String>) {
140 self.mapping.insert(gql.into(), rust.into());
141 }
142 #[allow(dead_code)]
143 pub fn lookup(&self, gql_name: &str) -> Option<&String> {
144 self.mapping.get(gql_name)
145 }
146 #[allow(dead_code)]
147 pub fn len(&self) -> usize {
148 self.mapping.len()
149 }
150}
151#[allow(dead_code)]
152#[derive(Debug, Clone)]
153pub struct GQLCacheControl {
154 pub max_age_seconds: u32,
155 pub scope: GQLCacheScope,
156 pub inherit_max_age: bool,
157}
158impl GQLCacheControl {
159 #[allow(dead_code)]
160 pub fn public(max_age: u32) -> Self {
161 GQLCacheControl {
162 max_age_seconds: max_age,
163 scope: GQLCacheScope::Public,
164 inherit_max_age: false,
165 }
166 }
167 #[allow(dead_code)]
168 pub fn private(max_age: u32) -> Self {
169 GQLCacheControl {
170 max_age_seconds: max_age,
171 scope: GQLCacheScope::Private,
172 inherit_max_age: false,
173 }
174 }
175 #[allow(dead_code)]
176 pub fn emit_directive(&self) -> String {
177 let scope = match self.scope {
178 GQLCacheScope::Public => "PUBLIC",
179 GQLCacheScope::Private => "PRIVATE",
180 };
181 format!(
182 "@cacheControl(maxAge: {}, scope: {})",
183 self.max_age_seconds, scope
184 )
185 }
186}
187#[allow(dead_code)]
188#[derive(Debug, Clone)]
189pub struct GQLDataloader {
190 pub batch_size: usize,
191 pub cache_ttl_ms: u64,
192 pub entity_type: String,
193}
194impl GQLDataloader {
195 #[allow(dead_code)]
196 pub fn new(entity_type: impl Into<String>) -> Self {
197 GQLDataloader {
198 batch_size: 100,
199 cache_ttl_ms: 5000,
200 entity_type: entity_type.into(),
201 }
202 }
203 #[allow(dead_code)]
204 pub fn emit_ts_loader(&self) -> String {
205 format!(
206 "const {}Loader = new DataLoader<string, {}>(\n async (ids) => load{}ByIds(ids),\n {{ maxBatchSize: {} }}\n);",
207 self.entity_type.to_lowercase(), self.entity_type, self.entity_type, self
208 .batch_size
209 )
210 }
211}
212#[allow(dead_code)]
213#[derive(Debug, Clone)]
214pub struct GQLDeprecation {
215 pub field_name: String,
216 pub reason: String,
217 pub since_version: Option<String>,
218 pub replacement: Option<String>,
219}
220impl GQLDeprecation {
221 #[allow(dead_code)]
222 pub fn new(field_name: impl Into<String>, reason: impl Into<String>) -> Self {
223 GQLDeprecation {
224 field_name: field_name.into(),
225 reason: reason.into(),
226 since_version: None,
227 replacement: None,
228 }
229 }
230 #[allow(dead_code)]
231 pub fn emit_directive(&self) -> String {
232 format!("@deprecated(reason: \"{}\")", self.reason)
233 }
234}
235#[allow(dead_code)]
236#[derive(Debug, Clone)]
237pub struct GQLOperation {
238 pub op_type: GQLOperationType,
239 pub name: Option<String>,
240 pub variables: Vec<GQLVariable>,
241 pub selections: Vec<GQLSelectionField>,
242}
243impl GQLOperation {
244 #[allow(dead_code)]
245 pub fn query(name: impl Into<String>) -> Self {
246 GQLOperation {
247 op_type: GQLOperationType::Query,
248 name: Some(name.into()),
249 variables: Vec::new(),
250 selections: Vec::new(),
251 }
252 }
253 #[allow(dead_code)]
254 pub fn mutation(name: impl Into<String>) -> Self {
255 GQLOperation {
256 op_type: GQLOperationType::Mutation,
257 name: Some(name.into()),
258 variables: Vec::new(),
259 selections: Vec::new(),
260 }
261 }
262 #[allow(dead_code)]
263 pub fn emit(&self) -> String {
264 let op_name = match self.op_type {
265 GQLOperationType::Query => "query",
266 GQLOperationType::Mutation => "mutation",
267 GQLOperationType::Subscription => "subscription",
268 };
269 let mut out = String::new();
270 out.push_str(op_name);
271 if let Some(ref name) = self.name {
272 out.push(' ');
273 out.push_str(name);
274 }
275 if !self.variables.is_empty() {
276 out.push('(');
277 let vars: Vec<String> = self
278 .variables
279 .iter()
280 .map(|v| format!("${}: {}", v.name, emit_gql_type(&v.ty)))
281 .collect();
282 out.push_str(&vars.join(", "));
283 out.push(')');
284 }
285 out.push_str(" {\n");
286 for sel in &self.selections {
287 out.push_str(&sel.emit(1));
288 out.push('\n');
289 }
290 out.push('}');
291 out
292 }
293}
294#[allow(dead_code)]
295pub struct GQLCodegen {
296 pub(super) schema: GQLSchemaExtended,
297 pub(super) language: GQLCodegenTarget,
298}
299impl GQLCodegen {
300 #[allow(dead_code)]
301 pub fn new(schema: GQLSchemaExtended, target: GQLCodegenTarget) -> Self {
302 GQLCodegen {
303 schema,
304 language: target,
305 }
306 }
307 #[allow(dead_code)]
308 pub fn generate_types(&self) -> String {
309 match self.language {
310 GQLCodegenTarget::TypeScript => self.gen_typescript(),
311 GQLCodegenTarget::Rust => self.gen_rust(),
312 GQLCodegenTarget::Go => self.gen_go(),
313 GQLCodegenTarget::Python => self.gen_python(),
314 }
315 }
316 #[allow(dead_code)]
317 pub(super) fn gen_typescript(&self) -> String {
318 let mut out = String::new();
319 for obj in &self.schema.objects {
320 out.push_str(&format!("export interface {} {{\n", obj.name));
321 for f in &obj.fields {
322 out.push_str(&format!(" {}: {};\n", f.name, ts_type(&f.ty)));
323 }
324 out.push_str("}\n\n");
325 }
326 for e in &self.schema.enums {
327 out.push_str(&format!("export enum {} {{\n", e.name));
328 for v in &e.values {
329 out.push_str(&format!(" {} = \"{}\",\n", v.name, v.name));
330 }
331 out.push_str("}\n\n");
332 }
333 out
334 }
335 #[allow(dead_code)]
336 pub(super) fn gen_rust(&self) -> String {
337 let mut out = String::new();
338 for obj in &self.schema.objects {
339 out.push_str("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\n");
340 out.push_str(&format!("pub struct {} {{\n", obj.name));
341 for f in &obj.fields {
342 out.push_str(&format!(" pub {}: {},\n", f.name, rust_type(&f.ty)));
343 }
344 out.push_str("}\n\n");
345 }
346 out
347 }
348 #[allow(dead_code)]
349 pub(super) fn gen_go(&self) -> String {
350 let mut out = String::new();
351 for obj in &self.schema.objects {
352 out.push_str(&format!("type {} struct {{\n", obj.name));
353 for f in &obj.fields {
354 let fname: String = f
355 .name
356 .chars()
357 .enumerate()
358 .map(|(i, c)| {
359 if i == 0 {
360 c.to_uppercase().next().unwrap_or(c)
361 } else {
362 c
363 }
364 })
365 .collect();
366 out.push_str(&format!(
367 " {} {} `json:\"{}\"`\n",
368 fname,
369 go_type(&f.ty),
370 f.name
371 ));
372 }
373 out.push_str("}\n\n");
374 }
375 out
376 }
377 #[allow(dead_code)]
378 pub(super) fn gen_python(&self) -> String {
379 let mut out = String::new();
380 out.push_str("from dataclasses import dataclass\nfrom typing import Optional, List\n\n");
381 for obj in &self.schema.objects {
382 out.push_str("@dataclass\n");
383 out.push_str(&format!("class {}:\n", obj.name));
384 for f in &obj.fields {
385 out.push_str(&format!(" {}: {}\n", f.name, py_type(&f.ty)));
386 }
387 out.push('\n');
388 }
389 out
390 }
391}
392#[derive(Debug, Clone)]
394pub struct GQLSchema {
395 pub types: Vec<GQLObject>,
396 pub query_type: String,
397 pub mutation_type: Option<String>,
398}
399#[allow(dead_code)]
400pub struct GQLLiveQueryExtension {
401 pub throttle_ms: u64,
402 pub invalidation_keys: Vec<String>,
403}
404impl GQLLiveQueryExtension {
405 #[allow(dead_code)]
406 pub fn new(throttle_ms: u64) -> Self {
407 GQLLiveQueryExtension {
408 throttle_ms,
409 invalidation_keys: Vec::new(),
410 }
411 }
412 #[allow(dead_code)]
413 pub fn add_key(&mut self, key: impl Into<String>) {
414 self.invalidation_keys.push(key.into());
415 }
416 #[allow(dead_code)]
417 pub fn emit_extension_header(&self) -> String {
418 format!("@live(throttle: {})", self.throttle_ms)
419 }
420}
421#[derive(Debug, Clone)]
423pub struct GQLObject {
424 pub name: String,
425 pub fields: Vec<GQLField>,
426 pub implements: Vec<String>,
428 pub description: Option<String>,
430}
431#[allow(dead_code)]
432#[derive(Debug, Clone, Default)]
433pub struct GQLSchemaExtended {
434 pub objects: Vec<GQLObject>,
435 pub interfaces: Vec<GQLInterface>,
436 pub unions: Vec<GQLUnion>,
437 pub enums: Vec<GQLEnumDef>,
438 pub scalars: Vec<GQLScalar>,
439 pub input_objects: Vec<GQLInputObject>,
440 pub directives: Vec<GQLDirective>,
441 pub query_type: Option<String>,
442 pub mutation_type: Option<String>,
443 pub subscription_type: Option<String>,
444}
445impl GQLSchemaExtended {
446 #[allow(dead_code)]
447 pub fn new() -> Self {
448 Self::default()
449 }
450 #[allow(dead_code)]
451 pub fn add_object(&mut self, obj: GQLObject) {
452 self.objects.push(obj);
453 }
454 #[allow(dead_code)]
455 pub fn add_interface(&mut self, iface: GQLInterface) {
456 self.interfaces.push(iface);
457 }
458 #[allow(dead_code)]
459 pub fn add_scalar(&mut self, scalar: GQLScalar) {
460 self.scalars.push(scalar);
461 }
462 #[allow(dead_code)]
463 pub fn set_query_type(&mut self, name: impl Into<String>) {
464 self.query_type = Some(name.into());
465 }
466 #[allow(dead_code)]
467 pub fn emit(&self) -> String {
468 let mut out = String::new();
469 for scalar in &self.scalars {
470 out.push_str(&scalar.emit());
471 out.push_str("\n\n");
472 }
473 for iface in &self.interfaces {
474 out.push_str(&iface.emit());
475 out.push_str("\n\n");
476 }
477 for obj in &self.objects {
478 out.push_str(&format!("type {} {{\n", obj.name));
479 for f in &obj.fields {
480 out.push_str(&format!(" {}: {}\n", f.name, emit_gql_type(&f.ty)));
481 }
482 out.push_str("}\n\n");
483 }
484 for u in &self.unions {
485 out.push_str(&u.emit());
486 out.push_str("\n\n");
487 }
488 for e in &self.enums {
489 out.push_str(&format!("enum {} {{\n", e.name));
490 for v in &e.values {
491 out.push_str(&format!(" {}\n", v.name));
492 }
493 out.push_str("}\n\n");
494 }
495 for inp in &self.input_objects {
496 out.push_str(&inp.emit());
497 out.push_str("\n\n");
498 }
499 if self.query_type.is_some() || self.mutation_type.is_some() {
500 out.push_str("schema {\n");
501 if let Some(ref q) = self.query_type {
502 out.push_str(&format!(" query: {}\n", q));
503 }
504 if let Some(ref m) = self.mutation_type {
505 out.push_str(&format!(" mutation: {}\n", m));
506 }
507 if let Some(ref s) = self.subscription_type {
508 out.push_str(&format!(" subscription: {}\n", s));
509 }
510 out.push('}');
511 }
512 out
513 }
514}
515#[allow(dead_code)]
516#[derive(Debug, Clone)]
517pub struct GQLError {
518 pub message: String,
519 pub locations: Vec<GQLErrorLocation>,
520 pub path: Vec<String>,
521 pub extensions: std::collections::HashMap<String, String>,
522}
523impl GQLError {
524 #[allow(dead_code)]
525 pub fn new(message: impl Into<String>) -> Self {
526 GQLError {
527 message: message.into(),
528 locations: Vec::new(),
529 path: Vec::new(),
530 extensions: std::collections::HashMap::new(),
531 }
532 }
533 #[allow(dead_code)]
534 pub fn with_location(mut self, line: u32, column: u32) -> Self {
535 self.locations.push(GQLErrorLocation { line, column });
536 self
537 }
538 #[allow(dead_code)]
539 pub fn emit_json(&self) -> String {
540 let locs: Vec<String> = self
541 .locations
542 .iter()
543 .map(|l| format!("{{\"line\":{},\"column\":{}}}", l.line, l.column))
544 .collect();
545 format!(
546 "{{\"message\":\"{}\",\"locations\":[{}]}}",
547 self.message,
548 locs.join(",")
549 )
550 }
551}
552#[derive(Debug, Clone, PartialEq, Eq)]
554pub enum GQLType {
555 Scalar(String),
557 Object(String),
559 List(Box<GQLType>),
561 NonNull(Box<GQLType>),
563 Enum(String),
565 Interface(String),
567 Union(String),
569}
570#[allow(dead_code)]
571pub struct GQLSchemaComparator {
572 pub old_schema: GQLSchemaExtended,
573 pub new_schema: GQLSchemaExtended,
574}
575impl GQLSchemaComparator {
576 #[allow(dead_code)]
577 pub fn new(old_schema: GQLSchemaExtended, new_schema: GQLSchemaExtended) -> Self {
578 GQLSchemaComparator {
579 old_schema,
580 new_schema,
581 }
582 }
583 #[allow(dead_code)]
584 pub fn added_types(&self) -> Vec<String> {
585 let old_names: std::collections::HashSet<_> =
586 self.old_schema.objects.iter().map(|o| &o.name).collect();
587 self.new_schema
588 .objects
589 .iter()
590 .filter(|o| !old_names.contains(&o.name))
591 .map(|o| o.name.clone())
592 .collect()
593 }
594 #[allow(dead_code)]
595 pub fn removed_types(&self) -> Vec<String> {
596 let new_names: std::collections::HashSet<_> =
597 self.new_schema.objects.iter().map(|o| &o.name).collect();
598 self.old_schema
599 .objects
600 .iter()
601 .filter(|o| !new_names.contains(&o.name))
602 .map(|o| o.name.clone())
603 .collect()
604 }
605 #[allow(dead_code)]
606 pub fn is_breaking_change(&self) -> bool {
607 !self.removed_types().is_empty()
608 }
609 #[allow(dead_code)]
610 pub fn generate_changelog(&self) -> String {
611 let added = self.added_types();
612 let removed = self.removed_types();
613 let mut log = String::new();
614 for t in &added {
615 log.push_str(&format!("+ Added type: {}\n", t));
616 }
617 for t in &removed {
618 log.push_str(&format!("- Removed type: {}\n", t));
619 }
620 log
621 }
622}
623#[allow(dead_code)]
624#[derive(Debug, Clone)]
625pub struct GQLVariable {
626 pub name: String,
627 pub ty: GQLType,
628 pub default_value: Option<String>,
629}
630#[allow(dead_code)]
631pub struct GQLPersistedQueryStore {
632 pub(super) store: std::collections::HashMap<String, GQLPersistedQuery>,
633}
634impl GQLPersistedQueryStore {
635 #[allow(dead_code)]
636 pub fn new() -> Self {
637 GQLPersistedQueryStore {
638 store: std::collections::HashMap::new(),
639 }
640 }
641 #[allow(dead_code)]
642 pub fn register(&mut self, pq: GQLPersistedQuery) -> &str {
643 let hash = pq.hash.clone();
644 self.store.insert(hash.clone(), pq);
645 &self.store[&hash].hash
646 }
647 #[allow(dead_code)]
648 pub fn lookup(&self, hash: &str) -> Option<&GQLPersistedQuery> {
649 self.store.get(hash)
650 }
651 #[allow(dead_code)]
652 pub fn count(&self) -> usize {
653 self.store.len()
654 }
655}
656#[allow(dead_code)]
657#[derive(Debug, Clone)]
658pub struct GQLQueryComplexity {
659 pub max_depth: u32,
660 pub max_breadth: u32,
661 pub max_complexity: u64,
662 pub per_field_cost: u32,
663 pub per_list_multiplier: u32,
664}
665impl GQLQueryComplexity {
666 #[allow(dead_code)]
667 pub fn default_limits() -> Self {
668 GQLQueryComplexity {
669 max_depth: 10,
670 max_breadth: 50,
671 max_complexity: 1000,
672 per_field_cost: 1,
673 per_list_multiplier: 10,
674 }
675 }
676 #[allow(dead_code)]
677 pub fn calculate_selection_complexity(
678 &self,
679 selections: &[GQLSelectionField],
680 depth: u32,
681 ) -> u64 {
682 if depth > self.max_depth {
683 return u64::MAX;
684 }
685 let mut total = 0u64;
686 for sel in selections {
687 total = total.saturating_add(self.per_field_cost as u64);
688 if !sel.selection_set.is_empty() {
689 total = total.saturating_add(
690 self.calculate_selection_complexity(&sel.selection_set, depth + 1)
691 .saturating_mul(self.per_list_multiplier as u64),
692 );
693 }
694 }
695 total
696 }
697}
698#[allow(dead_code)]
699#[derive(Debug, Clone)]
700pub enum GQLDirectiveValue {
701 String(String),
702 Int(i64),
703 Float(f64),
704 Bool(bool),
705 Enum(String),
706 Null,
707 List(Vec<GQLDirectiveValue>),
708 Object(Vec<(String, GQLDirectiveValue)>),
709}
710#[allow(dead_code)]
711#[derive(Debug, Clone)]
712pub struct GQLConnection {
713 pub node_type: String,
714 pub edge_fields: Vec<GQLField>,
715 pub connection_fields: Vec<GQLField>,
716}
717impl GQLConnection {
718 #[allow(dead_code)]
719 pub fn new(node_type: impl Into<String>) -> Self {
720 GQLConnection {
721 node_type: node_type.into(),
722 edge_fields: Vec::new(),
723 connection_fields: Vec::new(),
724 }
725 }
726 #[allow(dead_code)]
727 pub fn emit_edge_type(&self) -> String {
728 let edge_name = format!("{}Edge", self.node_type);
729 let mut out = format!("type {} {{\n", edge_name);
730 out.push_str(&format!(" node: {}\n", self.node_type));
731 out.push_str(" cursor: String!\n");
732 for f in &self.edge_fields {
733 out.push_str(&format!(" {}: {}\n", f.name, emit_gql_type(&f.ty)));
734 }
735 out.push('}');
736 out
737 }
738 #[allow(dead_code)]
739 pub fn emit_connection_type(&self) -> String {
740 let conn_name = format!("{}Connection", self.node_type);
741 let edge_name = format!("{}Edge", self.node_type);
742 let mut out = format!("type {} {{\n", conn_name);
743 out.push_str(&format!(" edges: [{}]\n", edge_name));
744 out.push_str(" pageInfo: PageInfo!\n");
745 out.push_str(" totalCount: Int!\n");
746 for f in &self.connection_fields {
747 out.push_str(&format!(" {}: {}\n", f.name, emit_gql_type(&f.ty)));
748 }
749 out.push('}');
750 out
751 }
752}
753#[allow(dead_code)]
754#[derive(Debug, Clone)]
755pub struct GQLIntrospectionType {
756 pub kind: GQLIntrospectionKind,
757 pub name: Option<String>,
758 pub description: Option<String>,
759 pub fields: Vec<GQLIntrospectionField>,
760 pub interfaces: Vec<String>,
761 pub possible_types: Vec<String>,
762 pub enum_values: Vec<String>,
763 pub input_fields: Vec<String>,
764 pub of_type: Option<Box<GQLIntrospectionType>>,
765}
766#[allow(dead_code)]
767#[derive(Debug, Clone)]
768pub struct GQLIntrospectionField {
769 pub name: String,
770 pub description: Option<String>,
771 pub args: Vec<GQLInputField>,
772 pub ty: GQLIntrospectionType,
773 pub is_deprecated: bool,
774 pub deprecation_reason: Option<String>,
775}
776#[allow(dead_code)]
777pub struct GQLTypeSystemDocument {
778 pub type_defs: Vec<String>,
779 pub directives: Vec<GQLDirective>,
780 pub scalars: Vec<GQLScalar>,
781 pub schema_extensions: Vec<String>,
782}
783impl GQLTypeSystemDocument {
784 #[allow(dead_code)]
785 pub fn new() -> Self {
786 GQLTypeSystemDocument {
787 type_defs: Vec::new(),
788 directives: Vec::new(),
789 scalars: Vec::new(),
790 schema_extensions: Vec::new(),
791 }
792 }
793 #[allow(dead_code)]
794 pub fn extend_type(&mut self, type_name: &str, fields: &[&str]) {
795 let mut ext = format!("extend type {} {{\n", type_name);
796 for field in fields {
797 ext.push_str(&format!(" {}\n", field));
798 }
799 ext.push('}');
800 self.schema_extensions.push(ext);
801 }
802 #[allow(dead_code)]
803 pub fn emit(&self) -> String {
804 let mut parts = Vec::new();
805 for s in &self.scalars {
806 parts.push(s.emit());
807 }
808 for d in &self.directives {
809 parts.push(d.emit());
810 }
811 for td in &self.type_defs {
812 parts.push(td.clone());
813 }
814 for ext in &self.schema_extensions {
815 parts.push(ext.clone());
816 }
817 parts.join("\n\n")
818 }
819}
820#[allow(dead_code)]
821#[derive(Debug, Clone, Default)]
822pub struct GQLSchemaBuilder {
823 pub(super) objects: Vec<GQLObject>,
824 pub(super) interfaces: Vec<GQLInterface>,
825 pub(super) unions: Vec<GQLUnion>,
826 pub(super) enums: Vec<GQLEnumDef>,
827 pub(super) scalars: Vec<GQLScalar>,
828 pub(super) input_objects: Vec<GQLInputObject>,
829 pub(super) custom_directives: Vec<GQLDirective>,
830 pub(super) query_type: Option<String>,
831 pub(super) mutation_type: Option<String>,
832}
833impl GQLSchemaBuilder {
834 #[allow(dead_code)]
835 pub fn new() -> Self {
836 Self::default()
837 }
838 #[allow(dead_code)]
839 pub fn object(mut self, obj: GQLObject) -> Self {
840 self.objects.push(obj);
841 self
842 }
843 #[allow(dead_code)]
844 pub fn interface(mut self, iface: GQLInterface) -> Self {
845 self.interfaces.push(iface);
846 self
847 }
848 #[allow(dead_code)]
849 pub fn union_type(mut self, u: GQLUnion) -> Self {
850 self.unions.push(u);
851 self
852 }
853 #[allow(dead_code)]
854 pub fn enum_type(mut self, e: GQLEnumDef) -> Self {
855 self.enums.push(e);
856 self
857 }
858 #[allow(dead_code)]
859 pub fn scalar(mut self, s: GQLScalar) -> Self {
860 self.scalars.push(s);
861 self
862 }
863 #[allow(dead_code)]
864 pub fn input(mut self, inp: GQLInputObject) -> Self {
865 self.input_objects.push(inp);
866 self
867 }
868 #[allow(dead_code)]
869 pub fn query_type(mut self, name: impl Into<String>) -> Self {
870 self.query_type = Some(name.into());
871 self
872 }
873 #[allow(dead_code)]
874 pub fn mutation_type(mut self, name: impl Into<String>) -> Self {
875 self.mutation_type = Some(name.into());
876 self
877 }
878 #[allow(dead_code)]
879 pub fn build(self) -> GQLSchemaExtended {
880 GQLSchemaExtended {
881 objects: self.objects,
882 interfaces: self.interfaces,
883 unions: self.unions,
884 enums: self.enums,
885 scalars: self.scalars,
886 input_objects: self.input_objects,
887 directives: self.custom_directives,
888 query_type: self.query_type,
889 mutation_type: self.mutation_type,
890 subscription_type: None,
891 }
892 }
893}
894#[allow(dead_code)]
895pub struct GQLBatchRequest {
896 pub operations: Vec<GQLOperation>,
897 pub max_batch_size: usize,
898}
899impl GQLBatchRequest {
900 #[allow(dead_code)]
901 pub fn new(max_size: usize) -> Self {
902 GQLBatchRequest {
903 operations: Vec::new(),
904 max_batch_size: max_size,
905 }
906 }
907 #[allow(dead_code)]
908 pub fn add(&mut self, op: GQLOperation) -> bool {
909 if self.operations.len() >= self.max_batch_size {
910 return false;
911 }
912 self.operations.push(op);
913 true
914 }
915 #[allow(dead_code)]
916 pub fn emit_batch_json(&self) -> String {
917 let ops: Vec<String> = self
918 .operations
919 .iter()
920 .map(|op| {
921 format!(
922 "{{\"query\":\"{}\"}}",
923 op.emit().replace('\n', " ").replace('"', "\\\"")
924 )
925 })
926 .collect();
927 format!("[{}]", ops.join(","))
928 }
929}
930#[allow(dead_code)]
931#[derive(Debug, Clone, PartialEq)]
932pub enum GQLDirectiveLocation {
933 Field,
934 FieldDefinition,
935 Object,
936 Interface,
937 Union,
938 Enum,
939 EnumValue,
940 InputObject,
941 InputFieldDefinition,
942 Argument,
943 Schema,
944 Scalar,
945 Query,
946 Mutation,
947 Subscription,
948 FragmentDefinition,
949 FragmentSpread,
950 InlineFragment,
951}
952#[derive(Debug, Clone)]
954pub struct GQLEnumDef {
955 pub name: String,
956 pub values: Vec<GQLEnumValue>,
957}
958#[allow(dead_code)]
959#[derive(Debug, Clone)]
960pub struct GQLStreamDirective {
961 pub label: Option<String>,
962 pub initial_count: u32,
963 pub if_condition: Option<String>,
964}
965impl GQLStreamDirective {
966 #[allow(dead_code)]
967 pub fn new(initial_count: u32) -> Self {
968 GQLStreamDirective {
969 label: None,
970 initial_count,
971 if_condition: None,
972 }
973 }
974 #[allow(dead_code)]
975 pub fn emit(&self) -> String {
976 let mut args = vec![format!("initialCount: {}", self.initial_count)];
977 if let Some(ref label) = self.label {
978 args.push(format!("label: \"{}\"", label));
979 }
980 format!("@stream({})", args.join(", "))
981 }
982}
983#[allow(dead_code)]
984#[derive(Debug, Clone)]
985pub enum GQLOperationType {
986 Query,
987 Mutation,
988 Subscription,
989}
990#[allow(dead_code)]
991pub struct GQLSdlPrinter {
992 pub(super) indent_size: usize,
993 pub(super) include_descriptions: bool,
994 pub(super) include_deprecated: bool,
995}
996impl GQLSdlPrinter {
997 #[allow(dead_code)]
998 pub fn new() -> Self {
999 GQLSdlPrinter {
1000 indent_size: 2,
1001 include_descriptions: true,
1002 include_deprecated: true,
1003 }
1004 }
1005 #[allow(dead_code)]
1006 pub fn with_indent(mut self, size: usize) -> Self {
1007 self.indent_size = size;
1008 self
1009 }
1010 #[allow(dead_code)]
1011 pub fn without_descriptions(mut self) -> Self {
1012 self.include_descriptions = false;
1013 self
1014 }
1015 #[allow(dead_code)]
1016 pub fn print_object(&self, obj: &GQLObject) -> String {
1017 let indent = " ".repeat(self.indent_size);
1018 let mut out = format!("type {} {{\n", obj.name);
1019 for field in &obj.fields {
1020 if self.include_descriptions {
1021 if let Some(ref d) = field.description {
1022 out.push_str(&format!("{}\"\"\"{}\"\"\" \n", indent, d));
1023 }
1024 }
1025 out.push_str(&format!(
1026 "{}{}: {}\n",
1027 indent,
1028 field.name,
1029 emit_gql_type(&field.ty)
1030 ));
1031 }
1032 out.push('}');
1033 out
1034 }
1035 #[allow(dead_code)]
1036 pub fn print_enum(&self, e: &GQLEnumDef) -> String {
1037 let indent = " ".repeat(self.indent_size);
1038 let mut out = format!("enum {} {{\n", e.name);
1039 for v in &e.values {
1040 out.push_str(&format!("{}{}\n", indent, v.name));
1041 }
1042 out.push('}');
1043 out
1044 }
1045 #[allow(dead_code)]
1046 pub fn print_schema(&self, schema: &GQLSchemaExtended) -> String {
1047 let mut parts = Vec::new();
1048 for s in &schema.scalars {
1049 parts.push(s.emit());
1050 }
1051 for iface in &schema.interfaces {
1052 parts.push(iface.emit());
1053 }
1054 for obj in &schema.objects {
1055 parts.push(self.print_object(obj));
1056 }
1057 for u in &schema.unions {
1058 parts.push(u.emit());
1059 }
1060 for e in &schema.enums {
1061 parts.push(self.print_enum(e));
1062 }
1063 for inp in &schema.input_objects {
1064 parts.push(inp.emit());
1065 }
1066 parts.join("\n\n")
1067 }
1068}
1069#[allow(dead_code)]
1070pub enum GQLRateLimitPer {
1071 Ip,
1072 User,
1073 Global,
1074}
1075#[allow(dead_code)]
1076pub struct GQLIntrospectionQuery;
1077impl GQLIntrospectionQuery {
1078 #[allow(dead_code)]
1079 pub fn full_introspection_query() -> &'static str {
1080 "query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } }"
1081 }
1082 #[allow(dead_code)]
1083 pub fn type_query(type_name: &str) -> String {
1084 format!(
1085 "{{ __type(name: \"{}\") {{ name kind description fields {{ name type {{ name kind }} }} }} }}",
1086 type_name
1087 )
1088 }
1089}
1090#[allow(dead_code)]
1091#[derive(Debug, Clone)]
1092pub struct GQLDirective {
1093 pub name: String,
1094 pub locations: Vec<GQLDirectiveLocation>,
1095 pub args: Vec<GQLDirectiveArg>,
1096 pub is_repeatable: bool,
1097 pub description: Option<String>,
1098}
1099impl GQLDirective {
1100 #[allow(dead_code)]
1101 pub fn new(name: impl Into<String>) -> Self {
1102 GQLDirective {
1103 name: name.into(),
1104 locations: Vec::new(),
1105 args: Vec::new(),
1106 is_repeatable: false,
1107 description: None,
1108 }
1109 }
1110 #[allow(dead_code)]
1111 pub fn with_location(mut self, loc: GQLDirectiveLocation) -> Self {
1112 self.locations.push(loc);
1113 self
1114 }
1115 #[allow(dead_code)]
1116 pub fn with_arg(mut self, arg: GQLDirectiveArg) -> Self {
1117 self.args.push(arg);
1118 self
1119 }
1120 #[allow(dead_code)]
1121 pub fn repeatable(mut self) -> Self {
1122 self.is_repeatable = true;
1123 self
1124 }
1125 #[allow(dead_code)]
1126 pub fn emit(&self) -> String {
1127 let mut out = String::new();
1128 if let Some(ref desc) = self.description {
1129 out.push_str(&format!("\"\"\"{}\"\"\"\n", desc));
1130 }
1131 out.push_str(&format!("directive @{}", self.name));
1132 if !self.args.is_empty() {
1133 out.push('(');
1134 for (i, arg) in self.args.iter().enumerate() {
1135 if i > 0 {
1136 out.push_str(", ");
1137 }
1138 out.push_str(&arg.name);
1139 }
1140 out.push(')');
1141 }
1142 if self.is_repeatable {
1143 out.push_str(" repeatable");
1144 }
1145 out.push_str(" on ");
1146 let locs: Vec<&str> = self
1147 .locations
1148 .iter()
1149 .map(|l| match l {
1150 GQLDirectiveLocation::Field => "FIELD",
1151 GQLDirectiveLocation::FieldDefinition => "FIELD_DEFINITION",
1152 GQLDirectiveLocation::Object => "OBJECT",
1153 GQLDirectiveLocation::Interface => "INTERFACE",
1154 GQLDirectiveLocation::Union => "UNION",
1155 GQLDirectiveLocation::Enum => "ENUM",
1156 GQLDirectiveLocation::EnumValue => "ENUM_VALUE",
1157 GQLDirectiveLocation::InputObject => "INPUT_OBJECT",
1158 GQLDirectiveLocation::InputFieldDefinition => "INPUT_FIELD_DEFINITION",
1159 GQLDirectiveLocation::Argument => "ARGUMENT_DEFINITION",
1160 GQLDirectiveLocation::Schema => "SCHEMA",
1161 GQLDirectiveLocation::Scalar => "SCALAR",
1162 GQLDirectiveLocation::Query => "QUERY",
1163 GQLDirectiveLocation::Mutation => "MUTATION",
1164 GQLDirectiveLocation::Subscription => "SUBSCRIPTION",
1165 GQLDirectiveLocation::FragmentDefinition => "FRAGMENT_DEFINITION",
1166 GQLDirectiveLocation::FragmentSpread => "FRAGMENT_SPREAD",
1167 GQLDirectiveLocation::InlineFragment => "INLINE_FRAGMENT",
1168 })
1169 .collect();
1170 out.push_str(&locs.join(" | "));
1171 out
1172 }
1173}
1174#[allow(dead_code)]
1175pub struct GQLSubscriptionBuilder {
1176 pub(super) operations: Vec<GQLOperation>,
1177 pub(super) reconnect_delay_ms: u64,
1178 pub(super) max_reconnect_attempts: u32,
1179}
1180impl GQLSubscriptionBuilder {
1181 #[allow(dead_code)]
1182 pub fn new() -> Self {
1183 GQLSubscriptionBuilder {
1184 operations: Vec::new(),
1185 reconnect_delay_ms: 1000,
1186 max_reconnect_attempts: 5,
1187 }
1188 }
1189 #[allow(dead_code)]
1190 pub fn add_operation(&mut self, op: GQLOperation) {
1191 self.operations.push(op);
1192 }
1193 #[allow(dead_code)]
1194 pub fn set_reconnect_delay(&mut self, ms: u64) {
1195 self.reconnect_delay_ms = ms;
1196 }
1197 #[allow(dead_code)]
1198 pub fn build_ws_message(&self, op: &GQLOperation) -> String {
1199 format!(
1200 "{{\"type\":\"subscribe\",\"payload\":{{\"query\":\"{}\"}}}}",
1201 op.emit().replace('\n', "\\n")
1202 )
1203 }
1204}
1205#[allow(dead_code)]
1206#[derive(Debug, Clone)]
1207pub struct GQLUnion {
1208 pub name: String,
1209 pub members: Vec<String>,
1210 pub description: Option<String>,
1211}
1212impl GQLUnion {
1213 #[allow(dead_code)]
1214 pub fn new(name: impl Into<String>) -> Self {
1215 GQLUnion {
1216 name: name.into(),
1217 members: Vec::new(),
1218 description: None,
1219 }
1220 }
1221 #[allow(dead_code)]
1222 pub fn add_member(&mut self, member: impl Into<String>) {
1223 self.members.push(member.into());
1224 }
1225 #[allow(dead_code)]
1226 pub fn emit(&self) -> String {
1227 let mut out = String::new();
1228 if let Some(ref d) = self.description {
1229 out.push_str(&format!("\"\"\"{}\"\"\"\n", d));
1230 }
1231 out.push_str(&format!(
1232 "union {} = {}",
1233 self.name,
1234 self.members.join(" | ")
1235 ));
1236 out
1237 }
1238}
1239#[allow(dead_code)]
1240#[derive(Debug, Clone)]
1241pub struct GQLInterface {
1242 pub name: String,
1243 pub fields: Vec<GQLField>,
1244 pub implements: Vec<String>,
1245 pub description: Option<String>,
1246}
1247impl GQLInterface {
1248 #[allow(dead_code)]
1249 pub fn new(name: impl Into<String>) -> Self {
1250 GQLInterface {
1251 name: name.into(),
1252 fields: Vec::new(),
1253 implements: Vec::new(),
1254 description: None,
1255 }
1256 }
1257 #[allow(dead_code)]
1258 pub fn add_field(&mut self, field: GQLField) {
1259 self.fields.push(field);
1260 }
1261 #[allow(dead_code)]
1262 pub fn emit(&self) -> String {
1263 let mut out = String::new();
1264 if let Some(ref d) = self.description {
1265 out.push_str(&format!("\"\"\"\n{}\n\"\"\"\n", d));
1266 }
1267 out.push_str(&format!("interface {}", self.name));
1268 if !self.implements.is_empty() {
1269 out.push_str(&format!(" implements {}", self.implements.join(" & ")));
1270 }
1271 out.push_str(" {\n");
1272 for f in &self.fields {
1273 out.push_str(&format!(" {}: {}\n", f.name, emit_gql_type(&f.ty)));
1274 }
1275 out.push('}');
1276 out
1277 }
1278}
1279#[allow(dead_code)]
1280#[derive(Debug, Clone)]
1281pub struct GQLDirectiveArg {
1282 pub name: String,
1283 pub value: GQLDirectiveValue,
1284}
1285#[allow(dead_code)]
1286pub struct GQLSchemaRegistry {
1287 pub(super) schemas: std::collections::HashMap<String, GQLSchemaExtended>,
1288}
1289impl GQLSchemaRegistry {
1290 #[allow(dead_code)]
1291 pub fn new() -> Self {
1292 GQLSchemaRegistry {
1293 schemas: std::collections::HashMap::new(),
1294 }
1295 }
1296 #[allow(dead_code)]
1297 pub fn register(&mut self, name: impl Into<String>, schema: GQLSchemaExtended) {
1298 self.schemas.insert(name.into(), schema);
1299 }
1300 #[allow(dead_code)]
1301 pub fn get(&self, name: &str) -> Option<&GQLSchemaExtended> {
1302 self.schemas.get(name)
1303 }
1304 #[allow(dead_code)]
1305 pub fn list_names(&self) -> Vec<&String> {
1306 self.schemas.keys().collect()
1307 }
1308 #[allow(dead_code)]
1309 pub fn merge(&mut self, base: &str, overlay: &str) -> Option<GQLSchemaExtended> {
1310 let base_schema = self.schemas.get(base)?.clone();
1311 let overlay_schema = self.schemas.get(overlay)?.clone();
1312 let mut merged = base_schema;
1313 for obj in overlay_schema.objects {
1314 merged.objects.push(obj);
1315 }
1316 for iface in overlay_schema.interfaces {
1317 merged.interfaces.push(iface);
1318 }
1319 Some(merged)
1320 }
1321}
1322#[allow(dead_code)]
1323#[derive(Debug, Clone)]
1324pub struct GQLInputField {
1325 pub name: String,
1326 pub ty: GQLType,
1327 pub default_value: Option<String>,
1328 pub description: Option<String>,
1329 pub directives: Vec<String>,
1330}
1331#[allow(dead_code)]
1332#[derive(Debug, Clone)]
1333pub struct GQLNullabilityAnnotation {
1334 pub field_path: Vec<String>,
1335 pub nullable: bool,
1336}
1337#[derive(Debug, Clone)]
1339pub struct GQLEnumValue {
1340 pub name: String,
1341 pub description: Option<String>,
1342}
1343#[allow(dead_code)]
1344#[derive(Debug, Clone)]
1345pub enum GQLCodegenTarget {
1346 TypeScript,
1347 Rust,
1348 Go,
1349 Python,
1350}
1351#[allow(dead_code)]
1352#[derive(Debug, Clone)]
1353pub struct GQLResolverSignature {
1354 pub type_name: String,
1355 pub field_name: String,
1356 pub args: Vec<GQLField>,
1357 pub return_type: GQLType,
1358 pub is_async: bool,
1359}
1360impl GQLResolverSignature {
1361 #[allow(dead_code)]
1362 pub fn new(
1363 type_name: impl Into<String>,
1364 field_name: impl Into<String>,
1365 return_type: GQLType,
1366 ) -> Self {
1367 GQLResolverSignature {
1368 type_name: type_name.into(),
1369 field_name: field_name.into(),
1370 args: Vec::new(),
1371 return_type,
1372 is_async: true,
1373 }
1374 }
1375 #[allow(dead_code)]
1376 pub fn emit_ts_signature(&self) -> String {
1377 let args_str = self
1378 .args
1379 .iter()
1380 .map(|f| format!("{}: {}", f.name, ts_type(&f.ty)))
1381 .collect::<Vec<_>>()
1382 .join(", ");
1383 let ret = ts_type(&self.return_type);
1384 if self.is_async {
1385 format!("async {}({}) Promise<{}>", self.field_name, args_str, ret)
1386 } else {
1387 format!("{}({}) {}", self.field_name, args_str, ret)
1388 }
1389 }
1390}
1391#[derive(Debug, Clone)]
1393pub struct GQLField {
1394 pub name: String,
1395 pub ty: GQLType,
1396 pub nullable: bool,
1398 pub description: Option<String>,
1399 pub args: Vec<GQLFieldArg>,
1401}
1402#[allow(dead_code)]
1404#[derive(Debug, Clone)]
1405pub struct GQLFieldArg {
1406 pub name: String,
1407 pub ty: GQLType,
1408 pub default_value: Option<String>,
1409}
1410pub struct GQLBackend;
1412impl GQLBackend {
1413 pub fn emit_type(&self, ty: &GQLType) -> String {
1415 match ty {
1416 GQLType::Scalar(name) => name.clone(),
1417 GQLType::Object(name) => name.clone(),
1418 GQLType::Enum(name) => name.clone(),
1419 GQLType::Interface(name) => name.clone(),
1420 GQLType::Union(name) => name.clone(),
1421 GQLType::List(inner) => format!("[{}]", self.emit_type(inner)),
1422 GQLType::NonNull(inner) => format!("{}!", self.emit_type(inner)),
1423 }
1424 }
1425 pub fn emit_field(&self, field: &GQLField) -> String {
1427 let mut ty_str = self.emit_type(&field.ty);
1428 if !field.nullable {
1429 if !ty_str.ends_with('!') {
1430 ty_str.push('!');
1431 }
1432 }
1433 let mut s = String::new();
1434 if let Some(desc) = &field.description {
1435 s.push_str(&format!(" \"\"\"{}\"\"\"\n", desc));
1436 }
1437 s.push_str(&format!(" {}: {}", field.name, ty_str));
1438 s
1439 }
1440 pub fn emit_object(&self, obj: &GQLObject) -> String {
1442 let mut s = String::new();
1443 s.push_str(&format!("type {}", obj.name));
1444 if !obj.implements.is_empty() {
1445 s.push_str(&format!(" implements {}", obj.implements.join(" & ")));
1446 }
1447 s.push_str(" {\n");
1448 for field in &obj.fields {
1449 s.push_str(&self.emit_field(field));
1450 s.push('\n');
1451 }
1452 s.push('}');
1453 s
1454 }
1455 pub fn emit_enum(&self, e: &GQLEnumDef) -> String {
1457 let mut s = format!("enum {} {{\n", e.name);
1458 for val in &e.values {
1459 if let Some(desc) = &val.description {
1460 s.push_str(&format!(" \"\"\"{}\"\"\"\n", desc));
1461 }
1462 s.push_str(&format!(" {}\n", val.name));
1463 }
1464 s.push('}');
1465 s
1466 }
1467 pub fn emit_schema(&self, schema: &GQLSchema) -> String {
1469 let mut s = String::new();
1470 s.push_str("schema {\n");
1471 s.push_str(&format!(" query: {}\n", schema.query_type));
1472 if let Some(ref mt) = schema.mutation_type {
1473 s.push_str(&format!(" mutation: {}\n", mt));
1474 }
1475 s.push_str("}\n\n");
1476 for obj in &schema.types {
1477 s.push_str(&self.emit_object(obj));
1478 s.push_str("\n\n");
1479 }
1480 s.trim_end().to_string()
1481 }
1482 pub fn generate_resolver_stubs(&self, schema: &GQLSchema) -> String {
1484 let mut s = String::new();
1485 for obj in &schema.types {
1486 s.push_str(&format!("// Resolvers for {}\n", obj.name));
1487 for field in &obj.fields {
1488 let ret = self.emit_type(&field.ty);
1489 s.push_str(&format!(
1490 "fn resolve_{}_{}(ctx: &Context) -> {} {{\n todo!()\n}}\n",
1491 obj.name.to_ascii_lowercase(),
1492 field.name,
1493 ret
1494 ));
1495 }
1496 s.push('\n');
1497 }
1498 s
1499 }
1500 pub fn schema_from_triples(
1502 &self,
1503 query_type: &str,
1504 triples: &[(&str, &str, &str)],
1505 ) -> GQLSchema {
1506 use std::collections::BTreeMap;
1507 let mut map: BTreeMap<String, Vec<GQLField>> = BTreeMap::new();
1508 for (type_name, field_name, scalar) in triples {
1509 map.entry(type_name.to_string())
1510 .or_default()
1511 .push(GQLField {
1512 name: field_name.to_string(),
1513 ty: GQLType::Scalar(scalar.to_string()),
1514 nullable: true,
1515 description: None,
1516 args: vec![],
1517 });
1518 }
1519 let types = map
1520 .into_iter()
1521 .map(|(name, fields)| GQLObject {
1522 name,
1523 fields,
1524 implements: vec![],
1525 description: None,
1526 })
1527 .collect();
1528 GQLSchema {
1529 types,
1530 query_type: query_type.to_string(),
1531 mutation_type: None,
1532 }
1533 }
1534}
1535#[allow(dead_code)]
1536pub struct GQLMockDataGenerator {
1537 pub(super) seed: u64,
1538}
1539impl GQLMockDataGenerator {
1540 #[allow(dead_code)]
1541 pub fn new(seed: u64) -> Self {
1542 GQLMockDataGenerator { seed }
1543 }
1544 #[allow(dead_code)]
1545 pub fn generate_for_type(&mut self, ty: &GQLType) -> String {
1546 match ty {
1547 GQLType::Scalar(s) if s == "String" || s == "ID" => {
1548 format!("\"mock_string_{}\"", self.seed)
1549 }
1550 GQLType::Scalar(s) if s == "Int" => {
1551 self.seed += 1;
1552 format!("{}", self.seed % 1000)
1553 }
1554 GQLType::Scalar(s) if s == "Float" => {
1555 format!("{:.2}", (self.seed as f64) * 0.1)
1556 }
1557 GQLType::Scalar(s) if s == "Boolean" => {
1558 if self.seed % 2 == 0 {
1559 "true".to_string()
1560 } else {
1561 "false".to_string()
1562 }
1563 }
1564 GQLType::List(inner) => format!("[{}]", self.generate_for_type(inner)),
1565 GQLType::NonNull(inner) => self.generate_for_type(inner),
1566 _ => "null".to_string(),
1567 }
1568 }
1569}
1570#[allow(dead_code)]
1571#[derive(Debug, Clone)]
1572pub enum GQLCacheScope {
1573 Public,
1574 Private,
1575}
1576#[allow(dead_code)]
1577#[derive(Debug, Clone)]
1578pub struct GQLScalar {
1579 pub name: String,
1580 pub description: Option<String>,
1581 pub directives: Vec<String>,
1582}
1583impl GQLScalar {
1584 #[allow(dead_code)]
1585 pub fn new(name: impl Into<String>) -> Self {
1586 GQLScalar {
1587 name: name.into(),
1588 description: None,
1589 directives: Vec::new(),
1590 }
1591 }
1592 #[allow(dead_code)]
1593 pub fn emit(&self) -> String {
1594 let mut out = String::new();
1595 if let Some(ref d) = self.description {
1596 out.push_str(&format!("\"\"\"{}\"\"\"\n", d));
1597 }
1598 out.push_str(&format!("scalar {}", self.name));
1599 if !self.directives.is_empty() {
1600 out.push(' ');
1601 out.push_str(
1602 &self
1603 .directives
1604 .iter()
1605 .map(|d| format!("@{}", d))
1606 .collect::<Vec<_>>()
1607 .join(" "),
1608 );
1609 }
1610 out
1611 }
1612}
1613#[allow(dead_code)]
1614#[derive(Debug, Clone)]
1615pub struct GQLDeferDirective {
1616 pub label: Option<String>,
1617 pub if_condition: Option<String>,
1618}
1619impl GQLDeferDirective {
1620 #[allow(dead_code)]
1621 pub fn new() -> Self {
1622 GQLDeferDirective {
1623 label: None,
1624 if_condition: None,
1625 }
1626 }
1627 #[allow(dead_code)]
1628 pub fn with_label(mut self, label: impl Into<String>) -> Self {
1629 self.label = Some(label.into());
1630 self
1631 }
1632 #[allow(dead_code)]
1633 pub fn emit(&self) -> String {
1634 let mut parts = vec!["@defer".to_string()];
1635 let mut args = Vec::new();
1636 if let Some(ref label) = self.label {
1637 args.push(format!("label: \"{}\"", label));
1638 }
1639 if let Some(ref cond) = self.if_condition {
1640 args.push(format!("if: {}", cond));
1641 }
1642 if !args.is_empty() {
1643 parts.push(format!("({})", args.join(", ")));
1644 }
1645 parts.join("")
1646 }
1647}
1648#[allow(dead_code)]
1649#[derive(Debug, Clone)]
1650pub struct GQLFragment {
1651 pub name: String,
1652 pub on_type: String,
1653 pub selections: Vec<GQLSelectionField>,
1654 pub directives: Vec<String>,
1655}
1656impl GQLFragment {
1657 #[allow(dead_code)]
1658 pub fn new(name: impl Into<String>, on_type: impl Into<String>) -> Self {
1659 GQLFragment {
1660 name: name.into(),
1661 on_type: on_type.into(),
1662 selections: Vec::new(),
1663 directives: Vec::new(),
1664 }
1665 }
1666 #[allow(dead_code)]
1667 pub fn emit(&self) -> String {
1668 let mut out = format!("fragment {} on {} {{\n", self.name, self.on_type);
1669 for sel in &self.selections {
1670 out.push_str(&sel.emit(1));
1671 out.push('\n');
1672 }
1673 out.push('}');
1674 out
1675 }
1676}
1677#[allow(dead_code)]
1678#[derive(Debug, Clone)]
1679pub struct GQLResponse {
1680 pub data: Option<String>,
1681 pub errors: Vec<GQLError>,
1682}
1683impl GQLResponse {
1684 #[allow(dead_code)]
1685 pub fn success(data: impl Into<String>) -> Self {
1686 GQLResponse {
1687 data: Some(data.into()),
1688 errors: Vec::new(),
1689 }
1690 }
1691 #[allow(dead_code)]
1692 pub fn error(err: GQLError) -> Self {
1693 GQLResponse {
1694 data: None,
1695 errors: vec![err],
1696 }
1697 }
1698 #[allow(dead_code)]
1699 pub fn is_success(&self) -> bool {
1700 self.errors.is_empty()
1701 }
1702}
1703#[allow(dead_code)]
1704#[derive(Debug, Clone)]
1705pub struct GQLPaginationArgs {
1706 pub first: Option<u32>,
1707 pub last: Option<u32>,
1708 pub after: Option<String>,
1709 pub before: Option<String>,
1710}
1711#[allow(dead_code)]
1712pub struct GQLValidator {
1713 pub(super) schema: GQLSchemaExtended,
1714 pub(super) errors: Vec<String>,
1715}
1716impl GQLValidator {
1717 #[allow(dead_code)]
1718 pub fn new(schema: GQLSchemaExtended) -> Self {
1719 GQLValidator {
1720 schema,
1721 errors: Vec::new(),
1722 }
1723 }
1724 #[allow(dead_code)]
1725 pub fn validate_object(&mut self, obj: &GQLObject) {
1726 if obj.name.is_empty() {
1727 self.errors.push("Object type must have a name".to_string());
1728 }
1729 if obj.fields.is_empty() {
1730 self.errors.push(format!(
1731 "Object type '{}' must have at least one field",
1732 obj.name
1733 ));
1734 }
1735 for field in &obj.fields {
1736 self.validate_field_name(&field.name);
1737 }
1738 }
1739 #[allow(dead_code)]
1740 pub fn validate_field_name(&mut self, name: &str) {
1741 if name.starts_with("__") {
1742 self.errors
1743 .push(format!("Field name '{}' must not begin with '__'", name));
1744 }
1745 }
1746 #[allow(dead_code)]
1747 pub fn is_valid(&self) -> bool {
1748 self.errors.is_empty()
1749 }
1750 #[allow(dead_code)]
1751 pub fn errors(&self) -> &[String] {
1752 &self.errors
1753 }
1754}
1755#[allow(dead_code)]
1756pub struct GQLFederationExtension {
1757 pub service_name: String,
1758 pub keys: Vec<String>,
1759 pub externals: Vec<String>,
1760 pub requires: Vec<String>,
1761 pub provides: Vec<String>,
1762}
1763impl GQLFederationExtension {
1764 #[allow(dead_code)]
1765 pub fn new(service_name: impl Into<String>) -> Self {
1766 GQLFederationExtension {
1767 service_name: service_name.into(),
1768 keys: Vec::new(),
1769 externals: Vec::new(),
1770 requires: Vec::new(),
1771 provides: Vec::new(),
1772 }
1773 }
1774 #[allow(dead_code)]
1775 pub fn add_key(&mut self, key: impl Into<String>) {
1776 self.keys.push(key.into());
1777 }
1778 #[allow(dead_code)]
1779 pub fn emit_key_directives(&self) -> String {
1780 self.keys
1781 .iter()
1782 .map(|k| format!("@key(fields: \"{}\")", k))
1783 .collect::<Vec<_>>()
1784 .join(" ")
1785 }
1786 #[allow(dead_code)]
1787 pub fn emit_service_query() -> &'static str {
1788 "type Query { _service: _Service! } type _Service { sdl: String }"
1789 }
1790}
1791#[allow(dead_code)]
1792#[derive(Debug, Clone)]
1793pub struct GQLSelectionField {
1794 pub alias: Option<String>,
1795 pub name: String,
1796 pub arguments: Vec<(String, String)>,
1797 pub directives: Vec<String>,
1798 pub selection_set: Vec<GQLSelectionField>,
1799}
1800impl GQLSelectionField {
1801 #[allow(dead_code)]
1802 pub fn new(name: impl Into<String>) -> Self {
1803 GQLSelectionField {
1804 alias: None,
1805 name: name.into(),
1806 arguments: Vec::new(),
1807 directives: Vec::new(),
1808 selection_set: Vec::new(),
1809 }
1810 }
1811 #[allow(dead_code)]
1812 pub fn emit(&self, indent: usize) -> String {
1813 let pad = " ".repeat(indent);
1814 let mut out = String::new();
1815 out.push_str(&pad);
1816 if let Some(ref alias) = self.alias {
1817 out.push_str(&format!("{}: ", alias));
1818 }
1819 out.push_str(&self.name);
1820 if !self.arguments.is_empty() {
1821 out.push('(');
1822 let args: Vec<String> = self
1823 .arguments
1824 .iter()
1825 .map(|(k, v)| format!("{}: {}", k, v))
1826 .collect();
1827 out.push_str(&args.join(", "));
1828 out.push(')');
1829 }
1830 if !self.selection_set.is_empty() {
1831 out.push_str(" {\n");
1832 for sel in &self.selection_set {
1833 out.push_str(&sel.emit(indent + 1));
1834 out.push('\n');
1835 }
1836 out.push_str(&pad);
1837 out.push('}');
1838 }
1839 out
1840 }
1841}