1use std::{fmt, ops::Deref, str};
19
20use super::types::{StructField, StructType, StructTypeBuilder};
21use derive_getters::Getters;
22use serde::{Deserialize, Serialize};
23
24use crate::error::Error;
25
26pub static DEFAULT_SCHEMA_ID: i32 = 0;
27
28#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Getters)]
29#[serde(rename_all = "kebab-case")]
30pub struct Schema {
32 schema_id: i32,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 identifier_field_ids: Option<Vec<i32>>,
37
38 #[serde(flatten)]
39 fields: StructType,
41}
42
43impl Deref for Schema {
44 type Target = StructType;
45 fn deref(&self) -> &Self::Target {
46 &self.fields
47 }
48}
49
50impl Schema {
51 pub fn builder() -> SchemaBuilder {
59 SchemaBuilder::default()
60 }
61
62 pub fn from_struct_type(
72 fields: StructType,
73 schema_id: i32,
74 identifier_field_ids: Option<Vec<i32>>,
75 ) -> Self {
76 Schema {
77 schema_id,
78 identifier_field_ids,
79 fields,
80 }
81 }
82
83 pub fn project(&self, ids: &[i32]) -> Schema {
92 Schema {
93 schema_id: self.schema_id,
94 identifier_field_ids: self.identifier_field_ids.as_ref().map(|x| {
95 x.iter()
96 .filter(|x| ids.contains(x))
97 .map(ToOwned::to_owned)
98 .collect()
99 }),
100 fields: StructType::new(
101 self.fields()
102 .iter()
103 .filter(|x| ids.contains(&x.id))
104 .map(ToOwned::to_owned)
105 .collect(),
106 ),
107 }
108 }
109}
110
111impl fmt::Display for Schema {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(
114 f,
115 "{}",
116 &serde_json::to_string(self).map_err(|_| fmt::Error)?,
117 )
118 }
119}
120
121impl str::FromStr for Schema {
122 type Err = Error;
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 serde_json::from_str(s).map_err(Error::from)
125 }
126}
127
128#[derive(Default)]
129pub struct SchemaBuilder {
130 schema_id: Option<i32>,
131 identifier_field_ids: Option<Vec<i32>>,
132 fields: StructTypeBuilder,
133}
134
135impl SchemaBuilder {
136 pub fn with_schema_id(&mut self, schema_id: i32) -> &mut Self {
144 self.schema_id = Some(schema_id);
145 self
146 }
147
148 pub fn with_identifier_field_ids(&mut self, ids: impl Into<Vec<i32>>) -> &mut Self {
156 self.identifier_field_ids = Some(ids.into());
157 self
158 }
159
160 pub fn with_struct_field(&mut self, field: StructField) -> &mut Self {
168 self.fields.with_struct_field(field);
169 self
170 }
171
172 pub fn build(&mut self) -> Result<Schema, Error> {
178 let fields = self.fields.build()?;
179
180 Ok(Schema {
181 schema_id: self.schema_id.unwrap_or(DEFAULT_SCHEMA_ID),
182 identifier_field_ids: self.identifier_field_ids.take(),
183 fields,
184 })
185 }
186}
187
188impl TryFrom<SchemaV2> for Schema {
189 type Error = Error;
190 fn try_from(value: SchemaV2) -> Result<Self, Self::Error> {
191 Ok(Schema {
192 schema_id: value.schema_id,
193 identifier_field_ids: value.identifier_field_ids,
194 fields: value.fields,
195 })
196 }
197}
198
199impl TryFrom<SchemaV1> for Schema {
200 type Error = Error;
201 fn try_from(value: SchemaV1) -> Result<Self, Self::Error> {
202 Ok(Schema {
203 schema_id: value.schema_id.unwrap_or(0),
204 identifier_field_ids: value.identifier_field_ids,
205 fields: value.fields,
206 })
207 }
208}
209
210impl From<Schema> for SchemaV2 {
211 fn from(value: Schema) -> Self {
212 SchemaV2 {
213 schema_id: value.schema_id,
214 identifier_field_ids: value.identifier_field_ids,
215 fields: value.fields,
216 }
217 }
218}
219
220impl From<Schema> for SchemaV1 {
221 fn from(value: Schema) -> Self {
222 SchemaV1 {
223 schema_id: Some(value.schema_id),
224 identifier_field_ids: value.identifier_field_ids,
225 fields: value.fields,
226 }
227 }
228}
229
230#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
231#[serde(rename_all = "kebab-case")]
232pub struct SchemaV2 {
234 pub schema_id: i32,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub identifier_field_ids: Option<Vec<i32>>,
239
240 #[serde(flatten)]
241 pub fields: StructType,
243}
244
245#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
246#[serde(rename_all = "kebab-case")]
247pub struct SchemaV1 {
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub schema_id: Option<i32>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub identifier_field_ids: Option<Vec<i32>>,
255
256 #[serde(flatten)]
257 pub fields: StructType,
259}
260
261impl From<SchemaV1> for SchemaV2 {
262 fn from(v1: SchemaV1) -> Self {
263 SchemaV2 {
264 schema_id: v1.schema_id.unwrap_or(0),
265 identifier_field_ids: v1.identifier_field_ids,
266 fields: v1.fields,
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use crate::spec::types::{PrimitiveType, Type};
274
275 use super::*;
276
277 #[test]
278 fn schema() {
279 let record = r#"
280 {
281 "type": "struct",
282 "schema-id": 1,
283 "fields": [ {
284 "id": 1,
285 "name": "id",
286 "required": true,
287 "type": "uuid"
288 }, {
289 "id": 2,
290 "name": "data",
291 "required": false,
292 "type": "int"
293 } ]
294 }
295 "#;
296
297 let result: SchemaV2 = serde_json::from_str(record).unwrap();
298 assert_eq!(1, result.schema_id);
299 assert_eq!(
300 Type::Primitive(PrimitiveType::Uuid),
301 result.fields[0].field_type
302 );
303 assert_eq!(1, result.fields[0].id);
304 assert!(result.fields[0].required);
305
306 assert_eq!(
307 Type::Primitive(PrimitiveType::Int),
308 result.fields[1].field_type
309 );
310 assert_eq!(2, result.fields[1].id);
311 assert!(!result.fields[1].required);
312 }
313}