use std::{fmt, path::PathBuf};
#[derive(clap::Parser)]
#[command(author, version, max_term_width = 80)]
pub struct Opts {
#[command(flatten)]
log: LogOpts,
#[command(subcommand)]
cmd: Command,
}
#[derive(Default, clap::Args)]
pub struct LogOpts {
#[arg(short, long, global = true)]
quiet: bool,
#[arg(long, conflicts_with("quiet"), global = true)]
no_quiet: bool,
#[arg(
short,
long,
global = true,
action(clap::ArgAction::Count),
conflicts_with("quiet"),
conflicts_with("no_quiet")
)]
verbose: u8,
}
#[derive(clap::Subcommand)]
pub enum Command {
Server,
#[command(flatten)]
Client(ClientCommand),
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum FormatKind {
Json,
Pretty,
}
#[derive(Debug, Clone, clap::Args)]
pub struct NowPlayingFormat {
#[arg(
short = 'f',
long = "format",
conflicts_with("file"),
conflicts_with("kind")
)]
pub string: Option<String>,
#[arg(
short = 'F',
long = "format-from",
conflicts_with("string"),
conflicts_with("kind")
)]
pub file: Option<PathBuf>,
#[arg(short = 'e', long = "extended", conflicts_with("kind"))]
pub extended: bool,
#[arg(
short = 'o',
long = "output",
default_value = "pretty",
conflicts_with("string"),
conflicts_with("file")
)]
pub kind: FormatKind,
}
#[derive(Debug, Clone, clap::Args)]
pub struct NowPlayingOpts {
#[command(flatten)]
pub player: PlayerOpts,
#[command(flatten)]
pub format: NowPlayingFormat,
#[arg(short, long, conflicts_with("PlayerOpts"))]
pub watch: bool,
#[arg(short = '0', long, requires("watch"))]
pub zero: bool,
#[arg(short = 'n', long, conflicts_with("watch"))]
pub no_lf: bool,
}
#[derive(Debug, Clone, clap::Subcommand)]
pub enum ClientCommand {
Scan,
ListPlayers,
NowPlaying(NowPlayingOpts),
Raise(PlayerOpts),
Next(PlayerOpts),
Previous(PlayerOpts),
Pause(PlayerOpts),
PlayPause(PlayerOpts),
Stop(PlayerOpts),
Play(PlayerOpts),
Seek {
#[command(flatten)]
player: PlayerOpts,
to: Offset,
},
Volume {
#[command(flatten)]
player: PlayerOpts,
vol: Offset,
},
SwitchCurrent {
to: String,
#[arg(short, long)]
no_play: bool,
},
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum PlaybackStatus {
Playing,
Paused,
Stopped,
}
#[derive(Debug, Default, Clone, clap::Args)]
pub struct PlayerOpts {
#[arg(short, long, conflicts_with("ibus"))]
bus: Option<String>,
#[arg(short, long, conflicts_with("bus"))]
ibus: Option<String>,
#[arg(long, use_value_delimiter(true))]
state: Vec<PlaybackStatus>,
}
#[derive(Debug, Clone, Copy)]
pub enum Offset {
Relative(f64),
Absolute(f64),
}
impl Offset {
fn offset(&self) -> f64 {
*match self {
Self::Relative(o) | Self::Absolute(o) => o,
}
}
}
impl fmt::Display for Offset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let offs = self.offset();
assert!(offs.is_sign_positive());
write!(f, "{offs}")?;
if matches!(self, Self::Relative(..)) {
f.write_str(if offs.is_sign_negative() { "-" } else { "+" })?;
}
Ok(())
}
}