use crate::{ColumnType, TableDefinition};
use serde_json::{json, Map, Value};
pub(super) fn schema_names(table: &TableDefinition) -> (String, String) {
let schema_name = to_pascal_case(&table.name);
let create_schema_name = format!("{}Create", schema_name);
(schema_name, create_schema_name)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Nullable {
Flag,
TypeUnion,
}
impl Nullable {
fn apply(self, mut prop: Value) -> Value {
if let Some(obj) = prop.as_object_mut() {
match self {
Nullable::Flag => {
obj.insert("nullable".into(), json!(true));
}
Nullable::TypeUnion => {
if let Some(kind) = obj.remove("type") {
obj.insert("type".into(), json!([kind, "null"]));
}
}
}
}
prop
}
}
pub(super) fn table_schemas(table: &TableDefinition, nullable: Nullable) -> [(String, Value); 2] {
let (schema_name, create_schema_name) = schema_names(table);
[
(schema_name, table_to_schema(table, nullable)),
(create_schema_name, table_to_create_schema(table, nullable)),
]
}
pub(super) fn pagination() -> Value {
json!({
"type": "object",
"properties": {
"current_page": { "type": "integer" },
"per_page": { "type": "integer" },
"total_records": { "type": "integer" },
"total_pages": { "type": "integer" }
}
})
}
pub(super) fn column_to_openapi_type(col_type: &ColumnType) -> Value {
match col_type {
ColumnType::Uuid => json!({"type": "string", "format": "uuid"}),
ColumnType::Text => json!({"type": "string"}),
ColumnType::Integer => json!({"type": "integer", "format": "int32"}),
ColumnType::BigInteger => json!({"type": "integer", "format": "int64"}),
ColumnType::Float => json!({"type": "number", "format": "float"}),
ColumnType::Double => json!({"type": "number", "format": "double"}),
ColumnType::Boolean => json!({"type": "boolean"}),
ColumnType::Timestamp => json!({"type": "string", "format": "date-time"}),
ColumnType::Date => json!({"type": "string", "format": "date"}),
ColumnType::Jsonb => json!({"type": "object"}),
}
}
fn table_to_schema(table: &TableDefinition, nullable: Nullable) -> Value {
let mut properties = Map::new();
let mut required = Vec::new();
for col in &table.columns {
let prop = column_to_openapi_type(&col.column_type);
let prop = if col.nullable {
nullable.apply(prop)
} else {
prop
};
properties.insert(col.name.clone(), prop);
if !col.nullable {
required.push(json!(col.name));
}
}
json!({
"type": "object",
"properties": Value::Object(properties),
"required": required
})
}
fn table_to_create_schema(table: &TableDefinition, nullable: Nullable) -> Value {
let mut properties = Map::new();
let mut required = Vec::new();
for col in &table.columns {
if col.auto_generate {
continue;
}
let prop = column_to_openapi_type(&col.column_type);
let prop = if col.nullable {
nullable.apply(prop)
} else {
prop
};
properties.insert(col.name.clone(), prop);
if !col.nullable && col.default_value.is_none() {
required.push(json!(col.name));
}
}
json!({
"type": "object",
"properties": Value::Object(properties),
"required": required
})
}
fn to_pascal_case(s: &str) -> String {
s.split('_')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
Some(c) => c.to_uppercase().to_string() + &chars.as_str().to_lowercase(),
None => String::new(),
}
})
.collect()
}