#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ScalarType {
Int,
Float,
String,
Boolean,
Id,
}
impl ScalarType {
pub(crate) fn graphql_name(self) -> &'static str {
match self {
Self::Int => "Int",
Self::Float => "Float",
Self::String => "String",
Self::Boolean => "Boolean",
Self::Id => "ID",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Column {
pub name: String,
pub ty: ScalarType,
pub nullable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ForeignKey {
pub columns: Vec<String>,
pub ref_table: String,
pub ref_columns: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Table {
pub name: String,
pub columns: Vec<Column>,
pub primary_key: Vec<String>,
pub foreign_keys: Vec<ForeignKey>,
}
impl Table {
pub(crate) fn column(&self, name: &str) -> Option<&Column> {
self.columns.iter().find(|c| c.name == name)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct DbSchema {
pub tables: Vec<Table>,
}
impl DbSchema {
pub(crate) fn table(&self, name: &str) -> Option<&Table> {
self.tables.iter().find(|t| t.name == name)
}
pub(crate) fn relationships(&self, table: &str) -> Vec<Relationship> {
let mut out = Vec::new();
let Some(t) = self.table(table) else {
return out;
};
for fk in &t.foreign_keys {
out.push(Relationship {
field: to_one_field_name(fk),
kind: RelKind::ToOne,
target_table: fk.ref_table.clone(),
local_columns: fk.columns.clone(),
target_columns: fk.ref_columns.clone(),
});
}
for other in &self.tables {
if other.name == table {
continue; }
for fk in &other.foreign_keys {
if fk.ref_table == table {
out.push(Relationship {
field: other.name.clone(),
kind: RelKind::ToMany,
target_table: other.name.clone(),
local_columns: fk.ref_columns.clone(),
target_columns: fk.columns.clone(),
});
}
}
}
out
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RelKind {
ToOne,
ToMany,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Relationship {
pub field: String,
pub kind: RelKind,
pub target_table: String,
pub local_columns: Vec<String>,
pub target_columns: Vec<String>,
}
fn to_one_field_name(fk: &ForeignKey) -> String {
if fk.columns.len() == 1 {
if let Some(stripped) = fk.columns[0].strip_suffix("_id") {
if !stripped.is_empty() {
return stripped.to_string();
}
}
}
fk.ref_table.clone()
}