MCPcopy Index your code
hub / github.com/biblius/validify

github.com/biblius/validify @1.4

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.4 ↗ · + Follow
534 symbols 1,016 edges 59 files 56 documented · 10%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Validify

Build test coverage docs version

Procedural macros that provide attributes for data validation and modification. Particularly useful in the context of web payloads.

Modifiers

Modifier Type Description
trim* String Removes surrounding whitespace
uppercase* String Calls .to_uppercase()
lowercase* String Calls .to_lowercase()
capitalize* String Makes the first char of the string uppercase
custom Any Takes a function whose argument is &mut <Type>
validify impl Validify / impl Iterator\ Can only be used on fields that are structs (or collections of) implementing the Validify trait. Runs all the nested struct's modifiers and validations.

*Also works for Vec\ by running the modifier on each element.

Validators

All validators also take in a code and message as parameters, their values are must be string literals if specified.

Validator Type Params Param type Description
email String -- -- Checks emails based on this spec.
ip String format Ident (v4/v6) Checks if the string is an IP address.
url String -- -- Checks if the string is a URL.
length Collection min, max, equal LitInt Checks if the collection length is within the specified params. Works through the HasLen trait.
range Int/Float min, max LitFloat Checks if the value is in the specified range.
must_match Any value Ident Checks if the field matches another field of the struct. The value must be equal to a field identifier on the deriving struct.
contains Collection value Lit/Path Checks if the collection contains the specified value. If used on a K,V collection, it checks whether it has the provided key.
contains_not Collection value Lit/Path Checks if the collection doesn't contain the specified value. If used on a K,V collection, it checks whether it has the provided key.
non_control_char String -- -- Checks if the field contains control characters
custom Function function Path Executes custom validation on the field by calling the provided function
regex String path Path Matches the provided regex against the field. Intended to be used with lazy_static by providing a path to an initialised regex.
credit_card String -- -- Checks if the field's value is a valid credit card number
phone String -- -- Checks if the field's value is a valid phone number
required Option\ -- -- Checks whether the field's value is Some
is_in impl PartialEq collection Path Checks whether the field's value is in the specified collection
not_in impl PartialEq collection Path Checks whether the field's value is not in the specified collection
validate impl Validate -- -- Calls the underlying struct's validate implementation
iter impl Iterator List of validators Validator Runs the provided validators on each element of the iterable
time NaiveDate[Time] See below See below Performs a check based on the specified op

Time operators

All time operators may take in inclusive = bool. All time operator must take in time = bool when validating datetimes, by default time validators will attempt to validate dates.

in_period and the *_from_now operators are inclusive by default.

The target param must be a string literal date or a path to an argless function that returns a date[time].

If the target is a string literal, it must contain a format param, as per this.

Accepted interval parameters are seconds, minutes, hours, days, weeks.

The _from_now operators should not use negative duration due to how they validate the inputs, negative duration for in_period works fine.

Op Params Description
before target Check whether a date[time] is before the target one
after target Check whether a date[time] is after the target one
before_now -- Check whether a date[time] is before today[now]
after_now -- Check whether a date[time] is after today[now]
before_from_now interval Check whether a date[time] is before the specified interval from today[now]
after_from_now interval Check whether a date[time] is after the specified interval from the today[now]
in_period target, interval Check whether a date[time] falls within a certain period

Annotate the struct you want to modify and validate with the Validify attribute (if you do not need the payload or modification, derive validify::Validate):

use validify::Validify;

#[derive(Debug, Clone, serde::Deserialize, Validify)]
struct Testor {
    #[modify(lowercase, trim)]
    #[validate(length(equal = 8))]
    pub a: String,
    #[modify(trim, uppercase)]
    pub b: Option<String>,
    #[modify(custom(do_something))]
    pub c: String,
    #[modify(custom(do_something))]
    pub d: Option<String>,
    #[validify]
    pub nested: Nestor,
}

#[derive(Debug, Clone, serde::Deserialize, Validify)]
struct Nestor {
    #[modify(trim, uppercase)]
    #[validate(length(equal = 12))]
    a: String,
    #[modify(capitalize)]
    #[validate(length(equal = 14))]
    b: String,
}

fn do_something(input: &mut String) {
    *input = String::from("modified");
}

let mut test = Testor {
  a: "   LOWER ME     ".to_string(),
  b: Some("  makemeshout   ".to_string()),
  c: "I'll never be the same".to_string(),
  d: Some("Me neither".to_string()),
  nested: Nestor {
    a: "   notsotinynow   ".to_string(),
      b: "capitalize me.".to_string(),
  },
};

// The magic line
let res = test.validify();

assert!(matches!(res, Ok(_)));

// Parent
assert_eq!(test.a, "lower me");
assert_eq!(test.b, Some("MAKEMESHOUT".to_string()));
assert_eq!(test.c, "modified");
assert_eq!(test.d, Some("modified".to_string()));
// Nested
assert_eq!(test.nested.a, "NOTSOTINYNOW");
assert_eq!(test.nested.b, "Capitalize me.");

