1use std::{error::Error, fmt::Display};
70
71pub enum Sanitize {
78 MatchString(String),
79 MatchStrings(Vec<String>),
80 IsBetween(isize, isize),
81 IsType(DesiredType),
82}
83
84trait Validate {
88 fn validate(&self, input: &str) -> Result<(), FilterErrorNot>;
89}
90
91#[derive(Debug)]
101pub(crate) enum FilterErrorNot {
102 Number(DesiredType),
103 String(DesiredType),
104 Bool(DesiredType),
105 MatchString(String),
106 MatchStrings(Vec<String>),
107 Between(isize, isize),
108}
109
110impl Display for FilterErrorNot {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Self::Number(t) => write!(f, "The value is not a {}, try again!", t),
114 Self::String(t) => write!(f, "The value is not {}, try again!", t),
115 Self::Bool(t) => write!(f, "The value is not a {}, try again!", t),
116 Self::MatchString(s) => write!(f, "The value doesn't match with {}, try again!", s),
117 Self::MatchStrings(v) => write!(
118 f,
119 "The value doesn't match with the options: {}, try again!",
120 v.join(", ")
121 ),
122 Self::Between(n1, n2) => {
123 write!(f, "The value is not between {} and {}, try again!", n1, n2)
124 }
125 }
126 }
127}
128
129impl Error for FilterErrorNot {}
130
131#[macro_export]
146macro_rules! check_type {
147 ($input:expr, $t:ty, $err:expr) => {
148 match $input.parse::<$t>() {
149 Ok(_) => Ok(()),
150 Err(_) => $err,
151 }
152 };
153}
154
155impl Sanitize {
156 pub(crate) fn execute(answer: &str, filters: &[Sanitize]) -> Result<String, FilterErrorNot> {
162 let clean_answer = answer.trim();
163
164 for filter in filters {
165 match filter.validate(clean_answer) {
166 Ok(_) => continue,
167 Err(e) => return Err(e),
168 }
169 }
170 Ok(clean_answer.to_string())
171 }
172}
173
174impl Validate for Sanitize {
175 fn validate(&self, input: &str) -> Result<(), FilterErrorNot> {
176 match self {
177 Sanitize::IsType(ty) => ty.parse(input),
178 Sanitize::MatchString(s) => {
179 if input == s {
180 Ok(())
181 } else {
182 Err(FilterErrorNot::MatchString(s.to_string()))
183 }
184 }
185 Sanitize::MatchStrings(options) => {
186 if options.contains(&input.to_string()) {
187 Ok(())
188 } else {
189 Err(FilterErrorNot::MatchStrings(options.clone()))
190 }
191 }
192 Sanitize::IsBetween(n1, n2) => match DesiredType::Isize.parse(input) {
193 Ok(_) => {
194 let input_parsed: isize = input.parse().unwrap_or_default();
195 if input_parsed >= *n1 && input_parsed <= *n2 {
196 Ok(())
197 } else {
198 Err(FilterErrorNot::Between(*n1, *n2))
199 }
200 }
201 Err(e) => Err(e),
202 },
203 }
204 }
205}
206
207#[derive(Debug)]
218pub enum DesiredType {
219 String,
220 Bool,
221 U8,
222 U16,
223 U32,
224 U64,
225 U128,
226 I8,
227 I16,
228 I32,
229 I64,
230 I128,
231 Isize,
232}
233
234impl std::str::FromStr for DesiredType {
235 type Err = DesiredTypeFromStrErr;
236 fn from_str(s: &str) -> Result<Self, Self::Err> {
237 match s {
238 "String" => Ok(DesiredType::String),
239 "bool" => Ok(DesiredType::Bool),
240 "u8" => Ok(DesiredType::U8),
241 "u16" => Ok(DesiredType::U16),
242 "u32" => Ok(DesiredType::U32),
243 "u64" => Ok(DesiredType::U64),
244 "u128" => Ok(DesiredType::U128),
245 "i8" => Ok(DesiredType::I8),
246 "i16" => Ok(DesiredType::I16),
247 "i32" => Ok(DesiredType::I32),
248 "i64" => Ok(DesiredType::I64),
249 "i128" => Ok(DesiredType::I128),
250 "isize" => Ok(DesiredType::Isize),
251 s => Err(DesiredTypeFromStrErr::UnknownType(s.to_string())),
252 }
253 }
254}
255
256impl TryFrom<&str> for DesiredType {
278 type Error = DesiredTypeFromStrErr;
279 fn try_from(value: &str) -> Result<Self, Self::Error> {
280 value.parse::<DesiredType>()
281 }
282}
283
284#[derive(Debug)]
301pub enum DesiredTypeFromStrErr {
302 UnknownType(String),
304}
305
306impl Display for DesiredTypeFromStrErr {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 match self {
325 Self::UnknownType(s) => {
326 write!(f, "The value {} is not available as a type, try again!", s)
327 }
328 }
329 }
330}
331
332impl Error for DesiredTypeFromStrErr {}
340
341impl DesiredType {
342 fn parse(&self, input: &str) -> Result<(), FilterErrorNot> {
357 match self {
358 DesiredType::String => {
359 check_type!(
360 input,
361 String,
362 Err(FilterErrorNot::String(DesiredType::String))
363 )
364 }
365 DesiredType::Bool => {
366 check_type!(input, bool, Err(FilterErrorNot::Bool(DesiredType::Bool)))
367 }
368 DesiredType::U8 => check_type!(input, u8, Err(FilterErrorNot::Number(DesiredType::U8))),
369 DesiredType::U16 => {
370 check_type!(input, u16, Err(FilterErrorNot::Number(DesiredType::U16)))
371 }
372 DesiredType::U32 => {
373 check_type!(input, u32, Err(FilterErrorNot::Number(DesiredType::U32)))
374 }
375 DesiredType::U64 => {
376 check_type!(input, u64, Err(FilterErrorNot::Number(DesiredType::U64)))
377 }
378 DesiredType::U128 => {
379 check_type!(input, u128, Err(FilterErrorNot::Number(DesiredType::U128)))
380 }
381 DesiredType::I8 => check_type!(input, i8, Err(FilterErrorNot::Number(DesiredType::I8))),
382 DesiredType::I16 => {
383 check_type!(input, i16, Err(FilterErrorNot::Number(DesiredType::I16)))
384 }
385 DesiredType::I32 => {
386 check_type!(input, i32, Err(FilterErrorNot::Number(DesiredType::I32)))
387 }
388 DesiredType::I64 => {
389 check_type!(input, i64, Err(FilterErrorNot::Number(DesiredType::I64)))
390 }
391 DesiredType::I128 => {
392 check_type!(input, i128, Err(FilterErrorNot::Number(DesiredType::I128)))
393 }
394 DesiredType::Isize => check_type!(
395 input,
396 isize,
397 Err(FilterErrorNot::Number(DesiredType::Isize))
398 ),
399 }
400 }
401}
402
403impl Display for DesiredType {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 match self {
406 Self::String => write!(f, "string"),
407 Self::Bool => write!(f, "bool"),
408 Self::U8 => write!(f, "u8"),
409 Self::U16 => write!(f, "u16"),
410 Self::U32 => write!(f, "u32"),
411 Self::U64 => write!(f, "u64"),
412 Self::U128 => write!(f, "u128"),
413 Self::I8 => write!(f, "i8"),
414 Self::I16 => write!(f, "i16"),
415 Self::I32 => write!(f, "i32"),
416 Self::I64 => write!(f, "i64"),
417 Self::I128 => write!(f, "i128"),
418 Self::Isize => write!(f, "isize"),
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[test]
428 fn test_sanitize_match_string_sucess() {
429 let filter = Sanitize::MatchString("hello".to_string());
430 assert!(filter.validate("hello").is_ok());
431 }
432
433 #[test]
434 fn test_sanitize_match_string_fail() {
435 let filter = Sanitize::MatchString("hello".to_string());
436 let res = filter.validate("world");
437 assert!(res.is_err());
438 if let Err(e) = res {
439 assert_eq!(
440 format!("{}", e),
441 "The value doesn't match with hello, try again!"
442 );
443 }
444 }
445
446 #[test]
447 fn match_sanitize_match_strings_sucess() {
448 let filter = Sanitize::MatchStrings(vec!["A".to_string(), "B".to_string()]);
449 assert!(filter.validate("A").is_ok());
450 assert!(filter.validate("B").is_ok());
451 }
452
453 #[test]
454 fn test_sanitize_match_strings_fail() {
455 let filter = Sanitize::MatchStrings(vec!["A".to_string(), "B".to_string()]);
456 let res = filter.validate("C");
457 assert!(res.is_err());
458 if let Err(e) = res {
459 assert_eq!(
460 format!("{}", e),
461 "The value doesn't match with the options: A, B, try again!"
462 );
463 }
464 }
465
466 #[test]
467 fn test_sanitize_is_type_bool() {
468 let filter = Sanitize::IsType(DesiredType::Bool);
469 assert!(filter.validate("true").is_ok());
470 assert!(filter.validate("false").is_ok());
471 assert!(filter.validate("maybe").is_err());
472 }
473
474 #[test]
475 fn test_sanitize_is_type_u8() {
476 let filter = Sanitize::IsType(DesiredType::U8);
477 assert!(filter.validate("42").is_ok());
478 assert!(filter.validate("-42").is_err());
479 assert!(filter.validate("256").is_err()); assert!(filter.validate("abc").is_err());
481 }
482
483 #[test]
484 fn test_sanitize_is_type_i32() {
485 let filter = Sanitize::IsType(DesiredType::I32);
486 assert!(filter.validate("-123").is_ok());
487 assert!(filter.validate("2147483647").is_ok()); assert!(filter.validate("2147483648").is_err()); }
490
491 #[test]
492 fn test_sanitize_is_type_isize() {
493 let filter = Sanitize::IsBetween(10, 20);
494 assert!(filter.validate("15").is_ok());
495 assert!(filter.validate("25").is_err()); assert!(filter.validate("-20").is_err()); }
498
499 #[test]
500 fn test_sanitize_is_type_from_str() {
501 let filter = Sanitize::IsType("u8".parse::<DesiredType>().unwrap());
502 assert!(filter.validate("42").is_ok());
503 assert!(filter.validate("-42").is_err());
504 assert!(filter.validate("256").is_err()); assert!(filter.validate("abc").is_err());
506 }
507
508 #[test]
509 fn test_sanitize_is_type_try_from_str() {
510 let filter = Sanitize::IsType(DesiredType::try_from("u8").unwrap());
511 assert!(filter.validate("42").is_ok());
512 assert!(filter.validate("-42").is_err());
513 assert!(filter.validate("256").is_err()); assert!(filter.validate("abc").is_err());
515 }
516
517 #[test]
518 fn test_sanitize_execute_filters_success() {
519 let filters = vec![
520 Sanitize::IsType(DesiredType::String),
521 Sanitize::MatchString("Hello".to_string()),
522 ];
523 let res = Sanitize::execute("Hello", &filters);
524 assert!(res.is_ok());
525 assert_eq!(res.unwrap(), "Hello".to_string());
526 }
527
528 #[test]
529 fn test_sanitize_execute_filters_fail() {
530 let filters = vec![
531 Sanitize::MatchString("Hello".to_string()),
532 Sanitize::IsType(DesiredType::Bool),
533 ];
534 let res = Sanitize::execute("Hello", &filters);
535 assert!(res.is_err());
536 if let Err(e) = res {
537 assert_eq!(format!("{}", e), "The value is not a bool, try again!");
538 }
539 }
540
541 #[test]
542 fn test_sanitize_execute_filters_fail2() {
543 let filters = vec![
544 Sanitize::IsType(DesiredType::Bool),
545 Sanitize::IsType(DesiredType::U8),
546 ];
547 let res = Sanitize::execute("true", &filters);
548 assert!(res.is_err());
549 if let Err(e) = res {
550 assert_eq!(format!("{}", e), "The value is not a u8, try again!");
551 }
552 }
553
554 #[test]
555 fn test_sanitize_execute_filters_fail3() {
556 let filters = vec![
557 Sanitize::IsType(DesiredType::U8),
558 Sanitize::IsType(DesiredType::Bool),
559 ];
560 let res = Sanitize::execute("true", &filters);
561 assert!(res.is_err());
562 if let Err(e) = res {
563 assert_eq!(format!("{}", e), "The value is not a u8, try again!");
564 }
565 }
566
567 #[test]
568 fn test_sanitize_execute_filters_fail4() {
569 let filters = vec![
570 Sanitize::IsType(DesiredType::String),
571 Sanitize::MatchStrings(vec![String::from("banana"), String::from("orange")]),
572 ];
573 let res = Sanitize::execute("watermelon", &filters);
574 assert!(res.is_err());
575 if let Err(e) = res {
576 assert_eq!(
577 format!("{}", e),
578 "The value doesn't match with the options: banana, orange, try again!"
579 );
580 }
581 }
582}