[−][src]Module justconfig::validators
Validators, in contrast to processors do not modify the value on any way.
They are called after processors and operate on the already typed configuration value. Therefore they can do things, that processors cant. They can do validation on attributes of the typed value (less than for example) that are not easily possible on strings.
Validators are used to validate the contents of the values of a configuration
item. For an introduction to configuration items see the documentation for
the item
module.
Validators are implemented as the combination of a trait and an implementation.
The trait defines the methods that the processor provides and is always
implemented for Result<TypedItem<T>, ConfigError>
and
Result<StringItem, ConfigError>
. Each processor method takes
an owned self
and returns Result<TypedItem<T>, ConfigError>
. That way
processors can be easily chained.
The implementation for Result<StringItem, ConfigError>
is only a wrapper
that does the type conversion from String
to T
on the first validator
call. All validator further down the pipeline operate directly on the converted
value.
To use a validator just put directly after the
get
method of the
Config
struct or after the last processor.
defaults.set(conf.root().push_all(&["myvalue"]), "4", "source info"); conf.add_source(defaults); let at_most_five: i16 = conf.get(ConfPath::from(&["myvalue"])).max(5).value().unwrap(); // This will fail because 4 is more that 3. let at_most_three: Result<i16, ConfigError> = conf.get(ConfPath::from(&["myvalue"])).max(3).value(); assert!(at_most_three.is_err());
Implementing a validator
To implement a new validator first have a look at the source of the existing validators.
For validators there is a helper method within the
TypedItem
struct. This method is called
filter
.
The validator first checks, if there is an error value within the Result
.
If there is one, the error is returned without any further validation.
Then filter
is called and the result of the filtering operation is returned to
the next step of the pipeline. A basic validator looks like this:
use std::str::FromStr; use std::convert::TryInto; use std::error::Error; use justconfig::error::ConfigError; use justconfig::item::{StringItem, TypedItem, MapAction}; pub trait IsFrobable<T: FromStr> { fn isFrobable(self, max_frobability: u8) -> Result<TypedItem<T>, ConfigError>; } impl <T: FromStr> IsFrobable<T> for Result<TypedItem<T>, ConfigError> { fn isFrobable(self, max_frobability: u8) -> Result<TypedItem<T>, ConfigError> { self?.filter(|v| { // Your code goes here. }) } } // This is the necessary wrapper to ensure the conversion of StringItem to // to TypedItem<T>. impl <T: FromStr> IsFrobable<T> for Result<StringItem, ConfigError> where T::Err: Error + 'static { fn isFrobable(self, max_frobability: u8) -> Result<TypedItem<T>, ConfigError> { (self.try_into() as Result<TypedItem<T>, ConfigError>).isFrobable(max_frobability) } }
This example shows the necessary wrapper that allows a validator to be called on the string- and typed-value.
The type argument T
your validator must be bound to implement the trait FromStr
.
This is necessary to allow the conversion from StringItem
to TypedItem<T>
. If
your validator needs T
to be bound to multiple traits just add them after the
'FromStr' trait using the +
syntax.
Enums
ValidatorError |
Traits
Range | Validates if a configuration value is within range by using the
|