Skip to content

Commit

Permalink
Use clap-derive to simplify commandline arguments parsing.
Browse files Browse the repository at this point in the history
This patch is intended to use clap-derive to parse commandline
arguments. This helps improve code readability and make it more
extensible.
  • Loading branch information
higuoxing authored and beeender committed Jan 9, 2025
1 parent fd6b146 commit 044d1cc
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 101 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build = "build.rs"

[dependencies]
anyhow = "1.0.94"
clap = "4.5.23"
clap = { version = "4.5.23", features = ["derive"] }
daemonize = "0.5.0"
log = "0.4.22"
wayrs-client = "1.1.3"
Expand Down
163 changes: 63 additions & 100 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod clipboard;
mod protocol;

use anyhow::{Context, Result};
use clap::{value_parser, Arg, ArgMatches, Command};
use clap::{Parser, Subcommand};
use daemonize::Daemonize;
use std::env;
use std::fs::File;
Expand All @@ -32,73 +32,47 @@ fn choose_backend() -> Backend {
std::process::exit(1)
}

fn cli() -> Command {
Command::new("richclip")
.about("A fictional versioning CLI")
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
Command::new("copy")
.about("Receive and copy data to the clipboard")
.arg(
Arg::new("primary")
.long("primary")
.short('p')
.required(false)
.num_args(0)
.help("Use the 'primary' clipboard")
)
.arg(
Arg::new("foreground")
.long("foreground")
.required(false)
.num_args(0)
.help("Run in foreground")
)
.arg(
Arg::new("chunk-size")
.long("chunk-size")
.value_parser(value_parser!(usize))
.default_value("0")
.required(false)
.hide(true)
.num_args(1)
.help("For testing X INCR mode")
)
)
.subcommand(
Command::new("paste")
.about("Paste the data from clipboard to the output")
.arg(
Arg::new("list-types")
.long("list-types")
.short('l')
.required(false)
.num_args(0)
.help("List the offered mime-types of the current clipboard only without the contents")
)
.arg(
Arg::new("type")
.long("type")
.short('t')
.value_name("mime-type")
.required(false)
.num_args(1)
.help("Specify the preferred mime-type to be pasted")
)
.arg(
Arg::new("primary")
.long("primary")
.short('p')
.required(false)
.num_args(0)
.help("Use the 'primary' clipboard")
),
)
.subcommand(
Command::new("version")
.about("Print version info")
)
/// A fictional versioning CLI
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Receive and copy data to the clipboard
Copy {
/// Use the 'primary' clipboard
#[arg(long = "primary", short = 'p', num_args = 0)]
primary: bool,
/// Run in foreground
#[arg(long = "foreground", num_args = 0)]
foreground: bool,
/// For testing X INCR mode
#[arg(
long = "chunk-size",
hide = true,
required = false,
num_args = 1,
default_value = "0"
)]
chunk_size: usize,
},
/// Paste the data from clipboard to the output
Paste {
/// List the offered mime-types of the current clipboard only without the contents
#[arg(long = "list-types", short = 'l', num_args = 0)]
list_types: bool,
/// Specify the preferred mime-type to be pasted
#[arg(long = "type", short = 't', value_name = "mime-type", num_args = 1)]
type_: Option<String>,
/// Use the 'primary' clipboard
#[arg(long = "primary", short = 'p', num_args = 0)]
primary: bool,
},
/// Print version info
Version,
}

fn init_logger() -> Result<()> {
Expand Down Expand Up @@ -135,33 +109,34 @@ fn init_logger() -> Result<()> {
fn main() -> Result<()> {
init_logger()?;

let matches = cli().get_matches();
match matches.subcommand() {
Some(("copy", sub_matches)) => {
do_copy(sub_matches)?;
}
Some(("paste", sub_matches)) => {
do_paste(sub_matches)?;
}
Some(("version", _)) => {
let cli = Cli::parse();

match cli.command {
Commands::Copy {
primary,
foreground,
chunk_size,
} => do_copy(primary, foreground, chunk_size)?,
Commands::Paste {
list_types,
type_,
primary,
} => do_paste(&type_.unwrap_or("".to_string()), list_types, primary)?,
Commands::Version => {
let ver = env!("CARGO_PKG_VERSION");
let git_desc = env!("VERGEN_GIT_DESCRIBE");
let build_date = env!("VERGEN_BUILD_DATE");
let target = env!("VERGEN_CARGO_TARGET_TRIPLE");
println!("richclip {ver} ({git_desc} {target} {build_date})");
}
_ => unreachable!(),
}

Ok(())
}

fn do_copy(arg_matches: &ArgMatches) -> Result<()> {
fn do_copy(primary: bool, foreground: bool, chunk_size: usize) -> Result<()> {
let stdin = stdin();
let source_data = protocol::receive_data(&stdin).context("Failed to read data from stdin")?;
let foreground = *arg_matches
.get_one::<bool>("foreground")
.context("`--foreground` option is not specified for the `copy` command")?;

// Move to background. We fork our process and leave the child running in the background, while
// exiting in the parent. We also replace stdin/stdout with /dev/null so the stdout file
Expand All @@ -184,12 +159,8 @@ fn do_copy(arg_matches: &ArgMatches) -> Result<()> {

let copy_config = clipboard::CopyConfig {
source_data,
use_primary: *arg_matches
.get_one::<bool>("primary")
.context("`--primary` option is not specified for the `copy` command")?,
x_chunk_size: *arg_matches
.get_one::<usize>("chunk-size")
.context("`--chunk-size` option is not specified for the `copy` command")?,
use_primary: primary,
x_chunk_size: chunk_size,
};
match choose_backend() {
Backend::Wayland => {
Expand All @@ -199,20 +170,12 @@ fn do_copy(arg_matches: &ArgMatches) -> Result<()> {
}
}

fn do_paste(arg_matches: &ArgMatches) -> Result<()> {
let t = match arg_matches.get_one::<String>("type") {
Some(t) => t,
_ => "",
};
fn do_paste(mime_type: &str, list_types: bool, primary: bool) -> Result<()> {
let cfg = clipboard::PasteConfig {
list_types_only: *arg_matches
.get_one::<bool>("list-types")
.context("`--list-types` option is not specified for the `paste` command")?,
use_primary: *arg_matches
.get_one::<bool>("primary")
.context("`--primary` option is not specified for the `paste` command")?,
list_types_only: list_types,
use_primary: primary,
writter: &mut stdout(),
expected_mime_type: t.to_string(),
expected_mime_type: mime_type.to_string(),
};
match choose_backend() {
Backend::Wayland => {
Expand Down

0 comments on commit 044d1cc

Please sign in to comment.