domainstack-cli
Code generation CLI for the domainstack full-stack validation ecosystem. Generate TypeScript/Zod schemas, JSON Schema, and OpenAPI specs from your Rust #[validate(...)] attributes.
Overview
domainstack-cli is a command-line tool that transforms Rust types annotated with domainstack validation rules into equivalent schemas for other languages and frameworks. This ensures your validation logic stays synchronized across your entire stack.
# Single source of truth in Rust
#[derive(Validate)]
# Generate TypeScript/Zod schemas
# Generate JSON Schema (Draft 2020-12)
# Generate OpenAPI 3.0/3.1 specification
Installation
From crates.io (when published)
From source
# Clone the repository
# Build and install
Verify installation
Quick Start
1. Define your Rust types with validation rules
// src/models.rs
use Validate;
2. Generate Zod schemas
3. Use the generated schemas in TypeScript
// frontend/src/schemas.ts (auto-generated)
import { z } from "zod";
export const UserSchema = z.object({
email: z.string().email().max(255),
username: z.string().min(3).max(50).regex(/^[a-zA-Z0-9]*$/),
age: z.number().min(18).max(120),
profile_url: z.string().url().optional(),
});
export type User = z.infer<typeof UserSchema>;
// Use it in your application
const result = UserSchema.safeParse(formData);
if (result.success) {
// Type-safe validated data
const user: User = result.data;
}
Commands
domainstack zod
Generate Zod validation schemas from Rust types.
Options:
-i, --input <PATH>- Input directory containing Rust source files (default:src)-o, --output <PATH>- Output TypeScript file (required)-w, --watch- Watch for changes and regenerate automatically-v, --verbose- Enable verbose output-h, --help- Print help information
Examples:
# Basic usage
# Specify input directory
# Verbose output
# Watch mode for development
domainstack json-schema
Generate JSON Schema (Draft 2020-12) from Rust types.
Options:
-i, --input <PATH>- Input directory containing Rust source files (default:src)-o, --output <PATH>- Output JSON file (required)-w, --watch- Watch for changes and regenerate automatically-v, --verbose- Enable verbose output-h, --help- Print help information
Examples:
# Basic usage
# Specify input directory
# Verbose with watch mode
Generated output:
domainstack openapi
Generate OpenAPI 3.0/3.1 specification from Rust types.
Options:
-i, --input <PATH>- Input directory containing Rust source files (default:src)-o, --output <PATH>- Output JSON file (required)--openapi-31- Use OpenAPI 3.1 (default is 3.0)-w, --watch- Watch for changes and regenerate automatically-v, --verbose- Enable verbose output-h, --help- Print help information
Examples:
# Generate OpenAPI 3.0 spec
# Generate OpenAPI 3.1 spec
# Watch mode
Generated output:
Supported Validation Rules
String Validations
| Rust Attribute | Zod Output | Description |
|---|---|---|
#[validate(email)] |
.email() |
Valid email address |
#[validate(url)] |
.url() |
Valid URL |
#[validate(min_len = N)] |
.min(N) |
Minimum string length |
#[validate(max_len = N)] |
.max(N) |
Maximum string length |
#[validate(length(min = N, max = M))] |
.min(N).max(M) |
String length range |
#[validate(non_empty)] |
.min(1) |
Non-empty string |
#[validate(non_blank)] |
.trim().min(1) |
Non-blank string (after trim) |
#[validate(alphanumeric)] |
.regex(/^[a-zA-Z0-9]*$/) |
Alphanumeric only |
#[validate(alpha_only)] |
.regex(/^[a-zA-Z]*$/) |
Letters only |
#[validate(numeric_string)] |
.regex(/^[0-9]*$/) |
Digits only |
#[validate(ascii)] |
.regex(/^[\x00-\x7F]*$/) |
ASCII characters only |
#[validate(starts_with = "prefix")] |
.startsWith("prefix") |
Must start with prefix |
#[validate(ends_with = "suffix")] |
.endsWith("suffix") |
Must end with suffix |
#[validate(contains = "substring")] |
.includes("substring") |
Must contain substring |
#[validate(matches_regex = "pattern")] |
.regex(/pattern/) |
Custom regex pattern |
#[validate(no_whitespace)] |
.regex(/^\S*$/) |
No whitespace allowed |
Numeric Validations
| Rust Attribute | Zod Output | Description |
|---|---|---|
#[validate(range(min = N, max = M))] |
.min(N).max(M) |
Numeric range |
#[validate(min = N)] |
.min(N) |
Minimum value |
#[validate(max = N)] |
.max(N) |
Maximum value |
#[validate(positive)] |
.positive() |
Must be positive (> 0) |
#[validate(negative)] |
.negative() |
Must be negative (< 0) |
#[validate(non_zero)] |
.refine(n => n !== 0, ...) |
Cannot be zero |
#[validate(multiple_of = N)] |
.multipleOf(N) |
Must be multiple of N |
#[validate(finite)] |
.finite() |
Must be finite (not NaN/Infinity) |
Type Mappings
| Rust Type | Zod Type | Notes |
|---|---|---|
String |
z.string() |
|
bool |
z.boolean() |
|
u8, u16, u32, i8, i16, i32, f32, f64 |
z.number() |
|
u64, u128, i64, i128 |
z.number() |
With precision warning comment |
Option<T> |
T.optional() |
Validations applied to inner type |
Vec<T> |
z.array(T) |
|
| Custom types | CustomTypeSchema |
References generated schema |
Examples
Multiple Validation Rules
You can apply multiple validation rules to a single field:
Generates:
export const AccountSchema = z.object({
email: z.string().email().max(255).min(1),
password: z.string().min(8).max(128).regex(/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])/),
});
Optional Fields with Validations
Optional fields have .optional() applied AFTER all validations:
Generates:
export const ProfileSchema = z.object({
website: z.string().url().optional(), // Correct order
bio: z.string().min(10).max(500).optional(),
});
Arrays and Collections
Generates:
export const PostSchema = z.object({
tags: z.array(z.string()),
scores: z.array(z.number()).min(1).max(100),
});
Architecture
Unified CLI Design
domainstack-cli is designed as a unified code generation tool with a single binary and multiple subcommands:
domainstack
├── zod ✅ Generate Zod schemas
├── json-schema ✅ Generate JSON Schema (Draft 2020-12)
├── openapi ✅ Generate OpenAPI 3.0/3.1 specification
├── yup 📋 Generate Yup schemas (planned)
├── graphql 📋 Generate GraphQL schemas (planned)
└── prisma 📋 Generate Prisma schemas (planned)
Benefits:
- Single installation, multiple generators
- Shared parsing infrastructure (efficient, consistent)
- Consistent CLI interface across all generators
- Easy to add new generators
Internal Structure
domainstack-cli/
├── src/
│ ├── main.rs # CLI entry point with clap
│ ├── commands/ # Subcommand implementations
│ │ ├── zod.rs
│ │ ├── json_schema.rs
│ │ └── openapi.rs
│ ├── parser/ # Shared parsing infrastructure
│ │ ├── mod.rs # Directory walking
│ │ ├── ast.rs # Rust AST parsing
│ │ └── validation.rs # Validation rule extraction
│ └── generators/ # Language-specific generators
│ ├── zod.rs
│ ├── json_schema.rs
│ └── openapi.rs
The parser module (parser/) is shared across all generators, ensuring consistent interpretation of Rust validation rules. Each generator (generators/) contains language-specific transformation logic.
Future Generators
The roadmap includes support for additional generators:
Yup (TypeScript validation)
GraphQL Schema Definition Language
Prisma Schema
Documentation
- JSON_SCHEMA.md - Complete JSON Schema generation guide
- examples/json_schema_demo.rs - Example types demonstrating tuple structs, enums, and nested validation
Contributing
Adding a New Generator
To add a new generator (e.g., Yup):
- Create generator file:
src/generators/yup.rs - Implement generation logic using shared parser types
- Create command file:
src/commands/yup.rs - Add subcommand to
src/main.rs
The shared parser infrastructure (parser::ParsedType, parser::ValidationRule) makes adding new generators straightforward - focus only on the output format transformation.
Development Setup
# Clone repository
# Build CLI
# Run tests
# Install locally
Troubleshooting
"No types found with validation rules"
Make sure your Rust types:
- Have
#[derive(Validate)]attribute - Contain at least one
#[validate(...)]attribute - Are in
.rsfiles within the input directory
Generated schemas don't match expectations
Run with --verbose flag to see parsing details:
Large integer precision warnings
JavaScript numbers cannot safely represent integers larger than Number.MAX_SAFE_INTEGER (2^53 - 1). Types like u64, i64, u128, i128 will generate schemas with inline warning comments:
big_number: z.number() /* Warning: Large integers may lose precision in JavaScript */
Consider using strings for large integers in your TypeScript schemas if precision is critical.
License
This project is part of the domainstack workspace. See the root LICENSE file for details.
Related Projects
- domainstack - Core validation library
- zod - TypeScript-first schema validation
- ts-rs - TypeScript type generation (no validation)
- typeshare - Cross-language type sharing