# Validators and Contracts Example

Comprehensive guide to **validation**, **constraints**, and **type-safe contracts** in TypeDialog.

Learn how to ensure data quality and enforce business rules with declarative validators that work across all backends (CLI, TUI, Web).

## Overview

TypeDialog provides multiple levels of validation:

| Level | Mechanism | Examples |
|-------|-----------|----------|
| **Field-Level** | Built-in validators | `required`, `min/max`, date ranges |
| **Array-Level** | Constraints | `min_items`, `max_items`, `unique` |
| **Selection-Level** | Multiselect limits | `min_selected`, `max_selected` |
| **Conditional** | Smart visibility | `when` clause for dependencies |
| **Type-Safe** | Nickel contracts | Compile-time validation |

## Examples Included

### 1. Basic Validators (`basic-validators.toml`)

Foundational validation patterns:

```bash
cargo run --example basic_validators

# Or with specific backend
cargo run -p typedialog-tui --example basic_validators
cargo run -p typedialog-web -- --config examples/14-validators-and-contracts/basic-validators.toml
```

**Demonstrates**

- ✅ `required` - Fields that cannot be empty
- ✅ `min/max` - Numeric range validation
- ✅ `min_selected/max_selected` - Multiselect constraints
- ✅ `min_items/max_items` - Array size limits
- ✅ `min_date/max_date` - Date range validation
- ✅ Constraints as configuration (single source of truth)

**Key Features:**

```toml
# Required field
[[elements]]
name = "username"
required = true

# Multiselect with limits
[[elements]]
name = "interests"
min_selected = 2
max_selected = 4

# Date range validation
[[elements]]
name = "birth_date"
min_date = "1950-01-01"
max_date = "2006-12-31"

# Constraints (DRY configuration)
[constraints]
username_min_length = 3
username_max_length = 32
```

---

### 2. Advanced Validators (`advanced-validators.toml`)

Sophisticated validation patterns and field dependencies:

```bash
cargo run --example advanced_validators
```

**Demonstrates**

- ✅ Smart defaults with environment variables (`{{ env.USER }}`)
- ✅ Field dependencies (`options_from`)
- ✅ Array uniqueness (`unique = true`)
- ✅ Multi-field validation rules
- ✅ Regex patterns for validation
- ✅ Constraint reuse for consistency

**Key Features:**

```toml
# Smart defaults from environment
[[elements]]
name = "current_user"
default = "{{ env.USER }}"

# Field dependencies - language options depend on platform
[[elements]]
name = "platform"
type = "select"
options = [...]

[[elements]]
name = "language"
options_from = "platform"  # Filter based on platform selection
options = [...]

# Array with uniqueness constraint
[[elements]]
name = "team_members"
type = "repeating-group"
min_items = 1
max_items = 10
fragment = "fragments/team-member.toml"
```

**Constraint Configuration:**

```toml
[constraints]
min_connections = 1
max_connections = 10000

[constraints.regex_patterns]
email = "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
hostname = "^[a-z0-9]..."
url = "^https?://..."
version = "^[0-9]+\\.[0-9]+\\.[0-9]+"
```

---

### 3. Conditional Validators (`conditional-validators.toml`)

Master field visibility and conditional requirements using the `when` clause:

```bash
cargo run --example conditional_validators
```

**Demonstrates**

- ✅ Conditional field visibility (`when` clause)
- ✅ Conditional requirements (show + require)
- ✅ Cascading conditions (multiple levels)
- ✅ Smart defaults based on conditions

**Key Features:**

```toml
# Conditional visibility
[[elements]]
name = "account_type"
type = "select"
options = [
  { value = "personal", label = "Personal" },
  { value = "business", label = "Business" },
]

# Only show if personal or education
[[elements]]
name = "first_name"
when = "account_type == personal || account_type == education"
required = true

# Only show if business
[[elements]]
name = "company_name"
when = "account_type == business"
required = true
```

**Multi-Level Conditions:**

```toml
# Level 1: Enable feature
[[elements]]
name = "enable_2fa"
type = "confirm"

# Level 2: Choose method (if enabled)
[[elements]]
name = "2fa_method"
when = "enable_2fa == true"
required = true

# Level 3: Provide details (if specific method)
[[elements]]
name = "phone_for_2fa"
when = "enable_2fa == true && 2fa_method == sms"
required = true
```

