1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum GraphQLType {
8 Int,
10 Float,
12 String,
14 Boolean,
16 Id,
18 BigInt,
20 BigDecimal,
22 Json,
24 Uuid,
26 Date,
28 DateTime,
30 Time,
32 List(Box<GraphQLType>),
34 Custom(std::string::String),
36}
37
38impl fmt::Display for GraphQLType {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 GraphQLType::Int => write!(f, "Int"),
42 GraphQLType::Float => write!(f, "Float"),
43 GraphQLType::String => write!(f, "String"),
44 GraphQLType::Boolean => write!(f, "Boolean"),
45 GraphQLType::Id => write!(f, "ID"),
46 GraphQLType::BigInt => write!(f, "BigInt"),
47 GraphQLType::BigDecimal => write!(f, "BigDecimal"),
48 GraphQLType::Json => write!(f, "JSON"),
49 GraphQLType::Uuid => write!(f, "UUID"),
50 GraphQLType::Date => write!(f, "Date"),
51 GraphQLType::DateTime => write!(f, "DateTime"),
52 GraphQLType::Time => write!(f, "Time"),
53 GraphQLType::List(inner) => write!(f, "[{}]", inner),
54 GraphQLType::Custom(name) => write!(f, "{}", name),
55 }
56 }
57}
58
59pub fn pg_type_to_graphql(pg_type: &str) -> GraphQLType {
61 let normalized = pg_type.to_lowercase().trim().to_string();
63
64 if normalized.starts_with('_') {
66 let inner_type = &normalized[1..];
68 return GraphQLType::List(Box::new(pg_type_to_graphql(inner_type)));
69 }
70
71 if normalized.ends_with("[]") {
72 let inner_type = normalized.trim_end_matches("[]");
74 return GraphQLType::List(Box::new(pg_type_to_graphql(inner_type)));
75 }
76
77 match normalized.as_str() {
78 "integer" | "int" | "int4" | "smallint" | "int2" => GraphQLType::Int,
80
81 "bigint" | "int8" => GraphQLType::BigInt,
83
84 "real" | "float4" | "double precision" | "float8" => GraphQLType::Float,
86
87 "numeric" | "decimal" => GraphQLType::BigDecimal,
89
90 "boolean" | "bool" => GraphQLType::Boolean,
92
93 "text" | "varchar" | "character varying" | "char" | "character" | "bpchar" => {
95 GraphQLType::String
96 }
97
98 "json" | "jsonb" => GraphQLType::Json,
100
101 "uuid" => GraphQLType::Uuid,
103
104 "timestamp" | "timestamp without time zone" | "timestamptz"
106 | "timestamp with time zone" => GraphQLType::DateTime,
107 "date" => GraphQLType::Date,
108 "time" | "time without time zone" | "timetz" | "time with time zone" => {
109 GraphQLType::Time
110 }
111
112 _ => GraphQLType::String,
114 }
115}
116
117pub fn is_nullable_type(nullable: bool, is_pk: bool) -> bool {
119 if is_pk {
121 return false;
122 }
123 nullable
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use pretty_assertions::assert_eq;
130
131 #[test]
132 fn test_pg_to_graphql_integer_types() {
133 assert_eq!(pg_type_to_graphql("integer"), GraphQLType::Int);
134 assert_eq!(pg_type_to_graphql("int4"), GraphQLType::Int);
135 assert_eq!(pg_type_to_graphql("int"), GraphQLType::Int);
136 assert_eq!(pg_type_to_graphql("smallint"), GraphQLType::Int);
137 assert_eq!(pg_type_to_graphql("int2"), GraphQLType::Int);
138 }
139
140 #[test]
141 fn test_pg_to_graphql_bigint() {
142 assert_eq!(pg_type_to_graphql("bigint"), GraphQLType::BigInt);
143 assert_eq!(pg_type_to_graphql("int8"), GraphQLType::BigInt);
144 }
145
146 #[test]
147 fn test_pg_to_graphql_float_types() {
148 assert_eq!(pg_type_to_graphql("real"), GraphQLType::Float);
149 assert_eq!(pg_type_to_graphql("float4"), GraphQLType::Float);
150 assert_eq!(pg_type_to_graphql("double precision"), GraphQLType::Float);
151 assert_eq!(pg_type_to_graphql("float8"), GraphQLType::Float);
152 }
153
154 #[test]
155 fn test_pg_to_graphql_numeric_types() {
156 assert_eq!(pg_type_to_graphql("numeric"), GraphQLType::BigDecimal);
157 assert_eq!(pg_type_to_graphql("decimal"), GraphQLType::BigDecimal);
158 }
159
160 #[test]
161 fn test_pg_to_graphql_string_types() {
162 assert_eq!(pg_type_to_graphql("text"), GraphQLType::String);
163 assert_eq!(pg_type_to_graphql("varchar"), GraphQLType::String);
164 assert_eq!(pg_type_to_graphql("character varying"), GraphQLType::String);
165 assert_eq!(pg_type_to_graphql("char"), GraphQLType::String);
166 assert_eq!(pg_type_to_graphql("bpchar"), GraphQLType::String);
167 }
168
169 #[test]
170 fn test_pg_to_graphql_boolean() {
171 assert_eq!(pg_type_to_graphql("boolean"), GraphQLType::Boolean);
172 assert_eq!(pg_type_to_graphql("bool"), GraphQLType::Boolean);
173 }
174
175 #[test]
176 fn test_pg_to_graphql_json() {
177 assert_eq!(pg_type_to_graphql("json"), GraphQLType::Json);
178 assert_eq!(pg_type_to_graphql("jsonb"), GraphQLType::Json);
179 }
180
181 #[test]
182 fn test_pg_to_graphql_uuid() {
183 assert_eq!(pg_type_to_graphql("uuid"), GraphQLType::Uuid);
184 }
185
186 #[test]
187 fn test_pg_to_graphql_datetime_types() {
188 assert_eq!(pg_type_to_graphql("timestamp"), GraphQLType::DateTime);
189 assert_eq!(pg_type_to_graphql("timestamptz"), GraphQLType::DateTime);
190 assert_eq!(
191 pg_type_to_graphql("timestamp with time zone"),
192 GraphQLType::DateTime
193 );
194 assert_eq!(
195 pg_type_to_graphql("timestamp without time zone"),
196 GraphQLType::DateTime
197 );
198 }
199
200 #[test]
201 fn test_pg_to_graphql_date() {
202 assert_eq!(pg_type_to_graphql("date"), GraphQLType::Date);
203 }
204
205 #[test]
206 fn test_pg_to_graphql_time() {
207 assert_eq!(pg_type_to_graphql("time"), GraphQLType::Time);
208 assert_eq!(pg_type_to_graphql("timetz"), GraphQLType::Time);
209 assert_eq!(
210 pg_type_to_graphql("time with time zone"),
211 GraphQLType::Time
212 );
213 }
214
215 #[test]
216 fn test_pg_to_graphql_array_types_underscore() {
217 assert_eq!(
218 pg_type_to_graphql("_int4"),
219 GraphQLType::List(Box::new(GraphQLType::Int))
220 );
221 assert_eq!(
222 pg_type_to_graphql("_text"),
223 GraphQLType::List(Box::new(GraphQLType::String))
224 );
225 assert_eq!(
226 pg_type_to_graphql("_uuid"),
227 GraphQLType::List(Box::new(GraphQLType::Uuid))
228 );
229 }
230
231 #[test]
232 fn test_pg_to_graphql_array_types_bracket() {
233 assert_eq!(
234 pg_type_to_graphql("integer[]"),
235 GraphQLType::List(Box::new(GraphQLType::Int))
236 );
237 assert_eq!(
238 pg_type_to_graphql("text[]"),
239 GraphQLType::List(Box::new(GraphQLType::String))
240 );
241 }
242
243 #[test]
244 fn test_pg_to_graphql_unknown_defaults_to_string() {
245 assert_eq!(pg_type_to_graphql("customtype"), GraphQLType::String);
246 assert_eq!(pg_type_to_graphql("my_domain"), GraphQLType::String);
247 }
248
249 #[test]
250 fn test_pg_to_graphql_case_insensitive() {
251 assert_eq!(pg_type_to_graphql("INTEGER"), GraphQLType::Int);
252 assert_eq!(pg_type_to_graphql("Text"), GraphQLType::String);
253 assert_eq!(pg_type_to_graphql("BOOLEAN"), GraphQLType::Boolean);
254 }
255
256 #[test]
257 fn test_graphql_type_display() {
258 assert_eq!(format!("{}", GraphQLType::Int), "Int");
259 assert_eq!(format!("{}", GraphQLType::String), "String");
260 assert_eq!(
261 format!("{}", GraphQLType::List(Box::new(GraphQLType::Int))),
262 "[Int]"
263 );
264 }
265
266 #[test]
267 fn test_is_nullable_type() {
268 assert!(!is_nullable_type(true, true));
270 assert!(!is_nullable_type(false, true));
271
272 assert!(is_nullable_type(true, false));
274 assert!(!is_nullable_type(false, false));
275 }
276}