tty-web 0.16.4

Web-based terminal emulator — opens a real PTY in the browser over WebSocket
Documentation
//! CLI configuration parsed from flags and environment variables.

use std::net::IpAddr;
use std::path::PathBuf;

use clap::{Parser, ValueEnum};

/// Log output format.
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogFormat {
    /// Human-readable text (default)
    Text,
    /// Structured JSON, one object per line
    Json,
}

/// Application configuration.
///
/// Every field can be set via a CLI flag (`--address`) or an environment
/// variable (`TTY_WEB_ADDRESS`). Defaults are suitable for local development.
#[derive(Parser, Debug, Clone)]
#[command(name = "tty-web", about = "Web-based terminal emulator")]
pub struct Config {
    /// Address to bind to
    #[arg(long, default_value = "127.0.0.1", env = "TTY_WEB_ADDRESS")]
    pub address: IpAddr,

    /// Port to listen on
    #[arg(long, default_value_t = 9090, env = "TTY_WEB_PORT")]
    pub port: u16,

    /// Shell to execute
    #[arg(long, default_value = "/bin/bash", env = "TTY_WEB_SHELL")]
    pub shell: String,

    /// Log level (trace, debug, info, warn, error)
    #[arg(long, default_value = "info", env = "TTY_WEB_LOG_LEVEL")]
    pub log_level: String,

    /// Log output format
    #[arg(long, default_value = "text", env = "TTY_WEB_LOG_FORMAT")]
    pub log_format: LogFormat,

    /// Working directory for new shell sessions
    #[arg(long, env = "TTY_WEB_PWD")]
    pub pwd: Option<PathBuf>,

    /// Scrollback buffer size in KiB (default: 256)
    #[arg(long, default_value_t = 256, env = "TTY_WEB_SCROLLBACK_LIMIT")]
    pub scrollback_limit: usize,

    /// Session orphan timeout in seconds — remove session after this long with no clients
    #[arg(long, default_value_t = 60, env = "TTY_WEB_ORPHAN_TIMEOUT")]
    pub orphan_timeout: u64,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_values() {
        let config = Config::parse_from(["tty-web"]);
        assert_eq!(config.address, "127.0.0.1".parse::<IpAddr>().unwrap());
        assert_eq!(config.port, 9090);
        assert_eq!(config.shell, "/bin/bash");
        assert_eq!(config.log_level, "info");
        assert_eq!(config.log_format, LogFormat::Text);
        assert_eq!(config.pwd, None);
        assert_eq!(config.scrollback_limit, 256);
        assert_eq!(config.orphan_timeout, 60);
    }

    #[test]
    fn test_custom_values() {
        let config = Config::parse_from([
            "tty-web",
            "--port",
            "8080",
            "--shell",
            "/bin/sh",
            "--address",
            "0.0.0.0",
            "--log-level",
            "debug",
            "--log-format",
            "json",
        ]);
        assert_eq!(config.address, "0.0.0.0".parse::<IpAddr>().unwrap());
        assert_eq!(config.port, 8080);
        assert_eq!(config.shell, "/bin/sh");
        assert_eq!(config.log_level, "debug");
        assert_eq!(config.log_format, LogFormat::Json);
    }

    #[test]
    fn test_pwd_flag() {
        let config = Config::parse_from(["tty-web", "--pwd", "/tmp"]);
        assert_eq!(config.pwd, Some(PathBuf::from("/tmp")));
    }
}