yaml_schema/schemas/
string.rs1use std::collections::HashMap;
2
3use log::debug;
4use regex::Regex;
5use saphyr::AnnotatedMapping;
6use saphyr::MarkedYaml;
7use saphyr::Scalar;
8use saphyr::YamlData;
9
10use crate::loader;
11use crate::utils::format_hash_map;
12use crate::utils::format_marker;
13
14#[derive(Default)]
16pub struct StringSchema {
17 pub min_length: Option<usize>,
18 pub max_length: Option<usize>,
19 pub pattern: Option<Regex>,
20}
21
22impl std::fmt::Debug for StringSchema {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 let mut h = HashMap::new();
25 if let Some(min_length) = self.min_length {
26 h.insert("minLength".to_string(), min_length.to_string());
27 }
28 if let Some(max_length) = self.max_length {
29 h.insert("maxLength".to_string(), max_length.to_string());
30 }
31 if let Some(pattern) = &self.pattern {
32 h.insert("pattern".to_string(), pattern.as_str().to_string());
33 }
34 write!(f, "StringSchema {}", format_hash_map(&h))
35 }
36}
37
38impl StringSchema {
39 pub fn builder() -> StringSchemaBuilder {
40 StringSchemaBuilder::new()
41 }
42}
43
44impl PartialEq for StringSchema {
45 fn eq(&self, other: &Self) -> bool {
46 self.min_length == other.min_length
47 && self.max_length == other.max_length
48 && are_patterns_equivalent(&self.pattern, &other.pattern)
49 }
50}
51
52impl TryFrom<&MarkedYaml<'_>> for StringSchema {
53 type Error = crate::Error;
54
55 fn try_from(value: &MarkedYaml) -> Result<StringSchema, Self::Error> {
56 if let YamlData::Mapping(mapping) = &value.data {
57 Ok(StringSchema::try_from(mapping)?)
58 } else {
59 Err(expected_mapping!(value))
60 }
61 }
62}
63
64impl TryFrom<&AnnotatedMapping<'_, MarkedYaml<'_>>> for StringSchema {
65 type Error = crate::Error;
66
67 fn try_from(mapping: &AnnotatedMapping<'_, MarkedYaml<'_>>) -> crate::Result<Self> {
68 let mut string_schema = StringSchema::default();
69 for (key, value) in mapping.iter() {
70 if let YamlData::Value(Scalar::String(key)) = &key.data {
71 match key.as_ref() {
72 "minLength" => {
73 if let Ok(i) = loader::load_integer_marked(value) {
74 string_schema.min_length = Some(i as usize);
75 } else {
76 return Err(unsupported_type!(
77 "minLength expected integer, but got: {:?}",
78 value
79 ));
80 }
81 }
82 "maxLength" => {
83 if let Ok(i) = loader::load_integer_marked(value) {
84 string_schema.max_length = Some(i as usize);
85 } else {
86 return Err(unsupported_type!(
87 "maxLength expected integer, but got: {:?}",
88 value
89 ));
90 }
91 }
92 "pattern" => {
93 if let YamlData::Value(Scalar::String(s)) = &value.data {
94 let regex = regex::Regex::new(s.as_ref())?;
95 string_schema.pattern = Some(regex);
96 } else {
97 return Err(unsupported_type!(
98 "pattern expected string, but got: {:?}",
99 value
100 ));
101 }
102 }
103 "type" => {
105 if let YamlData::Value(Scalar::String(s)) = &value.data {
106 if s != "string" {
107 return Err(unsupported_type!(
108 "Expected type: string, but got: {}",
109 s
110 ));
111 }
112 } else if let YamlData::Sequence(values) = &value.data {
113 if !values
114 .iter()
115 .any(|v| v.data == MarkedYaml::value_from_str("string").data)
116 {
117 return Err(unsupported_type!(
118 "Expected type: string, but got: {:?}",
119 value
120 ));
121 }
122 } else {
123 return Err(expected_type_is_string!(value));
124 }
125 }
126 _ => {
127 debug!("[StringSchema] Unsupported key for `type: string`: {key}");
128 }
129 }
130 } else {
131 return Err(expected_scalar!(
132 "{} Expected a scalar key, got: {:?}",
133 format_marker(&key.span.start),
134 key
135 ));
136 }
137 }
138 Ok(string_schema)
139 }
140}
141fn are_patterns_equivalent(a: &Option<Regex>, b: &Option<Regex>) -> bool {
145 match (a, b) {
146 (Some(a), Some(b)) => a.as_str() == b.as_str(),
147 (None, None) => true,
148 _ => false,
149 }
150}
151
152impl std::fmt::Display for StringSchema {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(
155 f,
156 "StringSchema {{ min_length: {:?}, max_length: {:?}, pattern: {:?} }}",
157 self.min_length, self.max_length, self.pattern
158 )
159 }
160}
161
162pub struct StringSchemaBuilder(StringSchema);
163
164impl Default for StringSchemaBuilder {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl StringSchemaBuilder {
171 pub fn new() -> Self {
172 Self(StringSchema::default())
173 }
174
175 pub fn build(&mut self) -> StringSchema {
176 std::mem::take(&mut self.0)
177 }
178
179 pub fn min_length(&mut self, min_length: usize) -> &mut Self {
180 self.0.min_length = Some(min_length);
181 self
182 }
183
184 pub fn max_length(&mut self, max_length: usize) -> &mut Self {
185 self.0.max_length = Some(max_length);
186 self
187 }
188
189 pub fn pattern(&mut self, pattern: Regex) -> &mut Self {
190 self.0.pattern = Some(pattern);
191 self
192 }
193}