green_barrel/models/
validation.rs

1//! Helper methods to validate data before saving or updating to the database.
2
3use async_trait::async_trait;
4use mongodb::{
5    bson::{doc, oid::ObjectId, Bson, Document},
6    Collection,
7};
8use regex::{Regex, RegexBuilder};
9use serde_json::value::Value;
10use std::error::Error;
11
12/// Helper methods to validate data before saving or updating to the database.
13// *************************************************************************************************
14#[async_trait(?Send)]
15pub trait Validation {
16    /// Validation of `minlength`.
17    // ---------------------------------------------------------------------------------------------
18    fn check_minlength(minlength: usize, value: &str) -> Result<(), Box<dyn Error>> {
19        if minlength > 0 && value.encode_utf16().count() < minlength {
20            Err(t!("min_chars", count = minlength))?
21        }
22        Ok(())
23    }
24
25    /// Validation of `maxlength`.
26    // ---------------------------------------------------------------------------------------------
27    fn check_maxlength(maxlength: usize, value: &str) -> Result<(), Box<dyn Error>> {
28        if maxlength > 0 && value.encode_utf16().count() > maxlength {
29            Err(t!("max_chars", count = maxlength))?
30        }
31        Ok(())
32    }
33
34    /// Accumulation of errors.
35    // ---------------------------------------------------------------------------------------------
36    fn accumula_err(field: &mut Value, err: &str) {
37        let err_vec = field["errors"].as_array_mut().unwrap();
38        let err = serde_json::to_value(err).unwrap();
39        if !err_vec.contains(&err) {
40            err_vec.push(err);
41        }
42    }
43
44    /// Validation Email, Url, IP, IPv4, IPv6, Color.
45    // ---------------------------------------------------------------------------------------------
46    fn validation(field_type: &str, value: &str) -> Result<(), Box<dyn Error>> {
47        match field_type {
48            "EmailField" => {
49                if !validator::validate_email(value) {
50                    Err(t!("invalid_email"))?
51                }
52            }
53            "URLField" => {
54                if !validator::validate_url(value) {
55                    Err(t!("invalid_url"))?
56                }
57            }
58            "IPField" => {
59                if !validator::validate_ip(value) {
60                    Err(t!("invalid_ip"))?
61                }
62            }
63            "IPv4Field" => {
64                if !validator::validate_ip_v4(value) {
65                    Err(t!("invalid_ipv4"))?
66                }
67            }
68            "IPv6Field" => {
69                if !validator::validate_ip_v6(value) {
70                    Err(t!("invalid_ipv6"))?
71                }
72            }
73            "ColorField" => {
74                if !(RegexBuilder::new(
75                    r"^(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6}|[a-f0-9]{8})\b|(?:rgb|hsl)a?\([^\)]*\)$",
76                )
77                .case_insensitive(true)
78                .build()
79                .unwrap()
80                .is_match(value))
81                {
82                    Err(t!("invalid_color"))?
83                }
84            }
85            _ => return Ok(()),
86        }
87        Ok(())
88    }
89
90    /// Validation of `unique`.
91    // ---------------------------------------------------------------------------------------------
92    async fn check_unique(
93        hash: &str,
94        field_name: &str,
95        field_value_bson: &Bson,
96        coll: &Collection<Document>,
97    ) -> Result<(), Box<dyn Error>> {
98        //
99        let object_id = ObjectId::parse_str(hash);
100        let mut filter = doc! { field_name: field_value_bson };
101        if let Ok(id) = object_id {
102            // If the document is will updated.
103            filter = doc! {
104                "$and": [
105                    { "_id": { "$ne": id } },
106                    filter
107                ]
108            };
109        }
110        let count = coll.count_documents(filter, None).await?;
111        if count > 0 {
112            Err(t!("not_unique"))?
113        }
114        Ok(())
115    }
116
117    /// Validation field attribute `regex`.
118    // ----------------------------------------------------------------------------------------------
119    fn regex_validation(field_value: &str, regex_str: &str) -> Result<(), Box<dyn Error>> {
120        let pattern = Regex::new(regex_str)?;
121        if !field_value.is_empty() && !pattern.is_match(field_value) {
122            Err("")?
123        }
124        Ok(())
125    }
126}