clapi/
validator.rs

1use std::fmt::Display;
2use std::marker::PhantomData;
3use std::str::FromStr;
4
5#[cfg(feature = "typing")]
6use crate::typing::Type;
7
8/// Exposes a method for check if an `str` value is a valid argument value.
9pub trait Validator {
10    /// Checks if the given string slice is valid.
11    /// Returns `Ok()` if is valid otherwise `Err(error)`.
12    fn validate(&self, value: &str) -> Result<(), String>;
13
14    /// Returns the `Type` that is valid for this `Validator`, by default returns `None`.
15    ///
16    /// When `None` is returned differents types may be valid for the validator,
17    /// for example `"1"` can be valid for types like `i32`, `u64`, `f32`, ...
18    /// to ensure the validator is only valid for `u64` the implementor must return: `Some(Type::of::<u64>())`.
19    ///
20    /// The returned `Type` is used by `Argument::convert` to ensure if safe to convert a type `T`.
21    #[cfg(feature = "typing")]
22    fn valid_type(&self) -> Option<Type> {
23        None
24    }
25}
26
27/// A `Validator` where a `str` is considered valid if can be parsed to a type `T`.
28#[derive(Default)]
29pub struct TypeValidator<T>(PhantomData<T>);
30impl<T> TypeValidator<T> {
31    #[inline]
32    pub fn new() -> Self {
33        TypeValidator(PhantomData)
34    }
35}
36impl<T: 'static> Validator for TypeValidator<T>
37    where
38        T: FromStr,
39{
40    fn validate(&self, value: &str) -> Result<(), String> {
41        match T::from_str(value) {
42            Ok(_) => Ok(()),
43            Err(_) => Err(format!("`{}`", value)),
44        }
45    }
46
47    #[cfg(feature = "typing")]
48    fn valid_type(&self) -> Option<Type> {
49        Some(Type::of::<T>())
50    }
51}
52
53/// A `Validator` where a `str` is valid if can be parsed to type `T`
54/// and is within the specified range.
55pub struct RangeValidator<T>(T, T);
56impl<T> RangeValidator<T>
57    where
58        T: FromStr + PartialOrd + Display,
59{
60    #[inline]
61    pub fn new(min: T, max: T) -> Self {
62        assert!(min < max, "min cannot be greater than max");
63        RangeValidator(min, max)
64    }
65}
66impl<T: 'static> Validator for RangeValidator<T>
67    where
68        T: FromStr + PartialOrd + Display,
69{
70    fn validate(&self, value: &str) -> Result<(), String> {
71        match T::from_str(value) {
72            Err(_) => Err(format!("`{}`", value)),
73            Ok(n) => {
74                if n >= self.0 && n <= self.1 {
75                    Ok(())
76                } else {
77                    Err(format!("{} is out of range: {}..{}", n, self.0, self.1))
78                }
79            }
80        }
81    }
82
83    #[cfg(feature = "typing")]
84    fn valid_type(&self) -> Option<Type> {
85        Some(Type::of::<T>())
86    }
87}
88
89// This allow to use a closure as a `Validator`
90impl<F> Validator for F
91    where
92        F: Fn(&str) -> std::result::Result<(), String>,
93{
94    fn validate(&self, value: &str) -> Result<(), String> {
95        match (self)(value) {
96            Ok(_) => Ok(()),
97            Err(msg) => Err(msg),
98        }
99    }
100}
101
102/// Constructs a `Validator` for the specified type.
103#[inline]
104pub fn validate_type<T: 'static + FromStr>() -> TypeValidator<T> {
105    TypeValidator::new()
106}
107
108/// Constructs a `Validator` for the given range.
109#[inline]
110pub fn validate_range<T: 'static>(min: T, max: T) -> RangeValidator<T>
111    where
112        T: FromStr + PartialOrd + Display,
113{
114    RangeValidator::new(min, max)
115}