chore: add new conditionals to docs

This commit is contained in:
Jesús Pérez 2025-12-28 18:28:34 +00:00
parent f7f7fec13b
commit 39e5c35a28
Signed by: jesus
GPG Key ID: 9F243E355E0BC939
5 changed files with 689 additions and 5 deletions

View File

@ -453,12 +453,135 @@ when = "database_driver == mysql"
required = true
```
**Supported operators**:
### Comparison Operators
- `==`: Equality
- `!=`: Inequality
- Parentheses for grouping (future)
- Logical AND/OR (future)
**Equality and inequality**:
- `==`: Equal to
- `!=`: Not equal to
```toml
[[elements]]
name = "postgres_config"
type = "text"
when = "database_driver == postgresql"
[[elements]]
name = "legacy_warning"
type = "section"
content = "⚠️ SQLite is for development only"
when = "database_driver != postgresql"
```
**Numeric comparisons**:
- `>`: Greater than
- `<`: Less than
- `>=`: Greater than or equal
- `<=`: Less than or equal
```toml
[[elements]]
name = "performance_warning"
type = "section"
content = "⚠️ High port number may require elevated privileges"
when = "port >= 1024"
[[elements]]
name = "pool_size_warning"
type = "section"
when = "connection_pool > 100"
```
### String Operators
**String matching**:
- `contains`: Check if field contains substring
- `startswith`: Check if field starts with prefix
- `endswith`: Check if field ends with suffix
```toml
[[elements]]
name = "rust_specific"
type = "text"
prompt = "Rust toolchain version"
when = "language contains rust"
[[elements]]
name = "protocol_warning"
type = "section"
when = "url startswith https"
[[elements]]
name = "yaml_parser"
type = "select"
when = "config_file endswith .yaml"
```
### Array Membership
**`in` operator**: Check if value exists in array field
```toml
[[elements]]
name = "rust_features"
type = "multiselect"
prompt = "Rust-specific features"
when = "rust in detected_languages"
options = [
{ value = "clippy", label = "Clippy linting" },
{ value = "cargo_audit", label = "Security auditing" }
]
[[elements]]
name = "python_virtualenv"
type = "confirm"
when = "python in languages"
```
**Note**: The `in` operator works with:
- Array fields (JSON array values)
- MultiSelect fields (comma-separated strings)
### File System Conditions
**`file_exists(path)`**: Check if file or directory exists
```toml
[[elements]]
name = "use_existing_config"
type = "confirm"
prompt = "Existing config.toml found. Use it?"
when = "file_exists(config.toml)"
[[elements]]
name = "create_new_config"
type = "text"
prompt = "Configuration name"
when = "!file_exists(.env)"
```
**Negation with `!`**:
- `!file_exists(path)`: File does NOT exist
```toml
[[elements]]
name = "docker_setup"
type = "group"
includes = ["fragments/docker-init.toml"]
when = "!file_exists(Dockerfile)"
```
### Future Support
**Planned features**:
- Parentheses for grouping: `(a == b) && (c == d)`
- Logical AND: `&&`
- Logical OR: `||`
---

View File

@ -0,0 +1,256 @@
# Conditional Logic Examples
This directory demonstrates all supported conditional operators in TypeDialog forms.
## Overview
TypeDialog supports rich conditional logic for dynamic form behavior. Fields can be shown/hidden based on previous answers using the `when` attribute.
## Running the Examples
### CLI Backend
```bash
cargo run --bin typedialog -- examples/13-conditional-logic/conditional-demo.toml
```
### TUI Backend
```bash
cargo run --bin typedialog-tui -- examples/13-conditional-logic/conditional-demo.toml
```
### Web Backend
```bash
cargo run --bin typedialog-web -- examples/13-conditional-logic/conditional-demo.toml
```
## Supported Operators
### Comparison Operators
#### Equality and Inequality
- `==`: Equal to
- `!=`: Not equal to
```toml
[[elements]]
name = "mysql_config"
when = "database_driver == mysql"
[[elements]]
name = "server_warning"
when = "database_driver != sqlite"
```
#### Numeric Comparisons
- `>`: Greater than
- `<`: Less than
- `>=`: Greater than or equal
- `<=`: Less than or equal
```toml
[[elements]]
name = "privileged_port_warning"
when = "server_port < 1024"
[[elements]]
name = "high_port_warning"
when = "server_port > 10000"
```
### String Operators
- `contains`: Check if field contains substring
- `startswith`: Check if field starts with prefix
- `endswith`: Check if field ends with suffix
```toml
[[elements]]
name = "https_notice"
when = "project_url startswith https"
[[elements]]
name = "github_specific"
when = "project_url endswith github.com"
[[elements]]
name = "gitlab_ci"
when = "project_url contains gitlab"
```
### Array Membership
- `in`: Check if value exists in array field
```toml
[[elements]]
name = "rust_toolchain"
when = "rust in detected_languages"
[[elements]]
name = "python_venv"
when = "python in detected_languages"
```
**Works with**:
- MultiSelect fields (JSON array or comma-separated string)
- Array values from JSON output
### File System Conditions
- `file_exists(path)`: Check if file or directory exists
- `!file_exists(path)`: Check if file does NOT exist (negation)
```toml
[[elements]]
name = "dockerfile_exists_notice"
when = "file_exists(Dockerfile)"
[[elements]]
name = "create_dockerfile"
when = "!file_exists(Dockerfile)"
[[elements]]
name = "env_setup"
type = "group"
includes = ["fragments/environment-setup.toml"]
when = "!file_exists(.env)"
```
**Path resolution**:
- Absolute paths: `/etc/config.toml`
- Relative paths: `Dockerfile`, `.env`, `config/settings.toml`
- Works with both files and directories
## Form Flow Example
1. **Select database** → Shows driver-specific fields
2. **Enter port number** → Shows warnings based on port range
3. **Enter project URL** → Shows platform-specific options
4. **Select languages** → Shows language-specific tooling
5. **Check for files** → Conditionally load setup fragments
## Key Features
### Dynamic Field Visibility
Fields appear/disappear based on user input:
```toml
# Only shows if user selects MySQL
[[elements]]
name = "mysql_password"
type = "password"
when = "database_driver == mysql"
```
### Conditional Fragment Loading
Load entire form sections conditionally:
```toml
[[elements]]
name = "docker_setup"
type = "group"
includes = ["fragments/docker-init.toml"]
when = "!file_exists(Dockerfile)"
```
### Multi-Condition Fields
Same field can have complex logic (future: `&&` and `||`):
```toml
# Current: Single condition per field
when = "rust in detected_languages"
# Future: Compound conditions
when = "(rust in detected_languages) && (server_port >= 1024)"
```
## Testing Scenarios
### Scenario 1: Database Configuration
1. Select `mysql` → See MySQL-specific fields
2. Select `postgresql` → See PostgreSQL-specific fields
3. Select `sqlite` → No extra server fields
### Scenario 2: Port Validation
1. Enter `80` → See privileged port warning (`< 1024`)
2. Enter `8080` → See standard port notice (`>= 1024`)
3. Enter `15000` → See high port warning (`> 10000`)
### Scenario 3: Language Detection
1. Select `rust` + `python` → See both Rust and Python config fields
2. Select only `javascript` → See only Node.js version selector
3. Select none → No language-specific fields appear
### Scenario 4: File System Checks
1. **With Dockerfile present**:
- Shows "Dockerfile found" notice
- Skips Docker setup wizard
2. **Without Dockerfile**:
- Asks to create Dockerfile
- Shows Docker setup group
3. **With .env file**:
- Asks to use existing config
- Skips environment setup
4. **Without .env file**:
- Loads environment setup fragment
- Prompts for all env variables
## Implementation Details
### Condition Evaluation
Conditions are evaluated at runtime during form execution:
1. User answers a field
2. System checks all pending fields for `when` conditions
3. Fields with satisfied conditions become visible
4. Fields with unsatisfied conditions remain hidden
### Fragment Loading
Conditional groups with `includes` load fragments only when condition is true:
```toml
[[elements]]
name = "advanced_config"
type = "group"
includes = ["fragments/advanced-settings.toml"]
when = "enable_advanced == true"
```
This prevents loading unnecessary form definitions until needed.
### Type Conversion
TypeDialog automatically handles type conversions in conditions:
- String `"8080"` compared to number `1024` → converted to number
- Boolean `true` compared to string `"true"` → compared as boolean
- Number compared to string number → compared numerically
## Related Documentation
- [Field Types Reference](../../docs/field_types.md) - All field types and attributes
- [Fragment System](../../docs/fragment-search-paths.md) - Dynamic form composition
- [Nickel Integration](../../docs/nickel.md) - Schema-driven forms
## Source Code
Condition evaluation logic: `crates/typedialog-core/src/form_parser/conditions.rs`

View File

@ -0,0 +1,198 @@
# Conditional Logic Demo
# Demonstrates all supported conditional operators in TypeDialog
name = "conditional_demo"
description = "Complete demonstration of conditional field visibility"
# ====================
# COMPARISON OPERATORS
# ====================
[[elements]]
name = "database_driver"
type = "select"
prompt = "Select database driver"
required = true
options = [
{ value = "sqlite", label = "SQLite (embedded)" },
{ value = "mysql", label = "MySQL" },
{ value = "postgresql", label = "PostgreSQL" }
]
# Equality (==)
[[elements]]
name = "mysql_config"
type = "text"
prompt = "MySQL connection string"
when = "database_driver == mysql"
placeholder = "mysql://localhost:3306/db"
# Inequality (!=)
[[elements]]
name = "server_warning"
type = "section"
content = "⚠️ You selected a server-based database. Ensure the server is running."
when = "database_driver != sqlite"
# ====================
# NUMERIC COMPARISONS
# ====================
[[elements]]
name = "server_port"
type = "text"
prompt = "Server port"
default = "8080"
required = true
# Greater than (>)
[[elements]]
name = "high_port_warning"
type = "section"
content = "⚠️ Port > 10000 is uncommon. Double-check your configuration."
when = "server_port > 10000"
# Less than (<)
[[elements]]
name = "privileged_port_warning"
type = "section"
content = "⚠️ Port < 1024 requires root/admin privileges."
when = "server_port < 1024"
# Greater than or equal (>=)
[[elements]]
name = "standard_port_notice"
type = "section"
content = "✓ Using standard user port range (>= 1024)"
when = "server_port >= 1024"
# Less than or equal (<=)
[[elements]]
name = "low_port_range"
type = "section"
content = "Using low port range (<= 5000)"
when = "server_port <= 5000"
# ====================
# STRING OPERATORS
# ====================
[[elements]]
name = "project_url"
type = "text"
prompt = "Project repository URL"
placeholder = "https://github.com/user/repo"
# startswith
[[elements]]
name = "https_notice"
type = "section"
content = "✓ Secure HTTPS URL detected"
when = "project_url startswith https"
# endswith
[[elements]]
name = "github_specific"
type = "text"
prompt = "GitHub Actions enabled?"
when = "project_url endswith github.com"
# contains
[[elements]]
name = "gitlab_ci"
type = "confirm"
prompt = "Enable GitLab CI integration?"
when = "project_url contains gitlab"
# ====================
# ARRAY MEMBERSHIP (in)
# ====================
[[elements]]
name = "detected_languages"
type = "multiselect"
prompt = "Which languages are used in your project?"
display_mode = "grid"
options = [
{ value = "rust", label = "🦀 Rust" },
{ value = "python", label = "🐍 Python" },
{ value = "javascript", label = "📜 JavaScript" },
{ value = "go", label = "🐹 Go" }
]
# Array membership check
[[elements]]
name = "rust_toolchain"
type = "select"
prompt = "Rust toolchain version"
when = "rust in detected_languages"
options = [
{ value = "stable", label = "Stable" },
{ value = "nightly", label = "Nightly" },
{ value = "beta", label = "Beta" }
]
[[elements]]
name = "python_venv"
type = "confirm"
prompt = "Use Python virtual environment?"
when = "python in detected_languages"
default = true
[[elements]]
name = "nodejs_version"
type = "select"
prompt = "Node.js version"
when = "javascript in detected_languages"
options = [
{ value = "18", label = "Node.js 18 LTS" },
{ value = "20", label = "Node.js 20 LTS" },
{ value = "latest", label = "Latest" }
]
# ====================
# FILE SYSTEM CONDITIONS
# ====================
# file_exists(path)
[[elements]]
name = "dockerfile_exists_notice"
type = "section"
content = "✓ Dockerfile found in current directory"
when = "file_exists(Dockerfile)"
# !file_exists(path) - negation
[[elements]]
name = "create_dockerfile"
type = "confirm"
prompt = "No Dockerfile found. Create one?"
when = "!file_exists(Dockerfile)"
default = true
[[elements]]
name = "use_existing_config"
type = "confirm"
prompt = "Existing .env file found. Use existing configuration?"
when = "file_exists(.env)"
[[elements]]
name = "env_setup"
type = "group"
includes = ["fragments/environment-setup.toml"]
when = "!file_exists(.env)"
# ====================
# COMBINED CONDITIONS
# ====================
[[elements]]
name = "rust_docker_setup"
type = "section"
content = "🦀 Rust + Docker detected. Consider using rust:alpine base image."
when = "rust in detected_languages"
[[elements]]
name = "production_ready_check"
type = "section"
content = "✅ Production-ready configuration detected (HTTPS + standard port)"
when = "server_port >= 1024"

View File

@ -0,0 +1,72 @@
# Environment Setup Fragment
# Loaded conditionally when .env file doesn't exist
name = "environment_setup"
description = "Environment variables configuration"
display_mode = "complete"
[[elements]]
name = "env_header"
type = "section_header"
title = "🔧 Environment Configuration"
border_top = true
border_bottom = true
[[elements]]
name = "env_mode"
type = "select"
prompt = "Environment mode"
required = true
options = [
{ value = "development", label = "Development" },
{ value = "staging", label = "Staging" },
{ value = "production", label = "Production" }
]
[[elements]]
name = "log_level"
type = "select"
prompt = "Log level"
default = "info"
options = [
{ value = "trace", label = "Trace (verbose)" },
{ value = "debug", label = "Debug" },
{ value = "info", label = "Info" },
{ value = "warn", label = "Warning" },
{ value = "error", label = "Error" }
]
[[elements]]
name = "debug_mode"
type = "confirm"
prompt = "Enable debug mode?"
when = "env_mode == development"
default = true
[[elements]]
name = "api_key"
type = "password"
prompt = "API Key"
required = true
help = "Your application API key"
[[elements]]
name = "database_url"
type = "text"
prompt = "Database URL"
placeholder = "postgresql://localhost:5432/mydb"
required = true
[[elements]]
name = "redis_url"
type = "text"
prompt = "Redis URL"
placeholder = "redis://localhost:6379"
when = "env_mode == production"
[[elements]]
name = "sentry_dsn"
type = "text"
prompt = "Sentry DSN (error tracking)"
when = "env_mode == production"
help = "Leave empty to disable error tracking"

View File

@ -189,6 +189,41 @@ cat examples/12-agent-execution/README.md
- [Tests](../tests/agent/) - Agent validation tests
- [Core Examples](../crates/typedialog-agent/typedialog-ag-core/examples/) - Rust API usage
### 13. **Conditional Logic** → [`13-conditional-logic/`](13-conditional-logic/)
Complete guide to all conditional operators and expressions.
**Operators demonstrated:**
| Category | Operators | Example |
|----------|-----------|---------|
| **Comparison** | `==`, `!=`, `>`, `<`, `>=`, `<=` | `port >= 1024` |
| **String** | `contains`, `startswith`, `endswith` | `url startswith https` |
| **Array** | `in` | `rust in detected_languages` |
| **File System** | `file_exists()`, `!file_exists()` | `!file_exists(Dockerfile)` |
**Features:**
- Dynamic field visibility based on user input
- Conditional fragment loading
- Multi-condition scenarios
- Type-safe comparisons
**Running the example:**
```bash
# CLI
cargo run --bin typedialog -- examples/13-conditional-logic/conditional-demo.toml
# TUI
cargo run --bin typedialog-tui -- examples/13-conditional-logic/conditional-demo.toml
# Web
cargo run --bin typedialog-web -- examples/13-conditional-logic/conditional-demo.toml
```
**Best for:** Dynamic forms, adaptive UX, configuration wizards
## Learning Path
```