[][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 PartialOrd trait of the configuration target value.