257 lines
5.8 KiB
Markdown
Raw Permalink Normal View History

2025-12-28 18:28:34 +00:00
# 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`