acorn_lib/schema/
validate.rs1#![deny(missing_docs)]
2use crate::constants::*;
6use fancy_regex::Regex;
7use validator::ValidationError;
8
9pub fn format_phone_number(value: &str) -> Result<String, ValidationError> {
11 const MESSAGE: &str = "Unable to format telephone number";
12 match RE_PHONE.captures(value) {
13 | Ok(value) => match value {
14 | Some(captures) => {
15 let country_code = match captures.name("country") {
16 | Some(value) => Some(value.as_str().trim().to_string()),
17 | None => None,
18 };
19 let area_code = match captures.name("area") {
20 | Some(value) => Some(value.as_str().replace("(", "").replace(")", "")),
21 | None => None,
22 };
23 let prefix = match captures.name("prefix") {
24 | Some(value) => Some(value.as_str().to_string()),
25 | None => None,
26 };
27 let line = match captures.name("line") {
28 | Some(value) => Some(value.as_str().to_string()),
29 | None => None,
30 };
31 Ok([country_code, area_code, prefix, line]
32 .into_iter()
33 .flatten()
34 .collect::<Vec<String>>()
35 .join("."))
36 }
37 | None => Err(ValidationError::new("telephone").with_message(MESSAGE.into())),
38 },
39 | _ => Err(ValidationError::new("telephone").with_message(MESSAGE.into())),
40 }
41}
42pub fn has_image_extension(value: &str) -> Result<(), ValidationError> {
44 const MESSAGE: &str = "Please provide a path with a PNG, JPEG, GIF, WEBP, TIFF or SVG extension";
45 match RE_IMAGE_EXTENSION.is_match(value) {
46 | Ok(value) if value => Ok(()),
47 | _ => Err(ValidationError::new("image").with_message(MESSAGE.into())),
48 }
49}
50pub fn is_doi(value: &str) -> Result<(), ValidationError> {
52 const MESSAGE: &str = "Please provide a valid DOI, by itself and without domain or 'doi:' prefix.";
53 match RE_DOI.is_match(value) {
54 | Ok(value) if value => Ok(()),
55 | _ => Err(ValidationError::new("doi").with_message(MESSAGE.into())),
56 }
57}
58pub fn is_ip6(value: &str) -> Result<(), ValidationError> {
60 const MESSAGE: &str = "Please provide a valid IP6 address";
61 match RE_IP6.is_match(value) {
62 | Ok(value) if value => Ok(()),
63 | _ => Err(ValidationError::new("IP6").with_message(MESSAGE.into())),
64 }
65}
66pub fn is_kebabcase(value: &str) -> Result<(), ValidationError> {
68 const MESSAGE: &str = "Please provide an ID in kebab-case format";
69 let kebab = match Regex::new(r"[ *_./\!@#$%^&(){}]") {
70 | Ok(re) => re.replace_all(value, "").trim().to_string(),
71 | Err(err) => err.to_string(),
72 };
73 match kebab.to_lowercase().eq(&value) {
74 | true => Ok(()),
75 | _ => Err(ValidationError::new("kebabcase").with_message(MESSAGE.into())),
76 }
77}
78pub fn is_phone_number(value: &str) -> Result<(), ValidationError> {
82 const MESSAGE: &str = "Please provide a valid phone number";
83 let is_fake = match RE_FAKE_PHONE.is_match(value) {
84 | Ok(value) if value => true,
85 | _ => false,
86 };
87 match RE_PHONE.is_match(value) {
88 | Ok(value) if value && !is_fake => Ok(()),
89 | _ => Err(ValidationError::new("phone").with_message(MESSAGE.into())),
90 }
91}
92pub fn is_raid(value: &str) -> Result<(), ValidationError> {
94 const MESSAGE: &str = "Please provide a valid RAiD";
95 match RE_RAID.is_match(value) {
96 | Ok(value) if value => Ok(()),
97 | _ => Err(ValidationError::new("raid").with_message(MESSAGE.into())),
98 }
99}
100pub fn is_ror(value: &str) -> Result<(), ValidationError> {
102 const MESSAGE: &str = "Please provide a valid ROR";
103 match RE_ROR.is_match(value) {
104 | Ok(value) if value => Ok(()),
105 | _ => Err(ValidationError::new("ror").with_message(MESSAGE.into())),
106 }
107}
108pub fn validate_attribute_approach(value: &[String]) -> Result<(), ValidationError> {
110 const MAX_LENGTH: usize = MAX_LENGTH_APPROACH;
111 let message: String = format!("Each approach statement should be less than {MAX_LENGTH} characters");
112 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
113 match is_valid {
114 | true => Ok(()),
115 | _ => Err(ValidationError::new("approach").with_message(message.into())),
116 }
117}
118pub fn validate_attribute_areas(value: &[String]) -> Result<(), ValidationError> {
120 const MAX_LENGTH: usize = MAX_LENGTH_RESEARCH_AREA;
121 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
122 match is_valid {
123 | true => Ok(()),
124 | _ => Err(ValidationError::new("area").with_message(format!("Each area should be less than {MAX_LENGTH} characters").into())),
125 }
126}
127pub fn validate_attribute_capabilities(value: &[String]) -> Result<(), ValidationError> {
131 const MAX_LENGTH: usize = MAX_LENGTH_CAPABILIY;
132 let is_valid = value.iter().all(|x| x.len() <= MAX_LENGTH);
133 match is_valid {
134 | true => Ok(()),
135 | _ => Err(ValidationError::new("capability").with_message(format!("Each capability should be less than {MAX_LENGTH} characters").into())),
136 }
137}
138pub fn validate_attribute_impact(value: &[String]) -> Result<(), ValidationError> {
143 const MAX_LENGTH: usize = MAX_LENGTH_IMPACT;
144 match value.iter().all(|x| x.len() <= MAX_LENGTH) {
145 | true => {
146 let all_periods = value.iter().all(|x| x.trim().ends_with("."));
147 let no_periods = value.iter().all(|x| !x.trim().ends_with("."));
148 let is_valid = all_periods || no_periods;
149 match is_valid {
150 | true => Ok(()),
151 | _ => Err(ValidationError::new("impact")
152 .with_message("Impact statements should be all sentences with periods or all phrases without periods".into())),
153 }
154 }
155 | _ => Err(ValidationError::new("impact").with_message(format!("Each impact statement should be less than {MAX_LENGTH} characters").into())),
156 }
157}