Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
What is prae
?
This crate aims to provide a better way to define types that require
validation. prae
is not a validation library, but a library that
helps developers to define validation-requiring types with very little
effort.
How it works?
The main way to use prae
is through define!
macro.
For example, suppose you want to create a Username
type. You want this
type to be a string, and you don't want it to be empty. Traditionally, 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::define;
define! {
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.inner, "value is invalid");
assert_eq!(err.value, "");
Futhermore, prae
allows you to use custom errors and extend your types.
See docs for define!
for more information and examples.
Additional features
*_unprocessed
functions
By default, all methods of the wrappers generated by
define!
(which are just aliases of the
Bounded
type) will run the adjustment/validation (if
present) routines on every construction and mutation.
If you find yourself in a situation where you know for sure that some
construction/mutation is valid, you can opt out of this using
*_unprocessed
functions (e.g. foo.set_unprocessed(value)
instead of
foo.set(value)
) and save a bit of computations.
To be able to use these functions, just enable the unprocessed
feature of the crate.
Serde integration
You can enable serde integration with the serde
feature. It will implement
Serialize
and Deserialize
traits for the wrappers if their inner type implements them. The
deserialization will automatically fail if it contains invalid value. Here
is an example:
use serde::{Deserialize, Serialize};
use prae::define;
define! {
Username: String
adjust |username| *username = username.trim().to_string()
validate(&'static str) |username| {
if username.is_empty() {
Err("username is empty")
} else {
Ok(())
}
}
}
#[derive(Debug, Deserialize, Serialize)]
struct User {
username: Username,
}
// Serialization works as expected.
let u = User {
username: Username::new(" john doe ").unwrap(),
};
let j = serde_json::to_string(&u).unwrap();
assert_eq!(j, r#"{"username":"john doe"}"#);
// Deserialization with invalid data fails.
let e = serde_json::from_str::<User>(r#"{ "username": " " }"#).unwrap_err();
assert_eq!(e.to_string(), "username is empty at line 1 column 20");
// And here we get a nice adjusted value.
let u = serde_json::from_str::<User>(r#"{ "username": " john doe " }"#).unwrap();
assert_eq!(u.username.get(), "john doe");
Drawbacks
Although proc macros are very powerful, they aren't free. In this case, you
have to pull up additional dependencies such as syn
and
quote
, and expect a slightly slower compile times.