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