1use std::collections::HashMap;
2
3use serde::de;
4use serde::{Deserialize, Serialize};
5
6use super::inflect::{to_pascal_singular, to_plural};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(deny_unknown_fields)]
10pub struct SchemaDefinition {
11 pub collections: Vec<CollectionDef>,
12}
13
14impl SchemaDefinition {
15 pub fn collection_by_name(&self, name: &str) -> Option<&CollectionDef> {
16 self.collections.iter().find(|coll_def| coll_def.collection == name)
17 }
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(deny_unknown_fields)]
23pub struct DirectiveDef {
24 pub name: String,
25 #[serde(default)]
26 pub args: HashMap<String, serde_json::Value>,
27}
28
29#[derive(Debug, Clone, Default, Serialize, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct FieldDirectives {
33 #[serde(default)]
34 pub get: Vec<DirectiveDef>,
35 #[serde(default)]
36 pub list: Vec<DirectiveDef>,
37 #[serde(default)]
38 pub create: Vec<DirectiveDef>,
39 #[serde(default)]
40 pub update: Vec<DirectiveDef>,
41 #[serde(default)]
42 pub delete: Vec<DirectiveDef>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46#[serde(deny_unknown_fields)]
47pub struct CollectionDef {
48 pub collection: String,
49
50 #[serde(default)]
51 pub graphql_name: Option<String>,
52
53 #[serde(default)]
54 pub description: Option<String>,
55
56 #[serde(default)]
57 pub directives: FieldDirectives,
58
59 pub fields: Vec<FieldDef>,
60}
61
62impl CollectionDef {
63 pub fn type_name(&self) -> String {
64 self.graphql_name
65 .clone()
66 .unwrap_or_else(|| to_pascal_singular(&self.collection))
67 }
68
69 pub fn plural_name(&self) -> String {
70 let base = self
71 .graphql_name
72 .as_deref()
73 .unwrap_or(&self.collection);
74 to_plural(&base.to_lowercase())
75 }
76
77 pub fn singular_name(&self) -> String {
78 let base = self.graphql_name.as_deref().unwrap_or(&self.collection);
79 let mut chars = base.chars();
80 match chars.next() {
81 None => base.to_string(),
82 Some(first_char) => {
83 first_char.to_lowercase().collect::<String>() + chars.as_str()
84 }
85 }
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(deny_unknown_fields)]
91pub struct FieldDef {
92 pub name: String,
93
94 #[serde(default)]
95 pub graphql_name: Option<String>,
96
97 #[serde(rename = "type")]
98 pub field_type: FieldType,
99
100 #[serde(default)]
101 pub required: bool,
102
103 #[serde(default)]
104 pub unique: bool,
105
106 #[serde(default)]
107 pub r#enum: Option<EnumDef>,
108
109 #[serde(default)]
110 pub description: Option<String>,
111}
112
113impl FieldDef {
114 pub fn graphql_name(&self) -> String {
115 self.graphql_name
116 .clone()
117 .unwrap_or_else(|| self.name.clone())
118 }
119}
120
121#[derive(Debug, Clone, Serialize)]
122#[serde(rename_all = "lowercase")]
123pub enum FieldType {
124 ID,
125 String,
126 Int,
127 Float,
128 Boolean,
129 DateTime,
130 Json,
131 List(Box<FieldType>),
133 Relation(RelationFieldDef),
134}
135
136impl<'de> Deserialize<'de> for FieldType {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 use serde_json::Value;
142
143 let value = Value::deserialize(deserializer)?;
144
145 match value {
146 Value::String(s) => match s.to_lowercase().as_str() {
147 "id" => Ok(FieldType::ID),
148 "string" => Ok(FieldType::String),
149 "int" => Ok(FieldType::Int),
150 "float" => Ok(FieldType::Float),
151 "boolean" => Ok(FieldType::Boolean),
152 "datetime" => Ok(FieldType::DateTime),
153 "json" => Ok(FieldType::Json),
154 _ => Err(de::Error::custom(format!("unknown type: {}", s))),
155 },
156 Value::Array(arr) if arr.len() == 1 => {
157 let first = arr.into_iter().next().ok_or_else(|| {
158 de::Error::custom("expected exactly one list element")
159 })?;
160 let inner: FieldType =
161 serde_json::from_value(first).map_err(de::Error::custom)?;
162 Ok(FieldType::List(Box::new(inner)))
163 }
164 Value::Object(ref obj) => {
165 let rel_value = if let Some(inner) = obj.get("relation") {
166 inner.clone()
167 } else {
168 value
169 };
170 let rel: RelationFieldDef =
171 serde_json::from_value(rel_value).map_err(de::Error::custom)?;
172 Ok(FieldType::Relation(rel))
173 }
174 Value::Array(ref arr) if arr.len() != 1 => Err(de::Error::custom(format!(
175 "list type must contain exactly one element, got {}",
176 arr.len()
177 ))),
178 _ => Err(de::Error::custom(
179 "expected a type string (e.g. \"String\"), a list type (e.g. [\"String\"]), or a relation object",
180 )),
181 }
182 }
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
186#[serde(deny_unknown_fields)]
187pub struct RelationFieldDef {
188 pub kind: RelationKind,
189 pub collection: String,
190 pub reference_field: String,
191
192 #[serde(default)]
194 pub reverse_name: Option<String>,
195
196 #[serde(default)]
198 pub junction: Option<JunctionDef>,
199}
200
201#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
202#[serde(rename_all = "snake_case")]
203pub enum RelationKind {
204 OneToMany,
206 OneToOne,
208 ManyToMany,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(deny_unknown_fields)]
214pub struct EnumDef {
215 pub name: String,
216 pub values: Vec<String>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(deny_unknown_fields)]
221pub struct JunctionDef {
222 pub collection: String,
223 pub local_field: String,
224 pub foreign_field: String,
225 pub foreign_reference: String,
226
227 #[serde(default)]
228 pub metadata_fields: Vec<String>,
229}