Crate prae

source · []
Expand description

prae is a crate that aims to provide a better way to define types that require validation.

The main concept of the library is the Wrapper trait. This trait describes a Newtype wrapper struct that contains some inner value and provides methods to construct, read and mutate it.

The easiest way to create a type that implements Wrapper is to use define! and extend! macros.

Example

Suppose you want to create a type Username. You want this type to be a String, and you don’t want it to be empty. Traditionally, you would create a wrapper struct with getter and setter functions, like this simplified example:

#[derive(Debug)]
pub struct Username(String);

impl Username {
    pub fn new(username: &str) -> Result<Self, &'static str> {
        let username = username.trim().to_owned();
        if username.is_empty() {
            Err("value is invalid")
        } else {
            Ok(Self(username))
        }
    }

    pub fn get(&self) -> &str {
        &self.0
    }

    pub fn set(&mut self, username: &str) -> Result<(), &'static str> {
        let username = username.trim().to_owned();
        if username.is_empty() {
            Err("value is invalid")
        } else {
            self.0 = username;
            Ok(())
        }
   }
}

let username = Username::new(" my username ").unwrap();
assert_eq!(username.get(), "my username");

let err = Username::new("  ").unwrap_err();
assert_eq!(err, "value is invalid");

Using prae, you will do it like this:

use prae::Wrapper;

prae::define! {
    #[derive(Debug)]
    pub Username: String;
    adjust |username| *username = username.trim().to_owned();
    ensure |username| !username.is_empty();
}

let username = Username::new(" my username ").unwrap();
assert_eq!(username.get(), "my username");

let err = Username::new("  ").unwrap_err();
assert_eq!(err.original, "value is invalid");
assert_eq!(err.value, "");

Futhermore, prae allows you to use custom errors and extend your types. See docs for define! and extend! for more information and examples.

Compilation speed

The macros provided by this crate are declarative, therefore make almost zero impact on the compilation speed.

Performarnce impact

If you find yourself in a situation where the internal adjustment and validation of your type becomes a performance bottleneck (for example, you perform a heavy validation and mutate your type in a hot loop) - try _unprocessed variants of Wrapper methods. They won’t call Wrapper::PROCESS. However, I strongly advise you to call Wrapper::verify after such operations.

Feature flags

prae provides additional features:

NameDescription
serdeAdds the impl_serde plugin.

Credits

This crate was highly inspired by the tightness crate. It’s basically just a fork of tightness with a slightly different philosophy. See this issue for details.

Macros

Convenience macro that creates a Newtype wrapper struct that implements Wrapper.

Convenience macro that creates a Newtype wrapper struct that implements Wrapper and extends another Wrapper.

Implement Deref for the wrapper.

Implement Display for the wrapper.

Implement Index for the wrapper.

Implement serde::Serialize and serde::Deserialize for the wrapper. Deserilization will fail if the value doesn’t pass wrapper’s PROCESS function.

Structs

A wrapper-error that will be returned if the Wrapper::new or Wrapper::set methods receive a value that doesn’t pass Wrapper::PROCESS function.

A wrapper-error that will be returned if the Wrapper::mutate method receives a closure that mutates the inner value in such a way that it no longer passes the Wrapper::PROCESS function.

A wrapper-error that will be returned if the Wrapper::verify method is called on a wrapper whose inner value no longer passes the Wrapper::PROCESS function.

Traits

Convenience trait that allows mapping from Result<_, ConstructionError<Wrapper>>, Result<_, MutationError<Wrapper> and Result<_, VerificationError<Wrapper>> to Result<_, Wrapper::Error>.

A trait that describes a Newtype wrapper struct generated by define! and extend! macros.