spyglass_cli/
graphql_types.rs

1use anyhow::Error;
2use quote::__private::TokenStream;
3use quote::{format_ident, quote};
4
5use crate::parser::{parse_file, parse_graphql_type};
6
7#[derive(Debug, Clone)]
8pub struct Schema {
9    pub types: Vec<TypeDefinition>,
10}
11
12impl Schema {
13    pub fn parse(input: &str) -> Result<Self, Error> {
14        let schema = parse_file(input)?;
15
16        Ok(schema)
17    }
18}
19
20/// A graphql type definition
21#[derive(Debug, Clone)]
22pub struct TypeDefinition {
23    pub name: String,
24    pub fields: Vec<Field>,
25}
26
27impl TypeDefinition {
28    // Parse a type definition
29    pub fn parse(input: &str) -> Result<Self, Error> {
30        parse_graphql_type(input)
31    }
32
33    pub fn snake_case_name(&self) -> String {
34        camel_to_snake_case(&self.name)
35    }
36}
37
38/// A graphql Field
39#[derive(Debug, Clone)]
40pub struct Field {
41    pub name: String,
42    pub field_type: GraphQlType,
43}
44
45impl Field {
46    pub fn snake_case_name(&self) -> String {
47        camel_to_snake_case(&self.name)
48    }
49
50    pub fn field_type(&self) -> &GraphQlType {
51        &self.field_type
52    }
53
54    pub fn name(&self) -> &str {
55        &self.name
56    }
57
58    pub fn is_id(&self) -> bool {
59        match self.field_type {
60            GraphQlType::Id => true,
61            _ => false,
62        }
63    }
64
65    pub fn required(&self) -> bool {
66        match self.field_type {
67            GraphQlType::Id
68            | GraphQlType::String { required: true, .. }
69            | GraphQlType::Bytes { required: true, .. }
70            | GraphQlType::Boolean { required: true, .. }
71            | GraphQlType::BigInt { required: true, .. }
72            | GraphQlType::Int { required: true, .. }
73            | GraphQlType::Relation { required: true, .. }
74            | GraphQlType::Array { required: true, .. } => true,
75            _ => false,
76        }
77    }
78
79    pub fn derived_from(&self) -> bool {
80        match self.field_type {
81            GraphQlType::String {
82                derived_from: true, ..
83            }
84            | GraphQlType::Bytes {
85                derived_from: true, ..
86            }
87            | GraphQlType::Boolean {
88                derived_from: true, ..
89            }
90            | GraphQlType::BigInt {
91                derived_from: true, ..
92            }
93            | GraphQlType::Int {
94                derived_from: true, ..
95            }
96            | GraphQlType::Relation {
97                derived_from: true, ..
98            }
99            | GraphQlType::Array {
100                derived_from: true, ..
101            } => true,
102            _ => false,
103        }
104    }
105
106    pub fn rust_type(&self) -> TokenStream {
107        match &self.field_type {
108            GraphQlType::Array { internal_type, .. } => {
109                let internal_type = format_ident!("{}", internal_type.rust_type());
110                quote! (
111                    &Vec<#internal_type>
112                )
113            }
114            GraphQlType::Bytes { .. } => {
115                quote!(&Vec<u8>)
116            }
117            GraphQlType::Boolean { .. } => {
118                quote!(bool)
119            }
120            _ => {
121                let type_ident = format_ident!("{}", self.field_type.rust_type());
122                quote! (
123                    &#type_ident
124                )
125            }
126        }
127    }
128}
129
130fn camel_to_snake_case(input: &str) -> String {
131    let mut result = String::new();
132    for (i, c) in input.chars().enumerate() {
133        if c.is_uppercase() {
134            if i != 0 {
135                result.push('_');
136            }
137            result.push(c.to_lowercase().next().unwrap());
138        } else {
139            result.push(c);
140        }
141    }
142    result
143}
144
145/// An enum that models graphql types and if they are required
146#[derive(Debug, Clone)]
147pub enum GraphQlType {
148    Id,
149    String {
150        required: bool,
151        derived_from: bool,
152    },
153    Bytes {
154        required: bool,
155        derived_from: bool,
156    },
157    Boolean {
158        required: bool,
159        derived_from: bool,
160    },
161    BigInt {
162        required: bool,
163        derived_from: bool,
164    },
165    Int {
166        required: bool,
167        derived_from: bool,
168    },
169    Array {
170        required: bool,
171        derived_from: bool,
172        internal_type: Box<GraphQlType>,
173    },
174    // for relations to another entity, the value here will always be the id of the related entity, which is a string
175    Relation {
176        required: bool,
177        derived_from: bool,
178    },
179}
180
181impl GraphQlType {
182    pub fn new(
183        required: bool,
184        derived_from: bool,
185        type_name: &str,
186        internal_type: Option<Box<GraphQlType>>,
187    ) -> Self {
188        match type_name {
189            "ID" => GraphQlType::Id,
190            "String" => GraphQlType::String {
191                required,
192                derived_from,
193            },
194            "Bytes" => GraphQlType::Bytes {
195                required,
196                derived_from,
197            },
198            "Boolean" => GraphQlType::Boolean {
199                required,
200                derived_from,
201            },
202            "BigInt" => GraphQlType::BigInt {
203                required,
204                derived_from,
205            },
206            "Int" => GraphQlType::Int {
207                required,
208                derived_from,
209            },
210            "Array" => GraphQlType::Array {
211                required,
212                derived_from,
213                internal_type: internal_type.unwrap(),
214            },
215            _ => GraphQlType::Relation {
216                required,
217                derived_from,
218            },
219        }
220    }
221
222    pub fn rust_type(&self) -> String {
223        match self {
224            GraphQlType::Id
225            | GraphQlType::String { .. }
226            | GraphQlType::BigInt { .. }
227            | GraphQlType::Relation { .. } => {
228                format!("String")
229            }
230            GraphQlType::Bytes { .. } => {
231                format!("Vec<u8>")
232            }
233            GraphQlType::Boolean { .. } => {
234                format!("bool")
235            }
236            GraphQlType::Int { .. } => {
237                format!("i32")
238            }
239            GraphQlType::Array { internal_type, .. } => {
240                let internal_type = internal_type.rust_type();
241                format!("Vec<{}>", internal_type)
242            }
243        }
244    }
245}