Skip to content

Releases

v0.23.0

Choose a tag to compare

@jprochazk jprochazk released this 23 May 14:49
62b71b1

Internationalization (i18n)

Error messages may now be customized.

struct Czech;

impl I18n for Czech {
    fn length_lower_than(&self, min: usize) -> Cow<'static, str> {
        format!("musí obsahovat alespoň {min} znaků").into()
    }
    
    fn email_invalid(&self, _reason: InvalidEmail) -> Cow<'static, str> {
        format!("email je neplatný").into()
    }
    
    // etc.
}

#[derive(Validate)]
struct User {
    #[garde(length(min = 3))]
    name: String,
    #[garde(email)]
    email: String,
}

let user = User {
    name: "Jan Novák".to_string(),
    email: "invalid-email".to_string(),
};

let result = with_i18n(Czech, || user.validate());

The goal behind this feature is to support localization of error messages, but it can also be used to assign custom error messages in English to all errors produced by garde.

if(cond = <expr>, <rules>...)

Rules may now be nested within an if rule, which only validates if a given condition is true. The condition can refer to self and ctx as with any other rule.

struct Config {
    strict: bool,
}

#[derive(garde::Validate)]
#[garde(context(Config as ctx))]
struct User {
    #[garde(if(cond = ctx.strict, length(min = 8)))]
    username: String,
}

Struct-level custom rules

Custom rules may now be applied to an entire struct. Struct-level and field-level rules may be combined arbitrarily. This is a lightweight alternative to implementing garde::Validate manually.

#[derive(garde::Validate)]
#[garde(custom(foo_is_not_bad))]
struct Foo {
    #[garde(length(min = 8))]
    bar: String,
}

fn foo_is_not_bad(v: &Foo, _: &()) -> garde::Result {
    todo!();
}

Other changes

  • The length rule now works on url::Url (#147).
  • Box<[T]> is now supported as a container, allowing length and inner to be used (#176).
  • Clearer compiler errors when required feature flags are missing (#171).

Bug fixes

  • Deriving Validate on tuple structs with #[garde(skip)] no longer shifts field bindings (#182).
  • Debug for Path now prints the component kind, so path.join("0") and path.join(0) no longer print identically.
  • The main example in the README is now a doctest, and should now actually compile.

Breaking changes

  • Validation traits for credit_card, email, and phone_number no longer have an associated Error type (#150)
  • garde::external was trimmed to only re-export garde::ToCompactString.
  • Some dependencies were updated
  • MSRV is now 1.87.

New Contributors

Full Changelog: v0.22.1...v0.23.0

v0.22.1

Choose a tag to compare

@jprochazk jprochazk released this 30 Nov 13:32
db02771

What's Changed

New Contributors

Full Changelog: v0.22.0...v0.22.1

v0.22.0

Choose a tag to compare

@jprochazk jprochazk released this 19 Jan 17:10
bb2af87

What's Changed

Full Changelog: v0.21.1...v0.22.0

v0.21.1

Choose a tag to compare

@jprochazk jprochazk released this 10 Jan 14:02
7f351cb

What's Changed

New Contributors

Full Changelog: v0.21.0...v0.21.1

v0.21.0

Choose a tag to compare

@jprochazk jprochazk released this 21 Dec 14:02
0c8823f

What's Changed

Breaking changes

The MSRV is now 1.81.

⚠️ The axum_garde crate has been deprecated. ⚠️

The last release for axum_garde is 0.21. I recommend switching to axum-valid as soon as possible, as axum_garde is no longer maintained and will not receive updates of any kind.

The reason is that I personally have not been using the crate, and it has never felt like quite the right solution for integrating it into axum. I can't invest time into maintaining it anymore. axum-valid supports garde, among other validation crates, and moving to it should not be too much effort. A find-and-replace of axum_garde::WithValidation for axum_valid::Garde should get you most of the way there.

The source code for it has been removed from this repository as of 0c8823f.

New Contributors

Full Changelog: v0.20.0...v0.21.0

v0.20.0

Choose a tag to compare

@jprochazk jprochazk released this 05 Jul 09:47

What's Changed

Full Changelog: v0.19.2...v0.20.0

v0.19.2

Choose a tag to compare

@jprochazk jprochazk released this 18 Jun 12:16

What's changed

  • Fixed a bug introduced in v0.19.1 that would cause garde to not compile when compiled with the --no-default-features -F email.

Full Changelog: v0.19.1...v0.19.2

v0.19.1

Choose a tag to compare

@jprochazk jprochazk released this 16 Jun 16:57

What changed

Full Changelog: v0.18.0...v0.19.1

v0.19.0

Choose a tag to compare

@jprochazk jprochazk released this 09 Jun 17:30

What's Changed

Validate without context

Previously, validate always required a context parameter, even if it was not actually used. In #102, @lasantosr split the validate function into validate and validate_with, where the former does not require a Context parameter if Context implements Default.

#[derive(garde::Validate)]
struct Foo<'a> {
    #[garde(length(min = 1))]
    bar: &'a str,
}

Foo { bar: "test" }.validate() // no more `&()`!

Add field matching rule

@darkenmay added a new matches rule in #110, which adds support for the basic case of matching one field against another in the same struct:

#[derive(garde::Validate)]
struct User<'a> {
    #[garde(length(min = 1))]
    password: &'a str,
    #[garde(matches(password))] // checks if this field equals `password`.
    password2: &'a str,
}

User { password: "ferris123", password2: "ferris123" }.validate() 

Note that this currently does not work for enums.

Other improvements

New Contributors

Full Changelog: v0.18.0...v0.19.0

v0.18.0

Choose a tag to compare

@jprochazk jprochazk released this 28 Jan 12:17

There are quite a few changes in this release:

  • Length modes in #88
  • Adapters in #93
  • Remove default features, replace with full in #95
  • Serialize/deserialize Report in #90
  • Remove empty colon from display impl of Report in #91

Some of these are breaking changes. If you are only using the derive functionality and calling validate, updating should still be as simple as a version bump:

cargo add [email protected] -F full

If it isn't, please open an issue!

Length modes

You can now specify what kind of length you wish to validate by adding a new mode argument to your length rules. The argument is optional.

#[derive(garde::Validate)]
#[garde(transparent)]
struct Username(
    #[garde(
        length(bytes, min = 1, max = 100),
        length(graphemes, min = 1, max = 25)
    )]
    String
);

