1use std::collections::HashMap;
31use std::fmt;
32
33pub mod schema;
34pub mod validators;
35
36pub use schema::*;
37pub use validators::*;
38
39pub const VERSION: &str = "1.0.0";
41
42#[derive(Debug, Clone)]
48pub struct FieldError {
49 pub field: String,
50 pub message: String,
51 pub value: Option<String>,
52}
53
54impl fmt::Display for FieldError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "{}: {}", self.field, self.message)
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct ValidationError {
63 pub errors: Vec<FieldError>,
64}
65
66impl ValidationError {
67 pub fn new(errors: Vec<FieldError>) -> Self {
68 Self { errors }
69 }
70
71 pub fn single(field: impl Into<String>, message: impl Into<String>) -> Self {
72 Self {
73 errors: vec![FieldError {
74 field: field.into(),
75 message: message.into(),
76 value: None,
77 }],
78 }
79 }
80}
81
82impl fmt::Display for ValidationError {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "Validation failed with {} error(s):", self.errors.len())?;
85 for error in &self.errors {
86 write!(f, "\n - {}", error)?;
87 }
88 Ok(())
89 }
90}
91
92impl std::error::Error for ValidationError {}
93
94pub type Result<T> = std::result::Result<T, ValidationError>;
96
97#[derive(Debug, Clone, PartialEq)]
103pub enum ValidatedValue {
104 Null,
105 Bool(bool),
106 Int(i64),
107 Float(f64),
108 String(String),
109 Reference(ISONReference),
110 Array(Vec<ValidatedValue>),
111 Object(HashMap<String, ValidatedValue>),
112}
113
114impl ValidatedValue {
115 pub fn as_bool(&self) -> Option<bool> {
116 match self {
117 ValidatedValue::Bool(b) => Some(*b),
118 _ => None,
119 }
120 }
121
122 pub fn as_int(&self) -> Option<i64> {
123 match self {
124 ValidatedValue::Int(i) => Some(*i),
125 _ => None,
126 }
127 }
128
129 pub fn as_float(&self) -> Option<f64> {
130 match self {
131 ValidatedValue::Float(f) => Some(*f),
132 ValidatedValue::Int(i) => Some(*i as f64),
133 _ => None,
134 }
135 }
136
137 pub fn as_str(&self) -> Option<&str> {
138 match self {
139 ValidatedValue::String(s) => Some(s),
140 _ => None,
141 }
142 }
143
144 pub fn as_reference(&self) -> Option<&ISONReference> {
145 match self {
146 ValidatedValue::Reference(r) => Some(r),
147 _ => None,
148 }
149 }
150
151 pub fn is_null(&self) -> bool {
152 matches!(self, ValidatedValue::Null)
153 }
154}
155
156#[derive(Debug, Clone, PartialEq)]
158pub struct ISONReference {
159 pub id: String,
160 pub ref_type: Option<String>,
161}
162
163impl ISONReference {
164 pub fn new(id: impl Into<String>) -> Self {
165 Self {
166 id: id.into(),
167 ref_type: None,
168 }
169 }
170
171 pub fn with_type(id: impl Into<String>, ref_type: impl Into<String>) -> Self {
172 Self {
173 id: id.into(),
174 ref_type: Some(ref_type.into()),
175 }
176 }
177
178 pub fn to_ison(&self) -> String {
179 match &self.ref_type {
180 Some(t) => format!(":{}:{}", t, self.id),
181 None => format!(":{}", self.id),
182 }
183 }
184}
185
186#[derive(Debug, Clone)]
192pub struct ValidatedRow {
193 pub fields: HashMap<String, ValidatedValue>,
194}
195
196impl ValidatedRow {
197 pub fn new() -> Self {
198 Self {
199 fields: HashMap::new(),
200 }
201 }
202
203 pub fn get(&self, field: &str) -> Option<&ValidatedValue> {
204 self.fields.get(field)
205 }
206
207 pub fn get_string(&self, field: &str) -> Option<&str> {
208 self.fields.get(field).and_then(|v| v.as_str())
209 }
210
211 pub fn get_int(&self, field: &str) -> Option<i64> {
212 self.fields.get(field).and_then(|v| v.as_int())
213 }
214
215 pub fn get_bool(&self, field: &str) -> Option<bool> {
216 self.fields.get(field).and_then(|v| v.as_bool())
217 }
218}
219
220impl Default for ValidatedRow {
221 fn default() -> Self {
222 Self::new()
223 }
224}
225
226#[derive(Debug, Clone)]
228pub struct ValidatedTable {
229 pub name: String,
230 pub rows: Vec<ValidatedRow>,
231}
232
233impl ValidatedTable {
234 pub fn new(name: impl Into<String>) -> Self {
235 Self {
236 name: name.into(),
237 rows: Vec::new(),
238 }
239 }
240
241 pub fn len(&self) -> usize {
242 self.rows.len()
243 }
244
245 pub fn is_empty(&self) -> bool {
246 self.rows.is_empty()
247 }
248
249 pub fn iter(&self) -> impl Iterator<Item = &ValidatedRow> {
250 self.rows.iter()
251 }
252}
253
254impl std::ops::Index<usize> for ValidatedTable {
255 type Output = ValidatedRow;
256
257 fn index(&self, index: usize) -> &Self::Output {
258 &self.rows[index]
259 }
260}
261
262pub mod prelude {
267 pub use crate::schema::*;
268 pub use crate::validators::*;
269 pub use crate::{
270 FieldError, ISONReference, Result, ValidatedRow, ValidatedTable,
271 ValidatedValue, ValidationError,
272 };
273}