avocado_schema/core/constraint/string/
pattern.rs1use crate::core::constraint::Constraint;
2use crate::core::value::FieldValue;
3use anyhow::{anyhow, Result};
4use regex::Regex;
5use serde::de::{Error, Visitor};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::fmt::Formatter;
8
9#[derive(Clone, Debug)]
10pub struct Pattern {
11 pub pattern: Regex,
12}
13
14impl Serialize for Pattern {
15 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
16 where
17 S: Serializer,
18 {
19 serializer.serialize_str(self.pattern.as_str())
20 }
21}
22
23impl<'de> Deserialize<'de> for Pattern {
24 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25 where
26 D: Deserializer<'de>,
27 {
28 deserializer.deserialize_string(PatternVisitor)
29 }
30}
31
32struct PatternVisitor;
33
34impl<'de> Visitor<'de> for PatternVisitor {
35 type Value = Pattern;
36
37 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
38 write!(
39 formatter,
40 "string field [pattern] needs to be a valid regular expression"
41 )
42 }
43
44 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
45 where
46 E: Error,
47 {
48 let pattern = Regex::new(v).map_err(|e| Error::custom(e.to_string()))?;
49 Ok(Pattern { pattern })
50 }
51}
52
53impl Constraint for Pattern {
54 fn validate(&self, val: &FieldValue) -> Result<()> {
55 match val {
56 FieldValue::String(v) if !self.pattern.is_match(v.as_str()) => Err(anyhow!(format!(
57 "{} does not match pattern {} ({})",
58 self.pattern, v, "Pattern"
59 ))),
60 _ => Ok(()),
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::core::constraint::string::pattern::Pattern;
68 use crate::core::constraint::Constraint;
69 use crate::core::value::FieldValue;
70
71 #[test]
72 fn test_pattern() {
73 let constraint = Pattern {
74 pattern: r"^\d{4}-\d{2}-\d{2}$".parse().unwrap(),
75 };
76
77 let value = FieldValue::String("2010-03-14".to_string());
78 assert!(constraint.validate(&value).is_ok());
79
80 let value = FieldValue::String("Not Match".to_string());
81 assert!(constraint.validate(&value).is_err());
82 }
83}