use crate::{
Colors, Orientation, SortCriteria, WallSwitchResult, get_config_path, read_config_file,
};
use clap::{
CommandFactory, Parser,
builder::styling::{AnsiColor, Effects, Styles},
}; use clap_complete::{Generator, Shell, generate};
fn get_styles() -> Styles {
Styles::styled()
.header(AnsiColor::Yellow.on_default() | Effects::BOLD)
.usage(AnsiColor::Yellow.on_default() | Effects::BOLD)
.literal(AnsiColor::Green.on_default())
.placeholder(AnsiColor::Cyan.on_default())
}
fn get_after_help() -> String {
let config_path = get_config_path()
.map(|p| p.display().to_string())
.unwrap_or_else(|_| "~/.config/wallswitch/wallswitch.json".to_string());
let mut help_text = format!(
"{}\n {}\n\n{}\n",
"Config file:".yellow().bold(),
config_path.blue().bold(),
"Examples:".yellow().bold()
);
let examples = [
(
"# Run a single wallpaper update cycle and exit (useful for cron jobs)",
"wallswitch --once",
),
(
"# Change wallpaper every 10 minutes (600 seconds)",
"wallswitch --interval 600",
),
(
"# Set 3 different wallpapers per monitor (Gnome desktop only)",
"wallswitch --pictures_per_monitor 3",
),
(
"# Filter images by dimension (min 1080px) and file size (max 5MB)",
"wallswitch --min_dimension 1080 --max_size 5242880",
),
(
"# Dry run mode to see what would be executed without applying changes",
"wallswitch --dry-run --verbose",
),
(
"# Wayland (awww): Use specific transition effects and duration",
"wallswitch --transition-type wave --transition-duration 3",
),
(
"# List all found images sorted by file size",
"wallswitch --list size",
),
(
"# Display all processed images (with dimensions) in JSON format",
"wallswitch --list processed",
),
(
"# Display all images that haven't been probed yet",
"wallswitch --list unprocessed",
),
(
"# Count processed images using jq",
"wallswitch -l processed | jq 'length'",
),
];
for (comment, cmd) in examples {
help_text.push_str(&format!(
" {}\n {}\n\n",
comment.dimmed(),
cmd.green().bold()
));
}
help_text.trim_end().to_string()
}
const APPLET_TEMPLATE: &str = "\
{before-help}
{about}
{usage-heading} {usage}
{all-args}
{after-help}";
#[derive(Parser, Debug, Clone)]
#[command(
// Read from `Cargo.toml`
author, version, about,
long_about = None,
next_line_help = true,
help_template = APPLET_TEMPLATE,
styles = get_styles(),
after_help = get_after_help(),
)]
pub struct Arguments {
#[arg(
short('b'), long("min_size"),
required = false,
default_value = None,
hide_default_value = true,
)]
pub min_size: Option<u64>,
#[arg(
short('B'), long("max_size"),
required = false,
default_value = None,
hide_default_value = true
)]
pub max_size: Option<u64>,
#[arg(short('c'), long("config"), default_value_t = false)]
pub config: bool,
#[arg(short('g'), long("generate"), value_enum)]
pub generator: Option<Shell>,
#[arg(
short('d'), long("min_dimension"),
required = false,
default_value = None,
hide_default_value = true,
value_parser = clap::value_parser!(u64).range(10..)
)]
pub min_dimension: Option<u64>,
#[arg(
short('D'), long("max_dimension"),
required = false,
default_value = None,
hide_default_value = true
)]
pub max_dimension: Option<u64>,
#[arg(
short('i'), long("interval"),
required = false,
default_value = None,
hide_default_value = true,
value_parser = clap::value_parser!(u64).range(5..)
)]
pub interval: Option<u64>,
#[arg(short('l'), long("list"), value_name = "CRITERIA")]
pub list: Option<SortCriteria>,
#[arg(
short('m'), long("monitor"),
required = false,
default_value = None,
hide_default_value = true,
value_parser = clap::value_parser!(u8).range(1..)
)]
pub monitor: Option<u8>,
#[arg(
short('o'),
long("orientation"),
required = false,
default_value = None,
hide_default_value = true,
)]
pub monitor_orientation: Option<Orientation>,
#[arg(long, default_value_t = false)]
pub once: bool,
#[arg(
short('p'), long("pictures_per_monitor"),
required = false,
default_value = None,
hide_default_value = true,
value_parser = clap::value_parser!(u8).range(1..=256)
)]
pub pictures_per_monitor: Option<u8>,
#[arg(short('s'), long("sort"), default_value_t = false)]
pub sort: bool,
#[arg(long("dry-run"), default_value_t = false)]
pub dry_run: bool,
#[arg(long("transition-type"), required = false)]
pub transition_type: Option<String>,
#[arg(long("transition-duration"), required = false)]
pub transition_duration: Option<u16>,
#[arg(long("transition-fps"), required = false)]
pub transition_fps: Option<u16>,
#[arg(long("transition-angle"), required = false)]
pub transition_angle: Option<u16>,
#[arg(long("transition-pos"), required = false)]
pub transition_pos: Option<String>,
#[arg(short('v'), long("verbose"), default_value_t = false)]
pub verbose: bool,
}
impl Arguments {
pub fn build() -> WallSwitchResult<Arguments> {
let args: Arguments = Arguments::parse();
if let Some(generator) = args.generator {
args.print_completions(generator);
}
if args.config {
let config_path = get_config_path()?;
let config = read_config_file(&config_path)?;
let json: String = serde_json::to_string_pretty(&config)?;
println!("{json}");
std::process::exit(0);
}
Ok(args)
}
fn print_completions<G>(&self, r#gen: G)
where
G: Generator + std::fmt::Debug,
{
let mut cmd = Arguments::command();
let cmd_name = cmd.get_name().to_string();
let mut stdout = std::io::stdout();
eprintln!("Generating completion file for {gen:?}...");
generate(r#gen, &mut cmd, cmd_name, &mut stdout);
std::process::exit(1);
}
}