catfive 0.2.0

Category-5 fluent, zero-dependency schema and form validation for Rust.
Documentation

Catfive 🌪️

Catfive is a category-5, fluent, zero-dependency schema and form validation library for Rust.

Inspired by developer-friendly schema libraries like Zod, Catfive gives you composable, expressive validation rules without forcing procedural attribute macros (#[derive(...)]) onto your structs.

Crates.io License: MIT


Highlights

  • ⚡ Macro-Free: Clean, functional, and composable builder API without #[derive(Validate)].
  • 📦 Zero Dependencies by Default: Core validators operate entirely on the Rust standard library (std).
  • 🔮 Universal Pattern Engine (spek): Validate documents and standards via .spek("[...]"):
    • [CPF]: Brazilian CPF (modulo 11 check digits, repeated digits rejection).
    • [CNPJ]: Brazilian CNPJ (native support for both classic numeric and the new 2026 alphanumeric standard IN RFB 2229/2024).
    • [LUHN]: ISO/IEC 7812 check digit algorithm (credit cards, IMEI).
    • [HEX], [BASE64], [BASE58], [ASCII 65..90], and custom literal charsets like [ABC234].
  • 🔄 Cloneable Validators: Every validator implements Clone for easy template reuse across your codebase.
  • 💬 Custom & Template Error Messages: Format errors like println! with dynamic placeholders ({char}, {index}, {min}, {count}, {value}) or closures.
  • 📋 Form & Struct Schemas: Validate untyped key-value form submissions or strongly-typed Rust structs with nested schemas and cross-field refinements.
  • 📚 Collection Validation: Validate lists, vectors, and slices using catfive::list with uniqueness and length rules.
  • 🎯 Optional Features:
    • serde: Serialize ValidationErrors directly to JSON for web APIs (Axum, Actix-web, Rocket).
    • regex: Enable regular expression pattern matching.

Installation

Add Catfive to your Cargo.toml:

[dependencies]
catfive = "0.2.0"

With optional features:

[dependencies]
catfive = { version = "0.2.0", features = ["serde", "regex"] }

Quick Start

1. Custom & Template Error Messages (println!-style)

You can let Catfive use its precise default error messages (which pinpoint the exact offending character), or provide your own message using template placeholders or closures:

use catfive::string;

// Using template placeholders:
let username_val = string()
    .min_msg(5, "Username must have at least {min} characters (got {count})")
    .block_msg(";@#$&", "Found forbidden character '{char}' at position {position}");

let err = username_val.validate("usr@").unwrap_err();
println!("{}", err.message()); // "Found forbidden character '@' at position 4"

// Using a custom closure:
let strict_val = string().block_with(";@#", |ch, idx| {
    format!("Prohibited character '{}' at 0-indexed position {}", ch, idx)
});

2. Universal Pattern Engine: .spek("[...]")

Validate Brazilian documents, standard encodings, mathematical algorithms, and custom character sets with a single declarative method:

use catfive::string;

// 1. Brazilian CNPJ (natively supports classic numeric AND new 2026 alphanumeric IN RFB 2229/2024):
let cnpj_val = string().spek("[CNPJ]").block(";@#");
assert!(cnpj_val.validate("12.345.678/0001-95").is_ok()); // Classic numeric
assert!(cnpj_val.validate("12.ABC.345/01DE-35").is_ok()); // New 2026 alphanumeric

// 2. Brazilian CPF:
let cpf_val = string().spek("[CPF]");
assert!(cpf_val.validate("529.982.247-25").is_ok());

// 3. Mathematical Checksum (Luhn algorithm for credit cards / IMEI):
let card_val = string().spek("[LUHN]");

// 4. ASCII ranges & custom character specifications:
let uppercase = string().spek("[ASCII 65..90]"); // 'A'..='Z'
let custom_charset = string().spek("[ABC234]").block("!");

// 5. Encodings:
let hex_val = string().spek("[HEX]");
let b64_val = string().spek("[BASE64]");
let b58_val = string().spek("[BASE58]");

3. Validating Structs (Optional Fields, Refinements & Nested Schemas)

Define schemas for arbitrary structs using typed field selectors, cross-field validations, and nested schemas:

use catfive::{schema, string, number, ValidationError};

struct Address {
    city: String,
}

struct User {
    username: String,
    bio: Option<String>,
    password: String,
    confirm_password: String,
    address: Address,
}

fn main() {
    let address_schema = schema::<Address>()
        .field("city", |a| &a.city, string().min(3));

    let user_schema = schema::<User>()
        .field("username", |u| &u.username, string().min(3).block(";@#$&"))
        .optional_field("bio", |u| u.bio.as_deref(), string().max(160))
        .refine("confirm_password", |u| {
            if u.password == u.confirm_password {
                Ok(())
            } else {
                Err(ValidationError::new("password_mismatch", "Passwords do not match"))
            }
        })
        .nested("address", |u| &u.address, address_schema);
}

4. Validating Collections & Arrays (catfive::list)

Validate vectors and slices with collection-level constraints and individual item checks:

use catfive::{list, string, Validator};

let tags_validator = list::<&str, _>(string().min(2).alphanumeric())
    .min_items(1)
    .max_items(5)
    .unique();

let tags = vec!["rust", "web", "api"];
assert!(tags_validator.validate(&tags).is_ok());

let duplicate_tags = vec!["rust", "rust"];
assert!(tags_validator.validate(&duplicate_tags).is_err()); // "unique_items"

5. Validating Forms (Key-Value Payloads)

Perfect for HTTP requests, query params, and HTML forms:

use std::collections::HashMap;
use catfive::{form, string};

let signup_form = form()
    .field("username", string().min(3).block(";@#$&"))
    .field("email", string().email())
    .optional_field("bio", string().max(160));

let mut form_data = HashMap::new();
form_data.insert("username", "kreivetech");
form_data.insert("email", "contact@kreive.com.br");

assert!(signup_form.validate(&form_data).is_ok());

6. Built-in String Validators

  • .spek("[...]") / .spek_msg("[...]", "...")
  • .email() / .email_msg(...)
  • .url() / .url_msg(...)
  • .uuid() / .uuid_msg(...)
  • .ip() / .ip_msg(...)
  • .in_list(&["admin", "user"]) / .in_list_msg(...)
  • .contains("substr") / .contains_msg(...)
  • .starts_with("prefix") / .ends_with("suffix")
  • .alphanumeric() / .alphabetic() / .numeric()
  • .block("chars") / .allow_only("chars")
  • .regex(pattern) (requires regex feature)

7. JSON Serialization for Web APIs (serde feature)

Enable the serde feature in Cargo.toml to serialize errors directly into JSON responses (e.g. HTTP 422 Unprocessable Entity in Axum or Actix-web):

#[cfg(feature = "serde")]
fn return_json_error(errors: &catfive::ValidationErrors) -> String {
    serde_json::to_string(errors).unwrap()
}

License

This project is licensed under the MIT License.