use clap::{Parser, ValueEnum};
use crate::detect::Kind;
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum TransferArg {
Auto,
Direct,
File,
Shm,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum ThemeArg {
Auto,
Dark,
Light,
}
const AFTER_HELP: &str = "\
\x1b[1mExamples:\x1b[0m
shw photo.jpg open the image viewer (wheel / + - to zoom)
shw ~/Pictures browse every image/video/markdown file in a folder
shw clip.mp4 --start 1:30 play a video from 1m30s
shw README.md GitHub-style markdown pager
shw -i diagram.svg print inline into the terminal (like icat)
curl -sL https://…/x.png | shw -
fzf --preview 'shw -i {}' preview files in fzf
\x1b[1mKeys:\x1b[0m press ? inside any viewer.
Requires a terminal implementing the kitty graphics protocol
(kitty, Ghostty, WezTerm, Konsole, …). Works over ssh and in tmux
(with `set -g allow-passthrough on`).";
#[derive(Parser, Debug)]
#[command(name = "shw", version, about, after_help = AFTER_HELP, max_term_width = 100)]
pub struct Cli {
#[arg(value_name = "FILE")]
pub files: Vec<String>,
#[arg(short, long, env = "SHW_INLINE")]
pub inline: bool,
#[arg(short = 't', long = "type", value_enum, value_name = "TYPE")]
pub kind: Option<Kind>,
#[arg(short, long, value_name = "LANG")]
pub language: Option<String>,
#[arg(short = 'W', long, value_name = "COLS")]
pub width: Option<u16>,
#[arg(short = 'H', long, value_name = "ROWS")]
pub height: Option<u16>,
#[arg(long)]
pub upscale: bool,
#[arg(long, value_enum, default_value = "auto", env = "SHW_THEME")]
pub theme: ThemeArg,
#[arg(long, value_enum, default_value = "auto", value_name = "MODE")]
pub transfer: TransferArg,
#[arg(long)]
pub placeholders: bool,
#[arg(short, long, value_name = "TIME", value_parser = parse_time, default_value = "0")]
pub start: f64,
#[arg(short, long)]
pub mute: bool,
#[arg(long = "loop")]
pub looping: bool,
#[arg(long, env = "SHW_OFFLINE")]
pub offline: bool,
#[arg(short, long)]
pub force: bool,
#[arg(long)]
pub doctor: bool,
#[arg(long, value_enum, value_name = "SHELL")]
pub completions: Option<clap_complete::Shell>,
}
fn parse_time(s: &str) -> Result<f64, String> {
let mut total = 0.0;
for part in s.split(':') {
let v: f64 = part
.trim()
.parse()
.map_err(|_| format!("invalid time `{s}`"))?;
total = total * 60.0 + v;
}
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn times() {
assert_eq!(parse_time("90").unwrap(), 90.0);
assert_eq!(parse_time("1:30").unwrap(), 90.0);
assert_eq!(parse_time("1:00:05").unwrap(), 3605.0);
assert!(parse_time("x").is_err());
}
#[test]
fn cli_is_valid() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
}