The above validates both bytes (via v.len()) and graphemes (via v.graphemes().count() using the unicode-segmentation crate. To understand why the distinction is important, consider the x̧̡̬̘͓̖̲̻̻̲̠̪̻͓͙̜̂̓̊̔̀̀͗̑̀̅̀̂̚͘̕̚͘͢͜͠ character, which occupies 73 bytes in memory, but is considered a single grapheme.

The available modes are:

  • simple, which is the default, and its behavior depends on the type. For strings, it measures the number of bytes, and for collections (such as Vec), it meaures the number of items.
  • bytes, which uses s.len(), measuring the number of bytes,
  • graphemes, which uses s.graphemes(true).count(), measuring the number of graphemes
  • chars, which uses s.chars().count(), measuring the number of unicode scalar values
  • utf16, which uses s.encode_utf16().count(), measuring the number of UTF-16 code units

The original Length and HasLength traits have been removed, and replaced by one trait per length mode.

Adapters

Using adapters, it is possible to implement validation rules for 3rd-party types directly in your own crates.

mod my_str_adapter {
    #![allow(unused_imports)]
    pub use garde::rules::*; // re-export garde's rules

    pub mod length {
        pub use garde::rules::length::*; // re-export `length` rules

        pub mod simple {
            // re-implement `simple`, but _only_ for the concrete type &str!
            pub fn apply(v: &str, (min, max): (usize, usize)) -> garde::Result {
                if !(min..=max).contains(&v.len()) {
                    Err(garde::Error::new("my custom error message"))
                } else {
                    Ok(())
                }
            }
        }
    }
}

Adapters are applied using the adapt field-level attribute:

#[derive(garde::Validate)]
struct Stuff<'a> {
    #[garde(
        adapt(my_str_adapter),
        length(min = 1),
        ascii,
    )]
    v: &'a str,
}

Now my_str_adapter::length::simple will be used instead of garde::rules::length::simple when validating the above type, removing the need for a newtype over str!

full feature

Having default features for a crate such as garde means that downstream users are more likely to end up with those default features in their lockfile with no way to turn them off, resulting in increased compile times in exchange for no benefit. Due to this, the default feature from garde has been removed, and we now have a new full feature which contains everything from the removed default feature.

To retain all the features previously in default, enable the full feature when you update:

cargo add [email protected] -F full

I urge you to consider if you really need all of full, though! There may be parts you're not using, and taking a few minutes to see what is the minimum feature set with which your project compiles is definitely worth it.

Miscellaneous

  • The Report type now has implementations of serde::Deserialize and serde::Serialize when the serde feature is enabled. Note that the implementation of Serialize has been updated to make it possible to implement a lossless serialize -> deserialize roundtrip, which is a breaking change if you were doing something with the serialized reports.

  • Validation errors on newtypes previously looked like this:

    : length is lower than 1
    

    This is because newtypes report their errors with an empty path, resulting in a lone : in the error message. This has now been fixed, and will report as:

    length is lower than 1
    

Full changelog

v0.17.0...v0.18.0