1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15pub struct Schema {
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub database: Option<String>,
19
20 #[serde(default)]
22 pub tables: Vec<Table>,
23
24 #[serde(default)]
26 pub functions: Vec<Function>,
27}
28
29impl Schema {
30 #[must_use]
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 #[must_use]
38 pub fn with_database(database: impl Into<String>) -> Self {
39 Self {
40 database: Some(database.into()),
41 ..Self::default()
42 }
43 }
44
45 pub fn add_table(&mut self, table: Table) -> &mut Self {
47 self.tables.push(table);
48 self
49 }
50
51 pub fn add_function(&mut self, function: Function) -> &mut Self {
53 self.functions.push(function);
54 self
55 }
56
57 #[must_use]
59 pub fn table(mut self, table: Table) -> Self {
60 self.tables.push(table);
61 self
62 }
63
64 #[must_use]
66 pub fn function(mut self, function: Function) -> Self {
67 self.functions.push(function);
68 self
69 }
70
71 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 self.tables.is_empty() && self.functions.is_empty()
75 }
76
77 #[must_use]
79 pub fn get_table(&self, name: &str) -> Option<&Table> {
80 self.tables.iter().find(|t| t.name.eq_ignore_ascii_case(name))
81 }
82
83 #[must_use]
85 pub fn get_function(&self, name: &str) -> Option<&Function> {
86 self.functions
87 .iter()
88 .find(|f| f.name.eq_ignore_ascii_case(name))
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct Table {
95 pub name: String,
97
98 #[serde(default)]
100 pub columns: Vec<Column>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub description: Option<String>,
105}
106
107impl Table {
108 #[must_use]
110 pub fn new(name: impl Into<String>) -> Self {
111 Self {
112 name: name.into(),
113 columns: Vec::new(),
114 description: None,
115 }
116 }
117
118 pub fn add_column(&mut self, column: Column) -> &mut Self {
120 self.columns.push(column);
121 self
122 }
123
124 #[must_use]
126 pub fn column(mut self, column: Column) -> Self {
127 self.columns.push(column);
128 self
129 }
130
131 #[must_use]
133 pub fn with_column(mut self, name: impl Into<String>, data_type: impl Into<String>) -> Self {
134 self.columns.push(Column::new(name, data_type));
135 self
136 }
137
138 #[must_use]
140 pub fn description(mut self, desc: impl Into<String>) -> Self {
141 self.description = Some(desc.into());
142 self
143 }
144
145 #[must_use]
147 pub fn get_column(&self, name: &str) -> Option<&Column> {
148 self.columns
149 .iter()
150 .find(|c| c.name.eq_ignore_ascii_case(name))
151 }
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Column {
157 pub name: String,
159
160 pub data_type: String,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub description: Option<String>,
166}
167
168impl Column {
169 #[must_use]
171 pub fn new(name: impl Into<String>, data_type: impl Into<String>) -> Self {
172 Self {
173 name: name.into(),
174 data_type: data_type.into(),
175 description: None,
176 }
177 }
178
179 #[must_use]
181 pub fn description(mut self, desc: impl Into<String>) -> Self {
182 self.description = Some(desc.into());
183 self
184 }
185
186 #[must_use]
188 pub fn string(name: impl Into<String>) -> Self {
189 Self::new(name, "string")
190 }
191
192 #[must_use]
194 pub fn long(name: impl Into<String>) -> Self {
195 Self::new(name, "long")
196 }
197
198 #[must_use]
200 pub fn real(name: impl Into<String>) -> Self {
201 Self::new(name, "real")
202 }
203
204 #[must_use]
206 pub fn bool(name: impl Into<String>) -> Self {
207 Self::new(name, "bool")
208 }
209
210 #[must_use]
212 pub fn datetime(name: impl Into<String>) -> Self {
213 Self::new(name, "datetime")
214 }
215
216 #[must_use]
218 pub fn timespan(name: impl Into<String>) -> Self {
219 Self::new(name, "timespan")
220 }
221
222 #[must_use]
224 pub fn guid(name: impl Into<String>) -> Self {
225 Self::new(name, "guid")
226 }
227
228 #[must_use]
230 pub fn dynamic(name: impl Into<String>) -> Self {
231 Self::new(name, "dynamic")
232 }
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
237pub struct Function {
238 pub name: String,
240
241 #[serde(default)]
243 pub parameters: Vec<Parameter>,
244
245 pub return_type: String,
247
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub body: Option<String>,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub description: Option<String>,
255}
256
257impl Function {
258 #[must_use]
260 pub fn new(name: impl Into<String>, return_type: impl Into<String>) -> Self {
261 Self {
262 name: name.into(),
263 parameters: Vec::new(),
264 return_type: return_type.into(),
265 body: None,
266 description: None,
267 }
268 }
269
270 pub fn add_parameter(&mut self, param: Parameter) -> &mut Self {
272 self.parameters.push(param);
273 self
274 }
275
276 #[must_use]
278 pub fn param(mut self, name: impl Into<String>, data_type: impl Into<String>) -> Self {
279 self.parameters.push(Parameter::new(name, data_type));
280 self
281 }
282
283 #[must_use]
285 pub fn body(mut self, body: impl Into<String>) -> Self {
286 self.body = Some(body.into());
287 self
288 }
289
290 #[must_use]
292 pub fn description(mut self, desc: impl Into<String>) -> Self {
293 self.description = Some(desc.into());
294 self
295 }
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct Parameter {
301 pub name: String,
303
304 pub data_type: String,
306
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub default_value: Option<String>,
310}
311
312impl Parameter {
313 #[must_use]
315 pub fn new(name: impl Into<String>, data_type: impl Into<String>) -> Self {
316 Self {
317 name: name.into(),
318 data_type: data_type.into(),
319 default_value: None,
320 }
321 }
322
323 #[must_use]
325 pub fn default(mut self, value: impl Into<String>) -> Self {
326 self.default_value = Some(value.into());
327 self
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_schema_builder() {
337 let schema = Schema::with_database("SecurityDB")
338 .table(
339 Table::new("SecurityEvent")
340 .with_column("TimeGenerated", "datetime")
341 .with_column("Account", "string")
342 .with_column("EventID", "long")
343 .with_column("Computer", "string"),
344 )
345 .table(
346 Table::new("SigninLogs")
347 .with_column("TimeGenerated", "datetime")
348 .with_column("UserPrincipalName", "string")
349 .with_column("IPAddress", "string")
350 .with_column("ResultType", "string"),
351 );
352
353 assert_eq!(schema.database, Some("SecurityDB".to_string()));
354 assert_eq!(schema.tables.len(), 2);
355 assert_eq!(schema.tables[0].columns.len(), 4);
356 }
357
358 #[test]
359 fn test_schema_serialization() {
360 let schema = Schema::new().table(
361 Table::new("Test")
362 .with_column("Id", "long")
363 .with_column("Name", "string"),
364 );
365
366 let json = serde_json::to_string(&schema).unwrap();
367 let parsed: Schema = serde_json::from_str(&json).unwrap();
368
369 assert_eq!(parsed.tables.len(), 1);
370 assert_eq!(parsed.tables[0].name, "Test");
371 assert_eq!(parsed.tables[0].columns.len(), 2);
372 }
373}