logger2 0.1.5

A modern, feature-rich replacement for the classic `logger` CLI: syslog (local/remote, RFC3164 & RFC5424), truecolor hex output, emoji, JSON, and a fully configurable TOML config file.
Documentation
use clap::Parser;
use std::path::PathBuf;
use clap_color_help::default_styles;

/// logger2 - a modern, feature-rich replacement for the classic `logger` CLI.
///
/// Sends messages to the local or a remote syslog daemon, with optional
/// truecolor and emoji-decorated console output, JSON output, and a fully
/// configurable TOML config file.
#[derive(Parser, Debug)]
#[command(
    name = "logger2",
    author = "Hadi Cahyadi <cumulus13@gmail.com>",
    version,
    about,
    long_about = None,
    propagate_version = true,
    styles = default_styles()
)]
pub struct Cli {
    /// The message to log. If omitted, reads from --file or stdin.
    #[arg(value_name = "MESSAGE")]
    pub message: Vec<String>,

    /// Facility.level priority, e.g. "user.notice" or "local0.error".
    #[arg(short = 'p', long = "priority", value_name = "FACILITY.LEVEL")]
    pub priority: Option<String>,

    /// Friendly severity level (trace, debug, info, notice, warning, error,
    /// critical, alert, emergency). Overrides the level portion of --priority.
    #[arg(short = 'l', long = "level", value_name = "LEVEL")]
    pub level: Option<String>,

    /// Tag to mark the message with. Defaults to the current username.
    #[arg(short = 't', long = "tag", value_name = "TAG")]
    pub tag: Option<String>,

    /// Include the process ID in the tag (optionally override the PID shown).
    #[arg(short = 'i', long = "id", value_name = "PID", num_args = 0..=1, default_missing_value = "self")]
    pub id: Option<String>,

    /// Also write the message to stderr.
    #[arg(short = 's', long = "stderr")]
    pub stderr: bool,

    /// Read message content from FILE instead of the command line.
    #[arg(short = 'f', long = "file", value_name = "FILE")]
    pub file: Option<PathBuf>,

    /// Remote syslog server hostname or IP address.
    #[arg(short = 'n', long = "server", value_name = "HOST")]
    pub server: Option<String>,

    /// Remote syslog server port.
    #[arg(short = 'P', long = "port", value_name = "PORT")]
    pub port: Option<u16>,

    /// Use TCP for the remote connection (default is UDP).
    #[arg(short = 'T', long = "tcp", conflicts_with = "udp")]
    pub tcp: bool,

    /// Use UDP for the remote connection (this is the default).
    #[arg(short = 'd', long = "udp")]
    pub udp: bool,

    /// Write to the given Unix domain socket instead of the system default.
    #[arg(short = 'u', long = "socket", value_name = "PATH")]
    pub socket: Option<PathBuf>,

    /// Use RFC 5424 message formatting.
    #[arg(short = 'r', long = "rfc5424")]
    pub rfc5424: bool,

    /// Maximum message size in bytes before truncation/error.
    #[arg(short = 'S', long = "size", value_name = "BYTES")]
    pub size: Option<usize>,

    /// Use RFC 6587 octet-counting framing for TCP.
    #[arg(long = "octet-count")]
    pub octet_count: bool,

    /// Override the message color with a hex value, e.g. "#00FFFF".
    #[arg(short = 'c', long = "color", value_name = "HEX")]
    pub color: Option<String>,

    /// Override the emoji shown for this message.
    #[arg(short = 'e', long = "emoji", value_name = "EMOJI")]
    pub emoji: Option<String>,

    /// Disable colored output.
    #[arg(long = "no-color", conflicts_with = "color")]
    pub no_color: bool,

    /// Disable emoji output.
    #[arg(long = "no-emoji", conflicts_with = "emoji")]
    pub no_emoji: bool,

    /// Force colored output even when stdout/stderr is not a TTY.
    #[arg(long = "force-color", conflicts_with_all = ["no_color"])]
    pub force_color: bool,

    /// Emit each message as a JSON object on stdout.
    #[arg(long = "json")]
    pub json: bool,

    /// Path to an explicit config file (overrides auto-discovery).
    #[arg(long = "config", value_name = "PATH", env = "LOGGER2_CONFIG")]
    pub config: Option<PathBuf>,

    /// Write a default config file to the standard location (or --config
    /// path, if given) and exit.
    #[arg(long = "init-config")]
    pub init_config: bool,

    /// Print the fully-resolved effective configuration as TOML and exit.
    #[arg(long = "print-config")]
    pub print_config: bool,

    /// strftime-style timestamp format override.
    #[arg(long = "timestamp-format", value_name = "FORMAT")]
    pub timestamp_format: Option<String>,

    /// Attach a structured key=value field. May be repeated.
    #[arg(short = 'k', long = "kv", value_name = "KEY=VALUE")]
    pub kv: Vec<String>,

    /// Show what would be sent/printed without actually sending or writing.
    #[arg(long = "dry-run")]
    pub dry_run: bool,

    /// Suppress all non-essential output.
    #[arg(short = 'q', long = "quiet", conflicts_with = "verbose")]
    pub quiet: bool,

    /// Increase diagnostic verbosity (repeatable: -v, -vv).
    #[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
    pub verbose: u8,
}

impl Cli {
    pub fn protocol_override(&self) -> Option<crate::config::Protocol> {
        if self.tcp {
            Some(crate::config::Protocol::Tcp)
        } else if self.udp {
            Some(crate::config::Protocol::Udp)
        } else {
            None
        }
    }
}