2026-01-11 22:35:49 +00:00

5.8 KiB

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

cargo run --bin typedialog -- examples/13-conditional-logic/conditional-demo.toml
```text

### TUI Backend

```bash
cargo run --bin typedialog-tui -- examples/13-conditional-logic/conditional-demo.toml
```text

### Web Backend

```bash
cargo run --bin typedialog-web -- examples/13-conditional-logic/conditional-demo.toml
```text

## 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"
```text

#### 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"
```text

### 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"
```text

### 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"
```text

**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)"
```text

**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"
```text

### Conditional Fragment Loading

Load entire form sections conditionally:

```toml
[[elements]]
name = "docker_setup"
type = "group"
includes = ["fragments/docker-init.toml"]
when = "!file_exists(Dockerfile)"
```text

### 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)"
```text

## 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"
```text

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`