# shepherd-config

Configuration parsing and validation for Shepherd.

## Overview

This crate handles loading, parsing, and validating the TOML configuration that defines what entries are available, when they're available, and for how long. It provides:

- **Schema definitions** - Raw configuration structure as parsed from TOML
- **Policy objects** - Validated, ready-to-use policy structures
- **Validation** - Detailed error messages for misconfiguration
- **Hot reload support** - Configuration can be reloaded at runtime

## Configuration Format

Shepherd uses TOML for configuration. Here's a complete example:

```toml
config_version = 1

[service]
socket_path = "/run/shepherdd/shepherdd.sock"
data_dir = "/var/lib/shepherdd"
default_max_run_seconds = 1800  # 30 minutes default

# Internet connectivity check (optional)
[service.internet]
check = "https://connectivitycheck.gstatic.com/generate_204"
interval_seconds = 10
timeout_ms = 1500

# Global volume restrictions
[service.volume]
max_volume = 80
allow_unmute = true

# Default warning thresholds (seconds before expiry)
[[service.default_warnings]]
seconds_before = 300  # 5 minutes
severity = "info"

[[service.default_warnings]]
seconds_before = 60   # 1 minute
severity = "warn"

[[service.default_warnings]]
seconds_before = 10
severity = "critical"
message_template = "Closing in {remaining} seconds!"

# Entry definitions
[[entries]]
id = "minecraft"
label = "Minecraft"
icon = "minecraft"
kind = { type = "snap", snap_name = "mc-installer" }

[entries.internet]
required = true

[entries.availability]
[[entries.availability.windows]]
days = "weekdays"
start = "15:00"
end = "18:00"

[[entries.availability.windows]]
days = "weekends"
start = "10:00"
end = "20:00"

[entries.limits]
max_run_seconds = 1800       # 30 minutes per session
daily_quota_seconds = 7200   # 2 hours per day
cooldown_seconds = 600       # 10 minutes between sessions

[[entries]]
id = "educational-game"
label = "GCompris"
icon = "gcompris-qt"
kind = { type = "process", command = "gcompris-qt" }

[entries.availability]
always = true  # Always available

[entries.limits]
max_run_seconds = 3600  # 1 hour
```

## Usage

### Loading Configuration

```rust
use shepherd_config::{load_config, parse_config, Policy};
use std::path::Path;

// Load from file (typically ~/.config/shepherd/config.toml)
let policy = load_config("config.toml")?;

// Parse from string
let toml_content = std::fs::read_to_string("config.toml")?;
let policy = parse_config(&toml_content)?;

// Access entries
for entry in &policy.entries {
    println!("{}: {:?}", entry.label, entry.kind);
}
```

### Entry Kinds

Entries can be of several types:

```toml
# Regular process
kind = { type = "process", command = "/usr/bin/game", args = ["--fullscreen"] }

# Snap application
kind = { type = "snap", snap_name = "mc-installer" }

# Steam game (via Steam snap)
kind = { type = "steam", app_id = 504230 }

# Virtual machine (future)
kind = { type = "vm", driver = "qemu", args = { disk = "game.qcow2" } }

# Media playback (future)
kind = { type = "media", library_id = "movies" }

# Custom type
kind = { type = "custom", type_name = "my-launcher", payload = { ... } }
```

### Time Windows

Time windows control when entries are available:

```toml
[entries.availability]
[[entries.availability.windows]]
days = "weekdays"        # or "weekends", "all"
start = "15:00"
end = "18:00"

[[entries.availability.windows]]
days = ["sat", "sun"]    # Specific days
start = "09:00"
end = "21:00"
```

### Limits

Control session duration and frequency:

```toml
[entries.limits]
max_run_seconds = 1800        # Max duration per session
daily_quota_seconds = 7200    # Total daily limit
cooldown_seconds = 600        # Wait time between sessions
```

### Internet Requirements

Entries can require internet connectivity. When the device is offline, those entries are hidden.

```toml
[service.internet]
check = "https://connectivitycheck.gstatic.com/generate_204"
interval_seconds = 300
timeout_ms = 1500

[entries.internet]
required = true
# Optional per-entry override:
# check = "tcp://1.1.1.1:53"
```

## Validation

The configuration is validated at load time. Validation catches:

- **Duplicate entry IDs** - Each entry must have a unique ID
- **Empty commands** - Process entries must specify a command
- **Invalid time windows** - Start time must be before end time
- **Invalid thresholds** - Warning thresholds must be less than max run time
- **Negative durations** - All durations must be positive
- **Unknown kinds** - Entry types must be recognized (unless Custom)

```rust
use shepherd_config::{parse_config, ConfigError};

let result = parse_config(toml_str);
match result {
    Ok(policy) => { /* Use policy */ }
    Err(ConfigError::ValidationFailed { errors }) => {
        for error in errors {
            eprintln!("Config error: {}", error);
        }
    }
    Err(e) => eprintln!("Failed to load config: {}", e),
}
```

## Hot Reload

Configuration can be reloaded at runtime via the service's `ReloadConfig` command or by sending `SIGHUP` to the service process. Reload is atomic: either the new configuration is fully applied or the old one remains.

Active sessions continue with their original time limits when configuration is reloaded.

## Key Types

- `Policy` - Validated policy ready for the core engine
- `Entry` - A launchable entry definition
- `AvailabilityPolicy` - Time window rules
- `LimitsPolicy` - Duration and quota limits
- `WarningPolicy` - Warning threshold configuration
- `VolumePolicy` - Volume restrictions

## Design Philosophy

- **Human-readable** - TOML is easy to read and write
- **Strict validation** - Catch errors at load time, not runtime
- **Versioned schema** - `config_version` enables future migrations
- **Sensible defaults** - Minimal config is valid

## Dependencies

- `toml` - TOML parsing
- `serde` - Deserialization
- `chrono` - Time types
- `thiserror` - Error types
