Skip to main content

oxilean_codegen/graphql_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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/// A complete GraphQL schema (SDL-level).
393#[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/// A GraphQL object type definition.
422#[derive(Debug, Clone)]
423pub struct GQLObject {
424    pub name: String,
425    pub fields: Vec<GQLField>,
426    /// Names of interfaces this object implements.
427    pub implements: Vec<String>,
428    /// Optional description for the object type.
429    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/// A GraphQL type reference.
553#[derive(Debug, Clone, PartialEq, Eq)]
554pub enum GQLType {
555    /// A named scalar (e.g. `String`, `Int`, `Boolean`, `ID`, `Float`).
556    Scalar(String),
557    /// A named object type reference.
558    Object(String),
559    /// A list wrapper (`[T]`).
560    List(Box<GQLType>),
561    /// A non-null wrapper (`T!`).
562    NonNull(Box<GQLType>),
563    /// A named enum type reference.
564    Enum(String),
565    /// A named interface type reference.
566    Interface(String),
567    /// A named union type reference.
568    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/// A GraphQL enum type definition.
953#[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/// A GraphQL enum value.
1338#[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/// A single field on a GraphQL object or interface.
1392#[derive(Debug, Clone)]
1393pub struct GQLField {
1394    pub name: String,
1395    pub ty: GQLType,
1396    /// Whether the field is nullable (i.e. NOT wrapped in NonNull).
1397    pub nullable: bool,
1398    pub description: Option<String>,
1399    /// Arguments this field accepts.
1400    pub args: Vec<GQLFieldArg>,
1401}
1402/// An argument to a GraphQL field.
1403#[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}
1410/// The GraphQL SDL emitter backend.
1411pub struct GQLBackend;
1412impl GQLBackend {
1413    /// Emit a GraphQL type reference string.
1414    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    /// Emit a single field definition line.
1426    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    /// Emit a full `type Foo { ... }` SDL block.
1441    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    /// Emit an `enum Foo { ... }` SDL block.
1456    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    /// Emit the top-level `schema { ... }` block and all type definitions.
1468    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    /// Generate simple resolver stub functions (as pseudo-Rust) for each field.
1483    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    /// Build a minimal schema from a list of (type_name, field_name, scalar) triples.
1501    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}