valida 1.1.2

Modular validation system for Rust with support for nested structures and localization.
Documentation
# ๐Ÿงช DSL vs Macros

Valida offers two distinct ways to define validation logic:

- ๐Ÿง  **Macro-Based (`#[Validatable]`)** โ€” declarative, concise, built for speed
- ๐Ÿ”ง **DSL-Based (`RulesBuilder`)** โ€” explicit, flexible, perfect for complex flows

Both approaches generate the same internal validation logic and support all Valida features: nested rules, custom validators, localization, and error output formatting.

---

## โš™๏ธ DSL Style

The DSL builder uses explicit Rust syntax and is ideal for complex scenarios or when control over composition is needed:

```rust
pub struct UserValidator;

#[async_trait::async_trait]
impl IValidate<User, std::io::Error> for UserValidator {
    fn rules(&self, mut builder: RulesBuilder<User, std::io::Error>) -> RulesBuilder<User, std::io::Error> {
        builder
            .field("email", |u| &u.email)
            .trimmed().lowercased().email().build();

        builder
            .field("age", |u| &u.age)
            .min(0).custom_async(MinAge(18)).build();

        builder
            .field("device", |u| &u.device)
            .nested(DeviceValidator).build();

        builder
    }
}
```

# โœ… Benefits of DSL
* Full visibility and control
* Works well with generics, dynamic dispatch
* Ideal for validators outside your data types (e.g. external modules)

---

# โšก Macro Style

With `#[Validatable(error_type)]`, you can annotate your DTOs directly. This approach is cleaner and more declarative for typical use cases:

```rust
#[Validatable(std::io::Error)]
pub struct User {
    #[validate(trimmed, email, min_length(5))]
    pub email: String,

    #[validate(min(0), custom_async(MinAge(18)))]
    pub age: i32,

    #[validate(nested(DeviceValidator))]
    pub device: Device,
}
```

The macro automatically generates a validator named UserValidator.

# โœ… Benefits of Macros

* Less boilerplate
* Inline rules improve readability
* Faster prototyping
* Encourages co-location with data definitions

---


# ๐Ÿšจ When to Use DSL Over Macros

* When validator logic must live in separate modules
* When validator state depends on runtime configuration
* When rules require conditional logic
* When macro limitations interfere (e.g. repeated `#[Validatable]` in same scope)

---

# ๐Ÿ”„ Combining Both

You can freely mix both styles in the same codebase. For example:

```rust
#[Validatable(std::io::Error)]
pub struct Profile { ... }

pub struct ContainerValidator;
impl IValidate<Container, std::io::Error> for ContainerValidator {
    fn rules(...) { ... }
}
```