use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaDefinition {
#[serde(default)]
pub types: HashMap<String, HashMap<String, FieldDefinition>>,
#[serde(default)]
pub collections: HashMap<String, CollectionDefinition>,
#[serde(default)]
pub views: HashMap<String, ViewDefinition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollectionDefinition {
pub path: String,
#[serde(default)]
pub fields: HashMap<String, FieldDefinition>,
#[serde(default)]
pub content: bool,
#[serde(default)]
pub additional_properties: bool,
#[serde(default)]
pub strict: bool,
#[serde(default)]
pub readonly: bool,
#[serde(default)]
pub on_delete: Option<OnDeletePolicy>,
#[serde(default)]
pub id: Option<IdConfig>,
#[serde(default)]
pub records: Option<RecordDefinition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdConfig {
#[serde(default)]
pub auto: Option<AutoIdStrategy>,
#[serde(default)]
pub on_conflict: Option<OnConflict>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AutoIdStrategy {
Ulid,
Uuid,
Nanoid,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OnConflict {
Error,
Suffix,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldDefinition {
#[serde(rename = "type")]
pub field_type: FieldType,
#[serde(default)]
pub required: bool,
#[serde(rename = "enum", default)]
pub enum_values: Option<Vec<String>>,
#[serde(default)]
pub default: Option<serde_yaml::Value>,
#[serde(default)]
pub target: Option<RefTarget>,
#[serde(default)]
pub items: Option<ItemType>,
#[serde(default)]
pub on_delete: Option<OnDeletePolicy>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FieldType {
String,
Number,
Boolean,
Date,
Datetime,
List,
Object,
Ref,
#[serde(untagged)]
Custom(std::string::String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RefTarget {
Single(String),
Multiple(Vec<String>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ItemType {
Simple(String),
Complex(Box<FieldDefinition>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OnDeletePolicy {
Error,
Cascade,
Nullify,
Archive,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewDefinition {
pub query: String,
#[serde(rename = "type", default)]
pub view_type: Option<ViewType>,
#[serde(default)]
pub materialize: bool,
#[serde(default)]
pub buffer: Option<String>,
#[serde(default)]
pub params: Option<HashMap<String, ParamDefinition>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ViewType {
View,
Query,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParamDefinition {
#[serde(rename = "type")]
pub param_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordDefinition {
pub base: HashMap<String, FieldDefinition>,
pub by: String,
pub variants: HashMap<String, RecordVariant>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordVariant {
#[serde(default)]
pub fields: HashMap<String, FieldDefinition>,
}
impl SchemaDefinition {
pub fn is_custom_type(&self, name: &str) -> bool {
self.types.contains_key(name)
}
pub fn get_custom_type(&self, name: &str) -> Option<&HashMap<String, FieldDefinition>> {
self.types.get(name)
}
}
impl CollectionDefinition {
pub fn file_extension(&self) -> &str {
if self.path.ends_with(".json") {
"json"
} else if self.path.ends_with(".jsonl") {
"jsonl"
} else {
"md"
}
}
pub fn on_conflict(&self) -> OnConflict {
self.id
.as_ref()
.and_then(|id| id.on_conflict.clone())
.unwrap_or(OnConflict::Error)
}
pub fn auto_id(&self) -> Option<&AutoIdStrategy> {
self.id.as_ref().and_then(|id| id.auto.as_ref())
}
}
impl FieldDefinition {
pub fn effective_on_delete(&self, collection_default: Option<&OnDeletePolicy>) -> OnDeletePolicy {
self.on_delete
.clone()
.or_else(|| collection_default.cloned())
.unwrap_or(OnDeletePolicy::Error)
}
}
impl RefTarget {
pub fn targets(&self) -> Vec<&str> {
match self {
RefTarget::Single(s) => vec![s.as_str()],
RefTarget::Multiple(v) => v.iter().map(|s| s.as_str()).collect(),
}
}
}