Notice how even though field d is an option, the function used to modify the field still takes in &mut String. This is because modifiers and validations are only executed when the field isn't None.

Traits

Validify is built around 3 simple traits:

  • Validate
  • Modify
  • Validify

These traits should theoretically never have to be implemented manually.

As their names suggest, the first two traits perform validation and modification, while the third combines those 2 actions into a single one - validify.

The traits contain a single function which is constructed based on struct annotations when deriving them.

Payload

Structs annotated with #[derive(Payload)] get an associated payload struct, e.g.

#[derive(validify::Validify, validify::Payload)]
struct Something {
  a: usize,
  b: String,
  c: Option<bool>
}

behind the scenes will generate an intermediary

#[derive(Debug, Clone, serde::Deserialize, validify::Validate)]
struct SomethingPayload {
  #[validate(required)]
  a: Option<usize>,
  #[validate(required)]
  b: Option<String>,
  c: Option<bool>,

  /* From and Into impls */
}

The motivation for this is to aid in deserializing potentially missing fields. Even though the payload struct cannot help with deserializing wrong types, it can still prove useful and provide a bit more meaningful error messages when fields are missing.

The original struct gets a ValidifyPayload implementation with 2 associated fns: validate_from and validify_from whose whose respective arguments are the generated payload.

The ValidifyPayload implementations first validate the required fields of the payload. Then, if any required fields are missing, no further modification/validation is done and the errors are returned. Next, the payload is transformed to the original struct and modifications and/or validations are run on it.

When a struct contains nested validifies (child structs annotated with #[validify]), all the children in the payload will also be transformed and validated as payloads first. This means that any nested structs must also derive Payload.

The payload and serde

Struct level attributes, such as rename_all are propagated to the payload. When attributes that modify field names are present, any field names in returned errors will be represented as the original (i.e. client payload).

There are a few special serde attributes that validify treats differently; rename, with and deserialize_with. It is highly advised these attributes are kept in a separate annotation from any other serde attributes, due to the way they are parsed for the payload.

The rename attribute is used by validify to set the field name in any errors during validation. The with and deserialize_with will be transfered to the payload field and will create a special deserialization function that will call the original and wrap the result in an option. If the custom deserializer already returns an option, it will do nothing.

Schema validation

Schema level validation can be performed using the following:

```rust use validify::{Validify, ValidationErrors, schema_validation, schema_err};

[derive(validify::Va

Extension points exported contracts — how you extend this code

HasLen (Interface)
Trait to implement if one wants to make the `length` validator work for more types [21 implementers]
validify/src/traits.rs
Describe (Interface)
Trait implemented by validators to output validation codes and messages. [3 implementers]
validify_derive/src/validate/validation.rs
Contains (Interface)
Trait to implement if one wants to make the `contains` validator work for more types [10 implementers]
validify/src/traits.rs
ValidationErrorTokens (Interface)
Utility for generating error messages
validify_derive/src/tokens.rs
Validate (Interface)
Deriving [Validate] allows you to specify schema and field validations on structs. See the [repository](https://github.c
validify/src/lib.rs
ValidationMeta (Interface)
(no doc) [1 implementers]
validify_derive/src/validate.rs
Modify (Interface)
Modifies the struct based on the provided `modify` parameters. Automatically implemented when deriving Validify. See the
validify/src/lib.rs
Validify (Interface)
Deriving [Validify] allows you to modify structs before they are validated by providing a out of the box validation impl
validify/src/lib.rs

Core symbols most depended-on inside this repo

field_errors
called by 53
validify/src/error.rs
errors
called by 34
validify/src/error.rs
is_option
called by 16
validify_derive/src/fields.rs
collect
called by 15
validify_derive/src/fields.rs
quote_error
called by 14
validify_derive/src/tokens.rs
wrap_tokens_if_option
called by 14
validify_derive/src/fields.rs
is_empty
called by 13
validify/src/error.rs
add
called by 12
validify/src/error.rs

Shape

Function 354
Class 92
Method 69
Enum 10
Interface 9

Languages

Rust100%

Modules by API surface

derive_tests/tests/nested.rs31 symbols
validify/src/error.rs29 symbols
validify_derive/src/fields.rs27 symbols
validify/src/validation/time.rs23 symbols
derive_tests/tests/validify.rs23 symbols
derive_tests/tests/modify.rs23 symbols
derive_tests/tests/iter.rs20 symbols
derive_tests/tests/time.rs19 symbols
validify_derive/src/validate/validation.rs15 symbols
derive_tests/tests/schema.rs15 symbols
derive_tests/tests/complex.rs15 symbols
derive_tests/tests/error_location.rs14 symbols

For agents

$ claude mcp add validify \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact