Skip to main content

wasm_dbms_api/dbms/validate/
web.rs

1use crate::prelude::{DbmsError, Validate, Value};
2
3/// A validator that checks if a string is a valid MIME type.
4///
5/// # Example
6///
7/// ```rust
8/// use wasm_dbms_api::prelude::{MimeTypeValidator, Validate, Value};
9///
10/// let validator = MimeTypeValidator;
11/// let valid_mime = Value::Text(wasm_dbms_api::prelude::Text("text/plain".into()));
12/// assert!(validator.validate(&valid_mime).is_ok());
13/// let invalid_mime = Value::Text(wasm_dbms_api::prelude::Text("invalid-mime".into()));
14/// assert!(validator.validate(&invalid_mime).is_err());
15/// ```
16pub struct MimeTypeValidator;
17
18impl Validate for MimeTypeValidator {
19    fn validate(&self, value: &crate::prelude::Value) -> crate::prelude::DbmsResult<()> {
20        let Value::Text(text) = value else {
21            return Err(DbmsError::Validation("Value is not a Text".to_string()));
22        };
23
24        let s = &text.0;
25
26        // must have exactly '/' character
27        let parts: Vec<&str> = s.split('/').collect();
28        if parts.len() != 2 {
29            return Err(DbmsError::Validation(format!(
30                "MIME type '{s}' must contain exactly one '/'"
31            )));
32        }
33
34        let is_valid_part = |part: &str| {
35            !part.is_empty()
36                && part
37                    .chars()
38                    .all(|c| c.is_ascii_alphanumeric() || "+.-".contains(c))
39        };
40
41        if !is_valid_part(parts[0]) {
42            return Err(DbmsError::Validation(format!(
43                "MIME type '{s}' has invalid type part"
44            )));
45        }
46        if !is_valid_part(parts[1]) {
47            return Err(DbmsError::Validation(format!(
48                "MIME type '{s}' has invalid subtype part"
49            )));
50        }
51
52        Ok(())
53    }
54}
55
56/// A validator that checks if a string is a valid URL.
57///
58/// # Example
59///
60/// ```rust
61/// use wasm_dbms_api::prelude::{UrlValidator, Validate, Value};
62/// let validator = UrlValidator;
63/// let valid_url = Value::Text(wasm_dbms_api::prelude::Text("http://example.com".into()));
64/// assert!(validator.validate(&valid_url).is_ok());
65/// ```
66pub struct UrlValidator;
67
68impl Validate for UrlValidator {
69    fn validate(&self, value: &crate::prelude::Value) -> crate::prelude::DbmsResult<()> {
70        let Value::Text(text) = value else {
71            return Err(DbmsError::Validation("Value is not a Text".to_string()));
72        };
73
74        let s = &text.0;
75
76        if url::Url::parse(s).is_err() {
77            return Err(DbmsError::Validation(format!(
78                "Value '{s}' is not a valid URL"
79            )));
80        }
81
82        Ok(())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88
89    use super::*;
90    use crate::prelude::Text;
91
92    #[test]
93    fn test_should_not_validate_mime_if_not_text() {
94        let value = Value::Uint32(crate::prelude::Uint32(42));
95        let result = MimeTypeValidator.validate(&value);
96        assert!(result.is_err());
97    }
98
99    #[test]
100    fn test_mime_type_validator() {
101        let valid_mime_types = vec![
102            "text/plain",
103            "image/jpeg",
104            "application/json",
105            "application/vnd.api+json",
106            "audio/mpeg",
107        ];
108        for mime in valid_mime_types {
109            let value = Value::Text(Text(mime.to_string()));
110            assert!(
111                MimeTypeValidator.validate(&value).is_ok(),
112                "MIME type '{mime}' should be valid"
113            );
114        }
115    }
116
117    #[test]
118    fn test_invalid_mime_type_validator() {
119        let invalid_mime_types = vec![
120            "textplain",
121            "image//jpeg",
122            "/json",
123            "application/vnd.api+json/extra",
124            "audio/mpeg/",
125            "audio/mpe g",
126        ];
127        for mime in invalid_mime_types {
128            let value = Value::Text(Text(mime.to_string()));
129            assert!(
130                MimeTypeValidator.validate(&value).is_err(),
131                "MIME type '{mime}' should be invalid"
132            );
133        }
134    }
135
136    #[test]
137    fn test_url_validator() {
138        let valid_urls = vec![
139            "http://example.com",
140            "https://example.com/path?query=param#fragment",
141            "ftp://ftp.example.com/resource",
142            "mailto:christian@example.com",
143        ];
144        for url in valid_urls {
145            let value = Value::Text(Text(url.to_string()));
146            assert!(
147                UrlValidator.validate(&value).is_ok(),
148                "URL '{url}' should be valid"
149            );
150        }
151    }
152
153    #[test]
154    fn test_invalid_url_validator() {
155        let invalid_urls = vec![
156            //"htp:/example.com",
157            "://missing.scheme.com",
158            "http//missing.colon.com",
159            "justastring",
160            "http://in valid.com",
161        ];
162        for url in invalid_urls {
163            let value = Value::Text(Text(url.to_string()));
164            assert!(
165                UrlValidator.validate(&value).is_err(),
166                "URL '{url}' should be invalid"
167            );
168        }
169    }
170
171    #[test]
172    fn test_should_not_validate_url_if_not_text() {
173        let value = Value::Uint32(crate::prelude::Uint32(42));
174        let result = UrlValidator.validate(&value);
175        assert!(result.is_err());
176    }
177}