---

### 4. Type-Safe Contracts (`nickel-validators.ncl`)

Compile-time validation using Nickel contracts:

```bash
# View the schema
nickel eval examples/14-validators-and-contracts/nickel-validators.ncl

# Query the inputs section
nickel query examples/14-validators-and-contracts/nickel-validators.ncl inputs

# Convert to TOML form
nickel query examples/14-validators-and-contracts/nickel-validators.ncl inputs | \
  typedialog parse-nickel > nickel-output.toml
```

**Demonstrates**

- ✅ Custom validation contracts
- ✅ Type constraints (`std.string.NonEmpty`, `std.number.Between`)
- ✅ Email and password validation
- ✅ Enum-like validation
- ✅ Optional fields with contracts
- ✅ Nested object validation

**Key Features:**

```nickel
# Custom email validator
let EmailContract = std.contract.from_predicate (
  fun x =>
    if std.string.is_match "^[^@]+@[^@]+\\.[^@]+$" x then true
    else std.contract.blame_with ("Invalid email format")
)
in

# Usage in schema
{
  email | EmailContract = "",

  # Type-safe enum
  role | std.contract.EnumContract ["user", "admin"] = "user",

  # Number range
  port | std.number.Between 1 65535 = 8080,

  # Non-empty string list
  tags | std.contract.list std.string.NonEmpty = [],

  # Optional field
  bio | (std.string.MaxLength 500 | std.contract.optional) = null,
}
```

---

## Validation Rules Reference

### Field-Level Validators

| Validator | Type | Example | Use Case |
|-----------|------|---------|----------|
| `required` | Boolean | `required = true` | Field cannot be empty |
| `default` | String | `default = "value"` | Pre-fill field with value |
| `placeholder` | String | `placeholder = "hint"` | Show hint text |
| `min_date` | Date | `min_date = "2020-01-01"` | Minimum date |
| `max_date` | Date | `max_date = "2025-12-31"` | Maximum date |
| `when` | Expression | `when = "field == value"` | Show if condition true |

### Selection Validators

| Validator | Type | Example | Use Case |
|-----------|------|---------|----------|
| `min_selected` | Number | `min_selected = 2` | Minimum selected items |
| `max_selected` | Number | `max_selected = 5` | Maximum selected items |
| `options_from` | Field | `options_from = "platform"` | Filter options |

### Array Validators

| Validator | Type | Example | Use Case |
|-----------|------|---------|----------|
| `min_items` | Number | `min_items = 1` | Minimum array size |
| `max_items` | Number | `max_items = 10` | Maximum array size |
| `unique` | Boolean | `unique = true` | No duplicate items |

### Constraints Section

Store validation limits in a `[constraints]` section for DRY principle:

```toml
[constraints]
username_min_length = 3
username_max_length = 32

password_min_length = 8
password_requires_uppercase = true
password_requires_digits = true

[constraints.regex_patterns]
email = "^[^@]+@[^@]+\\.[^@]+$"
url = "^https?://..."
```

---

## Backend Compatibility

All validators work across all backends:

| Feature | CLI | TUI | Web |
|---------|-----|-----|-----|
| `required` | ✅ | ✅ | ✅ |
| `min/max` | ✅ | ✅ | ✅ |
| `min_items/max_items` | ✅ | ✅ | ✅ |
| `when` (conditional) | ✅ | ✅ | ✅ |
| `min_selected/max_selected` | ✅ | ✅ | ✅ |
| Date range validation | ✅ | ✅ | ✅ |
| Nickel contracts | ✅ | ✅ | ✅ |

---

## Common Patterns

### 1. Account Type-Based Validation

Show different fields based on account type:

```toml
[[elements]]
name = "account_type"
type = "select"
options = [
  { value = "personal", label = "Personal" },
  { value = "business", label = "Business" },
]

# Only for business accounts
[[elements]]
name = "company_name"
when = "account_type == business"
required = true
```

### 2. Smart Defaults from Environment

Auto-populate from system:

```toml
[[elements]]
name = "current_user"
default = "{{ env.USER }}"

[[elements]]
name = "home_dir"
default = "{{ env.HOME }}"
```

### 3. Multi-Level Conditional Validation

