data_doctor_core/
schema.rs

1//! # Schema Module
2//!
3//! Defines data schemas and validation rules for different data formats.
4//! This module provides structures for defining expected data shapes,
5//! types, constraints, and validation rules.
6
7use std::collections::HashMap;
8
9/// Represents a data type in a schema
10#[derive(Debug, Clone, PartialEq)]
11pub enum DataType {
12    String,
13    Integer,
14    Float,
15    Boolean,
16    Date,
17    Email,
18    Url,
19    Custom(String),
20}
21
22/// Represents a field constraint
23#[derive(Debug, Clone)]
24pub enum Constraint {
25    Required,
26    MinLength(usize),
27    MaxLength(usize),
28    MinValue(f64),
29    MaxValue(f64),
30    Pattern(String),
31    Unique,
32    Custom(String),
33}
34
35/// Represents a single field in a schema
36#[derive(Debug, Clone)]
37pub struct FieldSchema {
38    pub name: String,
39    pub data_type: DataType,
40    pub constraints: Vec<Constraint>,
41    pub nullable: bool,
42    pub description: Option<String>,
43}
44
45impl FieldSchema {
46    /// Creates a new field schema
47    pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
48        Self {
49            name: name.into(),
50            data_type,
51            constraints: Vec::new(),
52            nullable: false,
53            description: None,
54        }
55    }
56
57    /// Adds a constraint to the field
58    pub fn with_constraint(mut self, constraint: Constraint) -> Self {
59        self.constraints.push(constraint);
60        self
61    }
62
63    /// Marks the field as nullable
64    pub fn nullable(mut self) -> Self {
65        self.nullable = true;
66        self
67    }
68}
69
70/// Represents a complete data schema
71#[derive(Debug, Clone)]
72pub struct Schema {
73    pub name: String,
74    pub fields: HashMap<String, FieldSchema>,
75    pub description: Option<String>,
76}
77
78impl Schema {
79    /// Creates a new schema
80    pub fn new(name: impl Into<String>) -> Self {
81        Self {
82            name: name.into(),
83            fields: HashMap::new(),
84            description: None,
85        }
86    }
87
88    /// Adds a field to the schema
89    pub fn add_field(mut self, field: FieldSchema) -> Self {
90        self.fields.insert(field.name.clone(), field);
91        self
92    }
93
94    /// Gets a field by name
95    pub fn get_field(&self, name: &str) -> Option<&FieldSchema> {
96        self.fields.get(name)
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_field_schema_creation() {
106        let field =
107            FieldSchema::new("email", DataType::Email).with_constraint(Constraint::Required);
108
109        assert_eq!(field.name, "email");
110        assert_eq!(field.data_type, DataType::Email);
111        assert_eq!(field.constraints.len(), 1);
112    }
113
114    #[test]
115    fn test_schema_creation() {
116        let schema = Schema::new("user").add_field(FieldSchema::new("name", DataType::String));
117
118        assert_eq!(schema.name, "user");
119        assert_eq!(schema.fields.len(), 1);
120    }
121}