Hello everyone! My crate prae just got a huge update! It is now much more flexible and lightweight.
For those who don't know (most of you), prae is a crate that aims to provide a better way to define types that require validation, like this:
use prae::Wrapper;
prae::define! {
#[derive(Debug)]
pub Username: String;
adjust |un| *username = un.trim().to_owned();
ensure |un| !un.is_empty();
}
let un = Username::new(" my username ").unwrap();
assert_eq!(un.get(), "my username");
let err = Username::new(" ").unwrap_err();
assert_eq!(err.original, "value is invalid");
assert_eq!(err.value, "");
The most important changes:
- The crate provides two declarative macros instead of one procedural. This means bye-bye to
syn
andquote
and hello to faster compilation times. The syntax of macros is also a bit nicer. - The API was greatly simplified:
- Previously it provided two important types: trait
Bound
and structBounded
. When user invoked macro, it generated a unit struct, implementedBound
for it and created a type alias to theBounded
struct (basically generated two types: the one you would use and a helper one). - Now it provides one important type: trait
Wrapper
. When user invokes macro, it generates a struct wrapper (like inNewtype
pattern) and implements theWrapper
trait for it (actually, it also implementsAsRef
,Borrow
,From
andTryFrom
). Since generated type is local (there's no type alias), you can basically do with it anything you can do with a tuple struct, for example use attribute macros and add customimpl
blocks.
- Previously it provided two important types: trait
Since the generated type is so customisable, you now can specify so-called "plugins" during creation of the type. These plugins are just macros that receive the name of the type and do anything they want with it. For example, prae
provides a impl_serde
plugin under the serde
feature. This plugin implements serde::Serialize
and serde::Deserialize
for your type, with a note that the implementation of Deserialize
will fail if you'll try to deserialize an invalid value.
Here is an example for how to create a type that can be used as async-graphql::Scalar
using plugins:
prae::define! {
pub Username: String;
plugins: [
prae::impl_serde,
async_graphql::scalar,
]
}
The crate no longer implements every possible trait for the generated type (for example, previously if you created a wrapper around a type that implemented Clone
, your wrapper would also implement Clone
). Instead, it encourages you to implement only the things you need (using attribute macros, plugins or manual impls
).
Please, give that library a try! I would love to hear feedback.