acorn_lib/schema/
validate.rs1use crate::constants::*;
2use fancy_regex::Regex;
3use std::collections::HashMap;
4use validator::ValidationError;
5use validator::{Validate, ValidationErrorsKind};
6
7pub fn format_errors(found: Vec<Option<HashMap<String, ValidationErrorsKind>>>) -> HashMap<String, ValidationErrorsKind> {
8 found.clone().into_iter().flatten().fold(HashMap::new(), |mut acc, x| {
9 for (key, value) in x.iter() {
10 acc.insert(key.clone(), value.clone());
11 }
12 acc
13 })
14}
15pub fn get_validation_errors<T: Validate>(attribute: T) -> Option<HashMap<String, ValidationErrorsKind>> {
16 match attribute.validate() {
17 | Ok(_) => None,
18 | Err(err) => Some(
19 err.into_errors()
20 .into_iter()
21 .map(|(k, v)| (k.to_string(), v))
22 .collect::<HashMap<String, ValidationErrorsKind>>(),
23 ),
24 }
25}
26pub fn get_error_count(errors: HashMap<String, ValidationErrorsKind>) -> usize {
27 errors
28 .clone()
29 .into_values()
30 .map(|v| match v {
31 | ValidationErrorsKind::Field(_) => 1,
32 | ValidationErrorsKind::Struct(errors) => errors.into_errors().len(),
33 | ValidationErrorsKind::List(_) => 0,
34 })
35 .sum()
36}
37pub fn has_image_extension(value: &str) -> Result<(), ValidationError> {
38 const MESSAGE: &str = "Please provide a path with a PNG, JPEG, or SVG extension";
39 match RE_IMAGE_EXTENSION.is_match(value) {
40 | Ok(value) if value => Ok(()),
41 | _ => Err(ValidationError::new("image").with_message(MESSAGE.into())),
42 }
43}
44pub fn is_doi(value: &str) -> Result<(), ValidationError> {
45 const MESSAGE: &str = "Please provide a valid DOI, by itself and without domain or 'doi:' prefix.";
46 match RE_DOI.is_match(value) {
47 | Ok(value) if value => Ok(()),
48 | _ => Err(ValidationError::new("doi").with_message(MESSAGE.into())),
49 }
50}
51pub fn is_ip6(value: &str) -> Result<(), ValidationError> {
52 const MESSAGE: &str = "Please provide a valid IP6 address";
53 match RE_IP6.is_match(value) {
54 | Ok(value) if value => Ok(()),
55 | _ => Err(ValidationError::new("IP6").with_message(MESSAGE.into())),
56 }
57}
58pub fn is_kebabcase(value: &str) -> Result<(), ValidationError> {
59 const MESSAGE: &str = "Please provide an ID in kebab-case format";
60 let kebab = match Regex::new(r"[ *_./\!@#$%^&(){}]") {
61 | Ok(re) => re.replace_all(value, "").trim().to_string(),
62 | Err(err) => err.to_string(),
63 };
64 match kebab.to_lowercase().eq(&value) {
65 | true => Ok(()),
66 | _ => Err(ValidationError::new("kebabcase").with_message(MESSAGE.into())),
67 }
68}
69pub fn is_phone_number(value: &str) -> Result<(), ValidationError> {
70 const MESSAGE: &str = "Please provide a valid phone number";
71 match RE_PHONE.is_match(value) {
72 | Ok(value) if value => Ok(()),
73 | _ => Err(ValidationError::new("phone").with_message(MESSAGE.into())),
74 }
75}
76pub fn is_raid(value: &str) -> Result<(), ValidationError> {
77 const MESSAGE: &str = "Please provide a valid RAiD";
78 match RE_DOI.is_match(value) {
79 | Ok(value) if value => Ok(()),
80 | _ => Err(ValidationError::new("raid").with_message(MESSAGE.into())),
81 }
82}
83pub fn is_ror(value: &str) -> Result<(), ValidationError> {
84 const MESSAGE: &str = "Please provide a valid ROR";
85 match RE_ROR.is_match(value) {
86 | Ok(value) if value => Ok(()),
87 | _ => Err(ValidationError::new("ror").with_message(MESSAGE.into())),
88 }
89}
90pub fn validate_attribute_areas(value: &[String]) -> Result<(), ValidationError> {
91 const MAX_LENGTH: usize = MAX_LENGTH_RESEARCH_AREA;
92 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
93 match is_valid {
94 | true => Ok(()),
95 | _ => Err(ValidationError::new("area").with_message(format!("Each area should be less than {} characters", MAX_LENGTH).into())),
96 }
97}
98pub fn validate_attribute_capabilities(value: &[String]) -> Result<(), ValidationError> {
99 const MAX_LENGTH: usize = MAX_LENGTH_CAPABILIY;
100 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
101 match is_valid {
102 | true => Ok(()),
103 | _ => Err(ValidationError::new("capability").with_message(format!("Each capability should be less than {} characters", MAX_LENGTH).into())),
104 }
105}
106pub fn validate_attribute_facilities(value: &[String]) -> Result<(), ValidationError> {
107 const MAX_LENGTH: usize = MAX_LENGTH_FACILITY;
108 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
109 match is_valid {
110 | true => Ok(()),
111 | _ => Err(ValidationError::new("facility").with_message(format!("Each facility should be less than {} characters", MAX_LENGTH).into())),
112 }
113}
114pub fn validate_attribute_impact(value: &[String]) -> Result<(), ValidationError> {
115 const MAX_LENGTH: usize = MAX_LENGTH_IMPACT;
116 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
117 match is_valid {
118 | true => Ok(()),
119 | _ => Err(ValidationError::new("impact").with_message(format!("Each impact item should be less than {} characters", MAX_LENGTH).into())),
120 }
121}
122pub fn validate_attribute_technical(value: &[String]) -> Result<(), ValidationError> {
123 const MAX_LENGTH: usize = MAX_LENGTH_TECHNICAL;
124 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
125 match is_valid {
126 | true => Ok(()),
127 | _ => Err(ValidationError::new("technical approach")
128 .with_message(format!("Each element of technical approach should be less than {} characters", MAX_LENGTH).into())),
129 }
130}
131pub fn validate_attribute_outcomes(value: &[String]) -> Result<(), ValidationError> {
132 const MAX_LENGTH: usize = MAX_LENGTH_OUTCOME;
133 match value.iter().all(|x| x.len() <= MAX_LENGTH) {
134 | true => {
135 let is_valid = value.iter().all(|x| !x.trim().ends_with("."));
136 match is_valid {
137 | true => Ok(()),
138 | _ => Err(ValidationError::new("outcome").with_message("Outcomes should be phrases that do not end in periods".into())),
139 }
140 }
141 | _ => Err(ValidationError::new("outcome").with_message(format!("Each outcome should be less than {} characters", MAX_LENGTH).into())),
142 }
143}