Add manpage generation using xtask
* Add manpage generation. * Hide xtask compilation info. This changes the default behavior of the `cargo run` command for xtasks to hide the compilation info. This makes sense as xtask's are run as a program, and showing the compilation info every time clutters stdout with unrelated info. * Move ncspot's `clap::Command` to `lib.rs`. Moving the `clap::Command` used internally by ncspot to a library allows it to be easily shared between different packages (xtask and ncspot itself). This commit also reworks the xtasks to use clap for parsing the xtask arguments, which simplifies writing new xtasks. * Make `generate-manpage` `--output` optional. When more xtasks get added, it would make sense to have a `generate-all` subcommand that executes all xtasks, which wouldn't be able to have options for every separate subcommand. Therefore the `output` argument should be optional, and by default output to the `misc` directory which contains extra metadata files already. * Add packaging info to `README.md`. Update the README to include information for packagers. The information includes provided files as well as info on how to generate some of them.
This commit is contained in:
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[alias]
|
||||||
|
xtask = "run --quiet --package xtask --"
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -10,4 +10,7 @@
|
|||||||
|
|
||||||
tags
|
tags
|
||||||
|
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
|
||||||
|
# Ignore generated manpages
|
||||||
|
*.1
|
||||||
|
|||||||
25
Cargo.lock
generated
25
Cargo.lock
generated
@@ -319,6 +319,16 @@ dependencies = [
|
|||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_mangen"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48283ce8d5cd9513633949a674a0442bcb507ab61ed6533863437052ddbb494b"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"roff",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard"
|
name = "clipboard"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -2593,6 +2603,12 @@ dependencies = [
|
|||||||
"cpal",
|
"cpal",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roff"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rspotify"
|
name = "rspotify"
|
||||||
version = "0.11.6"
|
version = "0.11.6"
|
||||||
@@ -3856,6 +3872,15 @@ version = "0.8.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"clap_mangen",
|
||||||
|
"ncspot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ version = "0.12.0"
|
|||||||
[badges]
|
[badges]
|
||||||
maintenance = {status = "actively-developed"}
|
maintenance = {status = "actively-developed"}
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
".",
|
||||||
|
"xtask"
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
clap = "4.1.7"
|
clap = "4.1.7"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ You **must** have an existing premium Spotify subscription to use `ncspot`.
|
|||||||
- [Debugging](#debugging)
|
- [Debugging](#debugging)
|
||||||
- [Compiling](#compiling)
|
- [Compiling](#compiling)
|
||||||
- [Building a Debian Package](#building-a-debian-package)
|
- [Building a Debian Package](#building-a-debian-package)
|
||||||
|
- [Packaging Information](#packaging-information)
|
||||||
- [Audio Backends](#audio-backends)
|
- [Audio Backends](#audio-backends)
|
||||||
- [Other Features](#other-features)
|
- [Other Features](#other-features)
|
||||||
- [Key Bindings](#key-bindings)
|
- [Key Bindings](#key-bindings)
|
||||||
@@ -170,6 +171,14 @@ cargo deb
|
|||||||
|
|
||||||
You can find the package under `target/debian`.
|
You can find the package under `target/debian`.
|
||||||
|
|
||||||
|
#### Packaging Information
|
||||||
|
The following files are provided and should be bundled together with ncspot:
|
||||||
|
- LICENSE
|
||||||
|
- images/logo.svg (optional)
|
||||||
|
- misc/ncspot.desktop (for Linux systems)
|
||||||
|
- misc/ncspot.1 (for Linux systems)
|
||||||
|
|
||||||
|
Some of these files have to be generated. Execute `cargo xtask --help` for more information.
|
||||||
### Audio Backends
|
### Audio Backends
|
||||||
|
|
||||||
By default `ncspot` is built using the PulseAudio backend. To make it use the
|
By default `ncspot` is built using the PulseAudio backend. To make it use the
|
||||||
|
|||||||
41
src/lib.rs
Normal file
41
src/lib.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use librespot_playback::audio_backend;
|
||||||
|
|
||||||
|
pub const AUTHOR: &str = "Henrik Friedrichsen <henrik@affekt.org> and contributors";
|
||||||
|
|
||||||
|
/// Return the [Command](clap::Command) that models the program's command line arguments. The
|
||||||
|
/// command can be used to parse the actual arguments passed to the program, or to automatically
|
||||||
|
/// generate a man page using clap's mangen package.
|
||||||
|
pub fn program_arguments() -> clap::Command {
|
||||||
|
let backends = {
|
||||||
|
let backends: Vec<&str> = audio_backend::BACKENDS.iter().map(|b| b.0).collect();
|
||||||
|
format!("Audio backends: {}", backends.join(", "))
|
||||||
|
};
|
||||||
|
|
||||||
|
clap::Command::new("ncspot")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.author(AUTHOR)
|
||||||
|
.about("cross-platform ncurses Spotify client")
|
||||||
|
.after_help(backends)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::new("debug")
|
||||||
|
.short('d')
|
||||||
|
.long("debug")
|
||||||
|
.value_name("FILE")
|
||||||
|
.help("Enable debug logging to the specified file"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::new("basepath")
|
||||||
|
.short('b')
|
||||||
|
.long("basepath")
|
||||||
|
.value_name("PATH")
|
||||||
|
.help("custom basepath to config/cache files"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::new("config")
|
||||||
|
.short('c')
|
||||||
|
.long("config")
|
||||||
|
.value_name("FILE")
|
||||||
|
.help("Filename of config file in basepath")
|
||||||
|
.default_value("config.toml"),
|
||||||
|
)
|
||||||
|
}
|
||||||
36
src/main.rs
36
src/main.rs
@@ -12,14 +12,13 @@ use std::path::PathBuf;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use clap::{Arg, Command as ClapCommand};
|
|
||||||
use cursive::event::EventTrigger;
|
use cursive::event::EventTrigger;
|
||||||
use cursive::traits::Nameable;
|
use cursive::traits::Nameable;
|
||||||
use librespot_core::authentication::Credentials;
|
use librespot_core::authentication::Credentials;
|
||||||
use librespot_core::cache::Cache;
|
use librespot_core::cache::Cache;
|
||||||
use librespot_playback::audio_backend;
|
|
||||||
use log::{error, info, trace};
|
use log::{error, info, trace};
|
||||||
|
|
||||||
|
use ncspot::program_arguments;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use signal_hook::{consts::SIGHUP, consts::SIGTERM, iterator::Signals};
|
use signal_hook::{consts::SIGHUP, consts::SIGTERM, iterator::Signals};
|
||||||
|
|
||||||
@@ -130,38 +129,7 @@ lazy_static!(
|
|||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
register_backtrace_panic_handler();
|
register_backtrace_panic_handler();
|
||||||
|
|
||||||
let backends = {
|
let matches = program_arguments().get_matches();
|
||||||
let backends: Vec<&str> = audio_backend::BACKENDS.iter().map(|b| b.0).collect();
|
|
||||||
format!("Audio backends: {}", backends.join(", "))
|
|
||||||
};
|
|
||||||
let matches = ClapCommand::new("ncspot")
|
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
|
||||||
.author("Henrik Friedrichsen <henrik@affekt.org> and contributors")
|
|
||||||
.about("cross-platform ncurses Spotify client")
|
|
||||||
.after_help(backends)
|
|
||||||
.arg(
|
|
||||||
Arg::new("debug")
|
|
||||||
.short('d')
|
|
||||||
.long("debug")
|
|
||||||
.value_name("FILE")
|
|
||||||
.help("Enable debug logging to the specified file"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("basepath")
|
|
||||||
.short('b')
|
|
||||||
.long("basepath")
|
|
||||||
.value_name("PATH")
|
|
||||||
.help("custom basepath to config/cache files"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("config")
|
|
||||||
.short('c')
|
|
||||||
.long("config")
|
|
||||||
.value_name("FILE")
|
|
||||||
.help("Filename of config file in basepath")
|
|
||||||
.default_value("config.toml"),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
if let Some(filename) = matches.get_one::<String>("debug") {
|
if let Some(filename) = matches.get_one::<String>("debug") {
|
||||||
setup_logging(filename).expect("can't setup logging");
|
setup_logging(filename).expect("can't setup logging");
|
||||||
|
|||||||
13
xtask/Cargo.toml
Normal file
13
xtask/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap_mangen = "0.2.8"
|
||||||
|
clap = "4.1.6"
|
||||||
|
|
||||||
|
[dependencies.ncspot]
|
||||||
|
path = ".."
|
||||||
92
xtask/src/main.rs
Normal file
92
xtask/src/main.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use clap::builder::PathBufValueParser;
|
||||||
|
use clap::error::{Error, ErrorKind};
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use ncspot::AUTHOR;
|
||||||
|
|
||||||
|
enum XTaskSubcommand {
|
||||||
|
GenerateManpage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&ArgMatches> for XTaskSubcommand {
|
||||||
|
type Error = clap::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &ArgMatches) -> Result<Self, Self::Error> {
|
||||||
|
if let Some(subcommand) = value.subcommand() {
|
||||||
|
match subcommand.0 {
|
||||||
|
"generate-manpage" => Ok(XTaskSubcommand::GenerateManpage),
|
||||||
|
_ => Err(Error::new(clap::error::ErrorKind::InvalidSubcommand)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::new(ErrorKind::MissingSubcommand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynError = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = try_main() {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_main() -> Result<(), DynError> {
|
||||||
|
let arguments_model = clap::Command::new("cargo xtask")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.author(AUTHOR)
|
||||||
|
.about("Automation using the cargo xtask convention.")
|
||||||
|
.arg_required_else_help(true)
|
||||||
|
.bin_name("cargo xtask")
|
||||||
|
.disable_version_flag(true)
|
||||||
|
.long_about(
|
||||||
|
"
|
||||||
|
Cargo xtask is a convention that allows easy integration of third party commands into the regular
|
||||||
|
cargo workflox. Xtask's are defined as a separate package and can be used for all kinds of
|
||||||
|
automation.
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
clap::Command::new("generate-manpage")
|
||||||
|
.visible_alias("gm")
|
||||||
|
.args([clap::Arg::new("output")
|
||||||
|
.short('o')
|
||||||
|
.long("output")
|
||||||
|
.value_name("PATH")
|
||||||
|
.help("Output directory for the generated man page.")
|
||||||
|
.value_parser(PathBufValueParser::new())])
|
||||||
|
.about("Automatic man page generation."),
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_parsed_arguments = arguments_model.get_matches();
|
||||||
|
|
||||||
|
let parsed_subcommand = XTaskSubcommand::try_from(&program_parsed_arguments)?;
|
||||||
|
|
||||||
|
match parsed_subcommand {
|
||||||
|
XTaskSubcommand::GenerateManpage => {
|
||||||
|
generate_manpage(program_parsed_arguments.subcommand().unwrap().1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_manpage(subcommand_arguments: &ArgMatches) -> Result<(), DynError> {
|
||||||
|
let output_directory =
|
||||||
|
if let Some(output_argument) = subcommand_arguments.get_one::<PathBuf>("output") {
|
||||||
|
output_argument.clone()
|
||||||
|
} else {
|
||||||
|
fs::create_dir_all("misc")?;
|
||||||
|
PathBuf::from("misc")
|
||||||
|
};
|
||||||
|
let cmd = ncspot::program_arguments();
|
||||||
|
let man = clap_mangen::Man::new(cmd);
|
||||||
|
let mut buffer: Vec<u8> = Default::default();
|
||||||
|
|
||||||
|
man.render(&mut buffer)?;
|
||||||
|
|
||||||
|
std::fs::write(output_directory.join("ncspot.1"), buffer)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user