Pydantic-like data validation and JSON serialization for Zig β bringing type-safe validation to the Zig ecosystem.
π Documentation | API Reference | Quick Start | Contributing
zigantic brings Pydantic-style data validation to Zig, using the type system for compile-time guarantees. Define validation rules as types, parse JSON with automatic error handling, and serialize with zero runtime overhead for unused features.
| Feature | Description |
|---|---|
| β‘ Compile-Time Driven | Validation logic is types. Constraints are checked at compile time. |
| π¦ Idiomatic Zig | No macros, no DSLs, no magic. Just types and functions. |
| π Human-Readable Errors | Field-aware messages with error codes (E001, E010, etc.) |
| π Zero Overhead | Unused features have zero runtime cost. |
| π 40+ Built-in Types | Strings, numbers, formats, collections with rich utilities. |
| π JSON Parsing | Parse and serialize JSON with automatic validation. |
| β 102 Tests | Comprehensive test coverage for reliability. |
zig fetch --save https://github.com/muhammad-fiaz/zigantic/archive/refs/tags/v0.0.1.tar.gzconst std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const zigantic = b.dependency("zigantic", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "my-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zigantic", zigantic.module("zigantic"));
b.installArtifact(exe);
}const std = @import("std");
const z = @import("zigantic");
pub fn main() !void {
// String with length constraints
const name = try z.String(1, 50).init("Alice");
std.debug.print("Name: {s} (len: {d})\n", .{name.get(), name.len()});
// Email with domain parsing
const email = try z.Email.init("alice@company.com");
std.debug.print("Email: {s}\n", .{email.get()});
std.debug.print("Domain: {s}\n", .{email.domain()});
std.debug.print("Business email: {}\n", .{email.isBusinessEmail()});
// Password with strength checking
const pwd = try z.Secret(8, 100).init("MyP@ssw0rd!");
std.debug.print("Password: {s}\n", .{pwd.masked()});
std.debug.print("Strength: {d}/6\n", .{pwd.strength()});
// Integer with range and utilities
const age = try z.Int(i32, 18, 120).init(25);
std.debug.print("Age: {d} (even: {}, positive: {})\n", .{
age.get(), age.isEven(), age.isPositive()
});
// IP address with network utilities
const ip = try z.Ipv4.init("192.168.1.1");
std.debug.print("IP: {s} (private: {})\n", .{ip.get(), ip.isPrivate()});
}Note: zigantic automatically checks for updates when using JSON functions. To disable, call
z.disableUpdateCheck()at the start of your program.
const std = @import("std");
const z = @import("zigantic");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Define a validated struct
const User = struct {
id: z.PositiveInt(u32),
name: z.String(1, 50),
age: z.Int(i32, 18, 120),
email: z.Email,
role: z.Default([]const u8, "user"),
website: ?z.Url = null,
};
const json =
\\{"id": 1, "name": "Alice", "age": 25, "email": "alice@example.com"}
;
var result = try z.fromJson(User, json, allocator);
defer result.deinit();
if (result.value) |user| {
std.debug.print("Welcome, {s}!\n", .{user.name.get()});
std.debug.print("Role: {s} (default)\n", .{user.role.get()});
}
if (!result.isValid()) {
for (result.error_list.errors.items) |err| {
std.debug.print("[{s}] {s}: {s}\n", .{
z.errorCode(err.error_type),
err.field,
err.message,
});
}
}
}| Type | Description | Example |
|---|---|---|
String(min, max) |
Length-constrained string | String(1, 50) |
NonEmptyString(max) |
Non-empty string | NonEmptyString(100) |
Trimmed(min, max) |
Auto-trim whitespace | Trimmed(1, 50) |
Lowercase(max) |
Lowercase only | Lowercase(50) |
Uppercase(max) |
Uppercase only | Uppercase(50) |
Alphanumeric(min, max) |
Letters and digits | Alphanumeric(1, 20) |
AsciiString(min, max) |
ASCII only (0-127) | AsciiString(1, 100) |
Secret(min, max) |
Password with strength | Secret(8, 100) |
StrongPassword(min, max) |
Requires upper+lower+digit+special | StrongPassword(8, 100) |
String Methods:
str.get() // Get value
str.len() // Length
str.isEmpty() // Check empty
str.startsWith("A") // Prefix check
str.endsWith("z") // Suffix check
str.contains("bc") // Contains check
str.charAt(0) // Character at index
str.slice(0, 5) // Substring
// Secret-specific
pwd.masked() // "********"
pwd.strength() // 0-6 score
pwd.hasUppercase() // bool
pwd.hasLowercase() // bool
pwd.hasDigit() // bool
pwd.hasSpecial() // bool| Type | Description | Example |
|---|---|---|
Int(T, min, max) |
Signed integer range | Int(i32, 0, 100) |
UInt(T, min, max) |
Unsigned integer range | UInt(u32, 1, 1000) |
PositiveInt(T) |
> 0 | PositiveInt(i32) |
NonNegativeInt(T) |
>= 0 | NonNegativeInt(i32) |
NegativeInt(T) |
< 0 | NegativeInt(i32) |
EvenInt(T, min, max) |
Even numbers only | EvenInt(i32, 0, 100) |
OddInt(T, min, max) |
Odd numbers only | OddInt(i32, 1, 99) |
MultipleOf(T, divisor) |
Must be multiple of N | MultipleOf(i32, 5) |
Float(T, min, max) |
Float range | Float(f64, 0.0, 1.0) |
Percentage(T) |
0-100 | Percentage(f64) |
Probability(T) |
0-1 | Probability(f64) |
PositiveFloat(T) |
> 0 | PositiveFloat(f64) |
NegativeFloat(T) |
< 0 | NegativeFloat(f64) |
FiniteFloat(T) |
No NaN/Infinity | FiniteFloat(f64) |
Number Methods:
n.get() // Get value
n.isPositive() // > 0
n.isNegative() // < 0
n.isZero() // == 0
n.isEven() // Even check
n.isOdd() // Odd check
n.abs() // Absolute value
n.clamp(0, 50) // Clamp to range
// Float-specific
f.floor() // Floor
f.ceil() // Ceiling
f.round() // Round
f.trunc() // Truncate| Type | Description | Methods |
|---|---|---|
Email |
Email address | domain(), localPart(), isBusinessEmail() |
Url |
HTTP/HTTPS URL | isHttps(), protocol(), host() |
HttpsUrl |
HTTPS only | - |
Uuid |
UUID format | version() |
Ipv4 |
IPv4 address | isPrivate(), isLoopback() |
Ipv6 |
IPv6 address | isLoopback() |
Slug |
URL slug | - |
Semver |
Semantic version | - |
PhoneNumber |
Phone number | hasCountryCode() |
CreditCard |
Credit card (Luhn) | cardType(), masked() |
Regex(pattern) |
Pattern matching | - |
| Type | Description | Methods |
|---|---|---|
List(T, min, max) |
List with length | len(), first(), last(), at(i) |
NonEmptyList(T, max) |
Non-empty list | Same as List |
FixedList(T, len) |
Exact size | at(i) |
| Type | Description | Methods |
|---|---|---|
Default(T, value) |
Default value | isDefault(), getOrDefault() |
Custom(T, fn) |
Custom validator | - |
Transform(T, fn) |
Transform value | getOriginal() |
Coerce(From, To) |
Type conversion | - |
Literal(T, value) |
Exact value match | - |
Partial(T) |
All fields optional | - |
OneOf(T, values) |
Allowed values | isFirst(), isLast() |
Range(T, s, e, step) |
Range with step | - |
Nullable(T) |
Explicit null | isNull(), unwrapOr() |
Lazy(T) |
Lazy evaluation | isComputed(), reset() |
Direct validation functions without types:
const v = z.validators;
// Format validators
v.isValidEmail("user@example.com") // true
v.isValidUrl("https://example.com") // true
v.isUuid("550e8400-...") // true
v.isIpv4("192.168.1.1") // true
v.isIpv6("::1") // true
v.isSlug("hello-world") // true
v.isSemver("1.2.3") // true
v.isPhoneNumber("+1234567890") // true
v.isJwt("header.payload.signature") // true
v.isValidCreditCard("4111...") // true
// String validators
v.isAlphanumeric("abc123") // true
v.isAlpha("hello") // true
v.isNumeric("12345") // true
v.isLowercase("hello") // true
v.isUppercase("HELLO") // true
v.isHexString("0123abcdef") // true
// Pattern matching
v.matchesPattern("[0-9][0-9][0-9]", "123") // true// Error messages and codes
if (z.String(3, 50).init("Jo")) |_| {} else |err| {
z.errorMessage(err) // "value is too short"
z.errorCode(err) // "E001"
}
// ErrorList for collecting multiple errors
var errors = z.errors.ErrorList.init(allocator);
defer errors.deinit();
try errors.add("name", error.TooShort, "too short", "Jo");
errors.count() // 1
errors.containsField("name") // true
// JSON output
const json = try errors.toJsonArray(allocator);
// [{"field":"name","message":"too short","value":"Jo"}]| Code | Error | Message |
|---|---|---|
| E001 | TooShort | value is too short |
| E002 | TooLong | value is too long |
| E003 | TooSmall | value is too small |
| E004 | TooLarge | value is too large |
| E010 | InvalidEmail | must be a valid email |
| E011 | InvalidUrl | must be a valid URL |
| E020 | MissingField | field is required |
| E021 | TypeMismatch | wrong type |
| E099 | CustomValidationFailed | validation failed |
The library includes 5 comprehensive examples:
zig build run-basic # Direct validation + JSON
zig build run-advanced_types # All 40+ types demo
zig build run-validators # Validator functions
zig build run-json_example # Full JSON workflow
zig build run-error_handling # Error managementzig build # Build library
zig build test # Run 102 tests
zig build example # Run basic exampleMIT License - Copyright (c) 2025 Muhammad Fiaz
Made with β€οΈ for the Zig community
β Star this repo β’ π Report Bug β’ π‘ Request Feature