use serde::de;
use serde::{Deserialize, Serialize};
use super::inflect::{to_pascal_singular, to_plural};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SchemaDefinition {
pub collections: Vec<CollectionDef>,
}
impl SchemaDefinition {
pub fn collection_by_name(&self, name: &str) -> Option<&CollectionDef> {
self.collections.iter().find(|coll_def| coll_def.collection == name)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CollectionDef {
pub collection: String,
#[serde(default)]
pub graphql_name: Option<String>,
#[serde(default)]
pub description: Option<String>,
pub fields: Vec<FieldDef>,
}
impl CollectionDef {
pub fn type_name(&self) -> String {
self.graphql_name
.clone()
.unwrap_or_else(|| to_pascal_singular(&self.collection))
}
pub fn plural_name(&self) -> String {
let base = self
.graphql_name
.as_deref()
.unwrap_or(&self.collection);
to_plural(&base.to_lowercase())
}
pub fn singular_name(&self) -> String {
let base = self.graphql_name.as_deref().unwrap_or(&self.collection);
let mut chars = base.chars();
match chars.next() {
None => base.to_string(),
Some(first_char) => {
first_char.to_lowercase().collect::<String>() + chars.as_str()
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FieldDef {
pub name: String,
#[serde(default)]
pub graphql_name: Option<String>,
#[serde(rename = "type")]
pub field_type: FieldType,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub unique: bool,
#[serde(default)]
pub r#enum: Option<EnumDef>,
#[serde(default)]
pub description: Option<String>,
}
impl FieldDef {
pub fn graphql_name(&self) -> String {
self.graphql_name
.clone()
.unwrap_or_else(|| self.name.clone())
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FieldType {
ID,
String,
Int,
Float,
Boolean,
DateTime,
Json,
List(Box<FieldType>),
Relation(RelationFieldDef),
}
impl<'de> Deserialize<'de> for FieldType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde_json::Value;
let value = Value::deserialize(deserializer)?;
match value {
Value::String(s) => match s.to_lowercase().as_str() {
"id" => Ok(FieldType::ID),
"string" => Ok(FieldType::String),
"int" => Ok(FieldType::Int),
"float" => Ok(FieldType::Float),
"boolean" => Ok(FieldType::Boolean),
"datetime" => Ok(FieldType::DateTime),
"json" => Ok(FieldType::Json),
_ => Err(de::Error::custom(format!("unknown type: {}", s))),
},
Value::Array(arr) if arr.len() == 1 => {
let first = arr.into_iter().next().ok_or_else(|| {
de::Error::custom("expected exactly one list element")
})?;
let inner: FieldType =
serde_json::from_value(first).map_err(de::Error::custom)?;
Ok(FieldType::List(Box::new(inner)))
}
Value::Object(ref obj) => {
let rel_value = if let Some(inner) = obj.get("relation") {
inner.clone()
} else {
value
};
let rel: RelationFieldDef =
serde_json::from_value(rel_value).map_err(de::Error::custom)?;
Ok(FieldType::Relation(rel))
}
Value::Array(ref arr) if arr.len() != 1 => Err(de::Error::custom(format!(
"list type must contain exactly one element, got {}",
arr.len()
))),
_ => Err(de::Error::custom(
"expected a type string (e.g. \"String\"), a list type (e.g. [\"String\"]), or a relation object",
)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RelationFieldDef {
pub kind: RelationKind,
pub collection: String,
pub reference_field: String,
#[serde(default)]
pub reverse_name: Option<String>,
#[serde(default)]
pub junction: Option<JunctionDef>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RelationKind {
OneToMany,
OneToOne,
ManyToMany,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EnumDef {
pub name: String,
pub values: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct JunctionDef {
pub collection: String,
pub local_field: String,
pub foreign_field: String,
pub foreign_reference: String,
#[serde(default)]
pub metadata_fields: Vec<String>,
}