use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GraphQLType {
Int,
Float,
String,
Boolean,
Id,
BigInt,
BigDecimal,
Json,
Uuid,
Date,
DateTime,
Time,
List(Box<GraphQLType>),
Custom(std::string::String),
}
impl fmt::Display for GraphQLType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphQLType::Int => write!(f, "Int"),
GraphQLType::Float => write!(f, "Float"),
GraphQLType::String => write!(f, "String"),
GraphQLType::Boolean => write!(f, "Boolean"),
GraphQLType::Id => write!(f, "ID"),
GraphQLType::BigInt => write!(f, "BigInt"),
GraphQLType::BigDecimal => write!(f, "BigDecimal"),
GraphQLType::Json => write!(f, "JSON"),
GraphQLType::Uuid => write!(f, "UUID"),
GraphQLType::Date => write!(f, "Date"),
GraphQLType::DateTime => write!(f, "DateTime"),
GraphQLType::Time => write!(f, "Time"),
GraphQLType::List(inner) => write!(f, "[{}]", inner),
GraphQLType::Custom(name) => write!(f, "{}", name),
}
}
}
pub fn pg_type_to_graphql(pg_type: &str) -> GraphQLType {
let normalized = pg_type.to_lowercase().trim().to_string();
if normalized.starts_with('_') {
let inner_type = &normalized[1..];
return GraphQLType::List(Box::new(pg_type_to_graphql(inner_type)));
}
if normalized.ends_with("[]") {
let inner_type = normalized.trim_end_matches("[]");
return GraphQLType::List(Box::new(pg_type_to_graphql(inner_type)));
}
match normalized.as_str() {
"integer" | "int" | "int4" | "smallint" | "int2" => GraphQLType::Int,
"bigint" | "int8" => GraphQLType::BigInt,
"real" | "float4" | "double precision" | "float8" => GraphQLType::Float,
"numeric" | "decimal" => GraphQLType::BigDecimal,
"boolean" | "bool" => GraphQLType::Boolean,
"text" | "varchar" | "character varying" | "char" | "character" | "bpchar" => {
GraphQLType::String
}
"json" | "jsonb" => GraphQLType::Json,
"uuid" => GraphQLType::Uuid,
"timestamp" | "timestamp without time zone" | "timestamptz"
| "timestamp with time zone" => GraphQLType::DateTime,
"date" => GraphQLType::Date,
"time" | "time without time zone" | "timetz" | "time with time zone" => {
GraphQLType::Time
}
_ => GraphQLType::String,
}
}
pub fn is_nullable_type(nullable: bool, is_pk: bool) -> bool {
if is_pk {
return false;
}
nullable
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_pg_to_graphql_integer_types() {
assert_eq!(pg_type_to_graphql("integer"), GraphQLType::Int);
assert_eq!(pg_type_to_graphql("int4"), GraphQLType::Int);
assert_eq!(pg_type_to_graphql("int"), GraphQLType::Int);
assert_eq!(pg_type_to_graphql("smallint"), GraphQLType::Int);
assert_eq!(pg_type_to_graphql("int2"), GraphQLType::Int);
}
#[test]
fn test_pg_to_graphql_bigint() {
assert_eq!(pg_type_to_graphql("bigint"), GraphQLType::BigInt);
assert_eq!(pg_type_to_graphql("int8"), GraphQLType::BigInt);
}
#[test]
fn test_pg_to_graphql_float_types() {
assert_eq!(pg_type_to_graphql("real"), GraphQLType::Float);
assert_eq!(pg_type_to_graphql("float4"), GraphQLType::Float);
assert_eq!(pg_type_to_graphql("double precision"), GraphQLType::Float);
assert_eq!(pg_type_to_graphql("float8"), GraphQLType::Float);
}
#[test]
fn test_pg_to_graphql_numeric_types() {
assert_eq!(pg_type_to_graphql("numeric"), GraphQLType::BigDecimal);
assert_eq!(pg_type_to_graphql("decimal"), GraphQLType::BigDecimal);
}
#[test]
fn test_pg_to_graphql_string_types() {
assert_eq!(pg_type_to_graphql("text"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("varchar"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("character varying"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("char"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("bpchar"), GraphQLType::String);
}
#[test]
fn test_pg_to_graphql_boolean() {
assert_eq!(pg_type_to_graphql("boolean"), GraphQLType::Boolean);
assert_eq!(pg_type_to_graphql("bool"), GraphQLType::Boolean);
}
#[test]
fn test_pg_to_graphql_json() {
assert_eq!(pg_type_to_graphql("json"), GraphQLType::Json);
assert_eq!(pg_type_to_graphql("jsonb"), GraphQLType::Json);
}
#[test]
fn test_pg_to_graphql_uuid() {
assert_eq!(pg_type_to_graphql("uuid"), GraphQLType::Uuid);
}
#[test]
fn test_pg_to_graphql_datetime_types() {
assert_eq!(pg_type_to_graphql("timestamp"), GraphQLType::DateTime);
assert_eq!(pg_type_to_graphql("timestamptz"), GraphQLType::DateTime);
assert_eq!(
pg_type_to_graphql("timestamp with time zone"),
GraphQLType::DateTime
);
assert_eq!(
pg_type_to_graphql("timestamp without time zone"),
GraphQLType::DateTime
);
}
#[test]
fn test_pg_to_graphql_date() {
assert_eq!(pg_type_to_graphql("date"), GraphQLType::Date);
}
#[test]
fn test_pg_to_graphql_time() {
assert_eq!(pg_type_to_graphql("time"), GraphQLType::Time);
assert_eq!(pg_type_to_graphql("timetz"), GraphQLType::Time);
assert_eq!(
pg_type_to_graphql("time with time zone"),
GraphQLType::Time
);
}
#[test]
fn test_pg_to_graphql_array_types_underscore() {
assert_eq!(
pg_type_to_graphql("_int4"),
GraphQLType::List(Box::new(GraphQLType::Int))
);
assert_eq!(
pg_type_to_graphql("_text"),
GraphQLType::List(Box::new(GraphQLType::String))
);
assert_eq!(
pg_type_to_graphql("_uuid"),
GraphQLType::List(Box::new(GraphQLType::Uuid))
);
}
#[test]
fn test_pg_to_graphql_array_types_bracket() {
assert_eq!(
pg_type_to_graphql("integer[]"),
GraphQLType::List(Box::new(GraphQLType::Int))
);
assert_eq!(
pg_type_to_graphql("text[]"),
GraphQLType::List(Box::new(GraphQLType::String))
);
}
#[test]
fn test_pg_to_graphql_unknown_defaults_to_string() {
assert_eq!(pg_type_to_graphql("customtype"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("my_domain"), GraphQLType::String);
}
#[test]
fn test_pg_to_graphql_case_insensitive() {
assert_eq!(pg_type_to_graphql("INTEGER"), GraphQLType::Int);
assert_eq!(pg_type_to_graphql("Text"), GraphQLType::String);
assert_eq!(pg_type_to_graphql("BOOLEAN"), GraphQLType::Boolean);
}
#[test]
fn test_graphql_type_display() {
assert_eq!(format!("{}", GraphQLType::Int), "Int");
assert_eq!(format!("{}", GraphQLType::String), "String");
assert_eq!(
format!("{}", GraphQLType::List(Box::new(GraphQLType::Int))),
"[Int]"
);
}
#[test]
fn test_is_nullable_type() {
assert!(!is_nullable_type(true, true));
assert!(!is_nullable_type(false, true));
assert!(is_nullable_type(true, false));
assert!(!is_nullable_type(false, false));
}
}