Show fields based on multiple conditions:

```toml
# Level 1: Enable feature
[[elements]]
name = "enable_api"
type = "confirm"

# Level 2: Choose type (if enabled)
[[elements]]
name = "api_type"
when = "enable_api == true"
required = true
options = [...]

# Level 3: Provide details (if OAuth2)
[[elements]]
name = "oauth_client_id"
when = "enable_api == true && api_type == oauth2"
required = true
```

### 4. Array Constraints with Uniqueness

Ensure unique items in arrays:

```toml
[[elements]]
name = "team_members"
type = "repeating-group"
min_items = 1
max_items = 10
fragment = "fragments/unique-member.toml"

# In unique-member.toml:
[[elements]]
name = "email"
required = true
unique = true  # No duplicate emails
```

### 5. Range Validation

Constrain numeric values:

```toml
[[elements]]
name = "age"
type = "text"
placeholder = "18-100"  # Hint

[[elements]]
name = "port"
placeholder = "1024-65535"

[constraints]
age_min = 18
age_max = 100
port_min = 1024
port_max = 65535
```

---

## Testing Validators

### CLI Backend

```bash
# Run with interactive prompts
cargo run --example conditional_validators

# Try entering invalid values:
# - Empty required fields
# - Out of range values
# - Wrong date ranges
```

### TUI Backend

```bash
# Rich terminal UI shows validation errors in real-time
cargo run -p typedialog-tui --example conditional_validators

# Keyboard navigation shows which fields are required
# Arrows/Tab show conditional fields appear/disappear
```

### Web Backend

```bash
# Start web server
cargo run -p typedialog-web -- \
  --config examples/14-validators-and-contracts/basic-validators.toml

# Open browser to http://localhost:3000
# Test validation - HTML5 constraints + server-side
```

---

## Performance Considerations

### Constraints vs. Hardcoded Values

**Good (DRY)**

```toml
[constraints]
max_connections = 10000

[[elements]]
name = "connections"
placeholder = "1-${constraint.max_connections}"
```

**Avoid (Duplication)**

```toml
[[elements]]
name = "connections"
placeholder = "1-10000"

# If you change the limit, you need to update multiple places
```

### When to Use Nickel Contracts

**Use Nickel when**

- You need compile-time validation
- You want type-safety
- Custom business logic is complex
- You're generating schemas from Nickel

**Use TOML when**

- Simple validation is sufficient
- You want quick iteration
- You're manually writing forms

---

## Troubleshooting

### "Field appears/disappears unexpectedly"

Check your `when` clause syntax:

```toml
# ✅ CORRECT
when = "account_type == business"
when = "enable_2fa == true && api_type == oauth2"

# ❌ INCORRECT
when = "account_type = business"  # Single = is assignment
when = "account_type eq business"  # Wrong syntax
```

### "Validation not working on Web backend"

Ensure field is in the form definition:

```toml
# Make sure required is set
[[elements]]
name = "email"
required = true  # This is checked by web backend

# Check min/max constraints
min_selected = 2
max_selected = 5
```

### "Nickel contract errors"

Verify Nickel syntax:

```bash
# Check for syntax errors
nickel eval nickel-validators.ncl

# If it fails, review contract definitions
# Ensure all contracts use std.contract.from_predicate
```

---

## Related Examples

- [`02-advanced`](../02-advanced/) - Conditional logic
- [`05-fragments`](../05-fragments/) - Array constraints
- [`07-nickel-generation`](../07-nickel-generation/) - Nickel basics
- [`08-nickel-roundtrip`](../08-nickel-roundtrip/) - Full Nickel workflow

---

## Next Steps

1. **Start Simple** - Run `basic-validators.toml`, understand each field
2. **Add Complexity** - Try `advanced-validators.toml` with dependencies
3. **Use Conditionals** - Explore `conditional-validators.toml` with multi-level logic
4. **Go Type-Safe** - Learn Nickel contracts with `nickel-validators.ncl`
5. **Combine Approaches** - Mix TOML and Nickel in your own forms

---

**Questions?** See [../../docs/field-types.md](../../docs/field-types.md) for field type reference.

**Want advanced patterns?** Check [`11-prov-gen`](../11-prov-gen/) for enterprise-scale validation.
