Skip to main content

docql/
schema.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Serialize, Deserialize)]
4pub struct GraphQLResponse {
5    pub data: Data,
6}
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct Data {
10    #[serde(rename = "__schema")]
11    pub schema: Schema,
12}
13
14#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
15#[serde(rename_all = "camelCase")]
16pub struct Schema {
17    pub query_type: Option<RootTypeRef>,
18    pub mutation_type: Option<RootTypeRef>,
19    pub types: Vec<FullType>,
20}
21
22impl Schema {
23    pub fn _find_type(&self, type_ref: &TypeRef) -> Option<&FullType> {
24        let type_ref_name = type_ref.name.as_ref()?;
25
26        for typ in &self.types {
27            if &typ.name == type_ref_name {
28                return Some(typ);
29            }
30        }
31
32        None
33    }
34}
35
36#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
37#[serde(rename_all = "camelCase")]
38pub struct RootTypeRef {
39    pub name: String,
40}
41
42#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
43#[serde(rename_all = "camelCase")]
44pub struct TypeRef {
45    pub kind: Kind,
46    pub name: Option<String>,
47    pub of_type: Option<Box<TypeRef>>,
48}
49
50#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
51#[serde(rename_all = "camelCase")]
52pub struct FullType {
53    pub kind: Kind,
54    pub name: String,
55    pub description: Option<String>,
56    pub fields: Option<Vec<Field>>,
57    pub input_fields: Option<Vec<InputValue>>,
58    pub interfaces: Option<Vec<TypeRef>>,
59    pub enum_values: Option<Vec<EnumValue>>,
60    pub possible_types: Option<Vec<TypeRef>>,
61}
62
63#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
64#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
65pub enum Kind {
66    NonNull,
67    List,
68    Object,
69    InputObject,
70    Union,
71    Enum,
72    Scalar,
73    Interface,
74}
75
76impl Kind {
77    pub fn prefix(&self) -> &str {
78        match self {
79            Self::NonNull => "non_null",
80            Self::List => "list",
81            Self::Object => "object",
82            Self::InputObject => "input_object",
83            Self::Union => "union",
84            Self::Enum => "enum",
85            Self::Scalar => "scalar",
86            Self::Interface => "interface",
87        }
88    }
89}
90
91#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
92#[serde(rename_all = "camelCase")]
93pub struct Field {
94    pub name: String,
95    pub description: Option<String>,
96    pub args: Vec<InputValue>,
97    #[serde(rename = "type")]
98    pub typ: TypeRef,
99    pub is_deprecated: bool,
100    pub deprecation_reason: Option<String>,
101}
102
103#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
104#[serde(rename_all = "camelCase")]
105pub struct InputValue {
106    pub name: String,
107    pub description: Option<String>,
108    #[serde(rename = "type")]
109    pub typ: TypeRef,
110    pub default_value: Option<String>,
111}
112
113#[derive(Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
114#[serde(rename_all = "camelCase")]
115pub struct EnumValue {
116    pub name: String,
117    pub description: Option<String>,
118    pub is_deprecated: bool,
119    pub deprecation_reason: Option<String>,
120}
121
122impl Schema {
123    pub fn find_uses(&self, full_type: &FullType) -> Vec<TypeUse> {
124        let mut uses = Vec::new();
125
126        for typ in &self.types {
127            if let Some(ref fields) = typ.fields {
128                for field in fields {
129                    if self.is_use(full_type, &field.typ) {
130                        uses.push(TypeUse::Field { typ, field: &field });
131                    } else {
132                        for arg in &field.args {
133                            if self.is_use(full_type, &arg.typ) {
134                                uses.push(TypeUse::Field { typ, field });
135                                break;
136                            }
137                        }
138                    }
139                }
140            }
141            if let Some(ref input_fields) = typ.input_fields {
142                for input_field in input_fields {
143                    if self.is_use(full_type, &input_field.typ) {
144                        uses.push(TypeUse::InputField {
145                            typ,
146                            input_field: &input_field,
147                        });
148                    }
149                }
150            }
151            if let Some(ref possible_types) = typ.possible_types {
152                for possible_type in possible_types {
153                    if self.is_use(full_type, &possible_type) {
154                        uses.push(TypeUse::PossibleType { typ });
155                    }
156                }
157            }
158        }
159
160        uses.sort();
161        uses
162    }
163
164    fn is_use(&self, full_type: &FullType, type_ref: &TypeRef) -> bool {
165        if Some(&full_type.name) == type_ref.name.as_ref() {
166            return true;
167        }
168
169        if let Some(ref of_type) = type_ref.of_type {
170            match type_ref.kind {
171                Kind::NonNull | Kind::List => self.is_use(full_type, of_type),
172                _ => false,
173            }
174        } else {
175            false
176        }
177    }
178}
179
180#[derive(Debug, Serialize, Eq, Ord, PartialEq, PartialOrd)]
181#[serde(tag = "use_type")]
182pub enum TypeUse<'a> {
183    /// The type is used as an input or an output in a field
184    Field {
185        #[serde(rename = "type")]
186        typ: &'a FullType,
187        field: &'a Field,
188    },
189    /// The type is used as an input field in another input object
190    InputField {
191        #[serde(rename = "type")]
192        typ: &'a FullType,
193        input_field: &'a InputValue,
194    },
195    /// The type is used as a possible type on an interface or an enumeration
196    PossibleType {
197        #[serde(rename = "type")]
198        typ: &'a FullType,
199    },
200}