feat: add man pages and shell completions

Auto-generated with clap.
This commit is contained in:
Donovan Glover 2024-07-30 14:01:49 -04:00
parent e1d55c6972
commit a6b1254248
No known key found for this signature in database
GPG Key ID: EA7408A77AE1BE65
7 changed files with 271 additions and 27 deletions

27
Cargo.lock generated
View File

@ -476,6 +476,15 @@ dependencies = [
"strsim 0.11.0",
]
[[package]]
name = "clap_complete"
version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.8"
@ -494,6 +503,16 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "clap_mangen"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17415fd4dfbea46e3274fcd8d368284519b358654772afb700dc2e8d2b24eeb"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "color-eyre"
version = "0.6.3"
@ -1610,6 +1629,8 @@ dependencies = [
"cfg-if",
"chrono",
"clap",
"clap_complete",
"clap_mangen",
"color-eyre",
"ctrlc",
"dirs",
@ -2576,6 +2597,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "roff"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
[[package]]
name = "ron"
version = "0.8.1"

View File

@ -162,3 +162,9 @@ zbus = { version = "3.15.2", default-features = false, features = ["tokio"], opt
# schema
schemars = { version = "0.8.21", optional = true }
[build-dependencies]
clap = { version = "4.5.9", features = ["derive"] }
clap_complete = "4.5.2"
clap_mangen = "0.2.20"
serde = { version = "1.0.204", features = ["derive"] }

69
build.rs Normal file
View File

@ -0,0 +1,69 @@
#[path = "src/cli.rs"]
mod cli;
use clap::Command;
use clap::CommandFactory;
use clap_complete::generate_to;
use clap_complete::Shell::{Bash, Fish, Zsh};
use clap_mangen::Man;
use cli::Args;
use std::fs;
use std::path::PathBuf;
static NAME: &str = "ironbar";
fn generate_man_pages(cmd: Command) {
let man_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/man");
let mut buffer = Vec::default();
Man::new(cmd.clone()).render(&mut buffer).unwrap();
fs::create_dir_all(&man_dir).unwrap();
fs::write(man_dir.join(NAME.to_owned() + ".1"), buffer).unwrap();
for subcommand in cmd.get_subcommands() {
let mut buffer = Vec::default();
Man::new(subcommand.clone()).render(&mut buffer).unwrap();
fs::write(
man_dir.join(NAME.to_owned() + "-" + subcommand.get_name() + ".1"),
buffer,
)
.unwrap();
for subsubcommand in subcommand.get_subcommands() {
let mut buffer = Vec::default();
Man::new(subsubcommand.clone()).render(&mut buffer).unwrap();
fs::write(
man_dir.join(
NAME.to_owned()
+ "-"
+ subcommand.get_name()
+ "-"
+ subsubcommand.get_name()
+ ".1",
),
buffer,
)
.unwrap();
}
}
}
fn generate_shell_completions(mut cmd: Command) {
let comp_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/completions");
fs::create_dir_all(&comp_dir).unwrap();
for shell in [Bash, Fish, Zsh] {
generate_to(shell, &mut cmd, NAME, &comp_dir).unwrap();
}
}
fn main() {
let mut cmd = Args::command();
cmd.set_bin_name(NAME);
generate_man_pages(cmd.clone());
generate_shell_completions(cmd);
}

View File

@ -17,6 +17,7 @@
luajit,
luajitPackages,
pkg-config,
installShellFiles,
hicolor-icon-theme,
rustPlatform,
lib,
@ -41,6 +42,7 @@
pkg-config
wrapGAppsHook
gobject-introspection
installShellFiles
];
buildInputs = [
@ -83,6 +85,15 @@
)
'';
postInstall = ''
installManPage target/man/*
installShellCompletion --cmd ironbar \
--bash target/completions/ironbar.bash \
--fish target/completions/ironbar.fish \
--zsh target/completions/_ironbar
'';
passthru = {
updateScript = gnome.updateScript {
packageName = pname;

View File

@ -1,9 +1,134 @@
use crate::error::ExitCode;
use crate::ipc::commands::Command;
use crate::ipc::responses::Response;
use clap::{Parser, ValueEnum};
use clap::ArgAction;
use clap::{Args as ClapArgs, Parser, Subcommand, ValueEnum};
use serde::{Deserialize, Serialize};
use std::process::exit;
use std::path::PathBuf;
#[derive(Subcommand, Debug, Serialize, Deserialize)]
#[serde(tag = "command", rename_all = "snake_case")]
pub enum Command {
/// Pong
Ping,
/// Open the GTK inspector.
Inspect,
/// Reload the config.
Reload,
/// Load an additional CSS stylesheet.
/// The sheet is automatically hot-reloaded.
LoadCss {
/// The path to the sheet.
path: PathBuf,
},
/// Get and set reactive Ironvar values.
#[command(subcommand)]
Var(IronvarCommand),
/// Interact with a specific bar.
Bar(BarCommand),
}
#[derive(Subcommand, Debug, Serialize, Deserialize)]
#[serde(tag = "subcommand", rename_all = "snake_case")]
pub enum IronvarCommand {
/// Set an `ironvar` value.
/// This creates it if it does not already exist, and updates it if it does.
/// Any references to this variable are automatically and immediately updated.
/// Keys and values can be any valid UTF-8 string.
Set {
/// Variable key. Can be any alphanumeric ASCII string.
key: Box<str>,
/// Variable value. Can be any valid UTF-8 string.
value: String,
},
/// Get the current value of an `ironvar`.
Get {
/// Variable key.
key: Box<str>,
},
/// Gets the current value of all `ironvar`s.
List,
}
#[derive(ClapArgs, Debug, Serialize, Deserialize)]
pub struct BarCommand {
/// The name of the bar.
pub name: String,
#[command(subcommand)]
#[serde(flatten)]
pub subcommand: BarCommandType,
}
#[derive(Subcommand, Debug, Serialize, Deserialize)]
#[serde(tag = "subcommand", rename_all = "snake_case")]
pub enum BarCommandType {
// == Visibility == \\
/// Force the bar to be shown, regardless of current visibility state.
Show,
/// Force the bar to be hidden, regardless of current visibility state.
Hide,
/// Set the bar's visibility state via an argument.
SetVisible {
/// The new visibility state.
#[clap(
num_args(1),
require_equals(true),
action = ArgAction::Set,
)]
visible: bool,
},
/// Toggle the current visibility state between shown and hidden.
ToggleVisible,
/// Get the bar's visibility state.
GetVisible,
// == Popup visibility == \\
/// Open a popup, regardless of current state.
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
ShowPopup {
/// The configured name of the widget.
widget_name: String,
},
/// Close a popup, regardless of current state.
HidePopup,
/// Set the popup's visibility state via an argument.
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
SetPopupVisible {
/// The configured name of the widget.
widget_name: String,
#[clap(
num_args(1),
require_equals(true),
action = ArgAction::Set,
)]
visible: bool,
},
/// Toggle a popup open/closed.
/// If opening this popup, and a different popup on the same bar is already open, the other is closed.
TogglePopup {
/// The configured name of the widget.
widget_name: String,
},
/// Get the popup's current visibility state.
GetPopupVisible,
// == Exclusivity == \\
/// Set whether the bar reserves an exclusive zone.
SetExclusive {
#[clap(
num_args(1),
require_equals(true),
action = ArgAction::Set,
)]
exclusive: bool,
},
}
#[derive(Parser, Debug, Serialize, Deserialize)]
#[command(version)]
@ -38,23 +163,3 @@ pub enum Format {
Plain,
Json,
}
pub fn handle_response(response: Response, format: Format) {
let is_err = matches!(response, Response::Err { .. });
match format {
Format::Plain => match response {
Response::Ok => println!("ok"),
Response::OkValue { value } => println!("{value}"),
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
},
Format::Json => println!(
"{}",
serde_json::to_string(&response).expect("to be valid json")
),
}
if is_err {
exit(ExitCode::IpcResponseError as i32)
}
}

View File

@ -1,5 +1,6 @@
use super::Ipc;
use crate::ipc::{Command, Response};
use crate::cli::Command;
use crate::ipc::Response;
use color_eyre::Result;
use color_eyre::{Help, Report};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

View File

@ -28,11 +28,15 @@ use tracing::{debug, error, info, warn};
use universal_config::ConfigLoader;
use crate::bar::{create_bar, Bar};
#[cfg(feature = "cli")]
use crate::cli::Format;
use crate::clients::wayland::OutputEventType;
use crate::clients::Clients;
use crate::config::{Config, MonitorConfig};
use crate::error::ExitCode;
#[cfg(feature = "ipc")]
use crate::ipc::responses::Response;
#[cfg(feature = "ipc")]
use crate::ironvar::VariableManager;
use crate::style::load_css;
@ -98,7 +102,7 @@ fn run_with_args() {
eprintln!("RESPONSE: {res:?}")
}
cli::handle_response(res, args.format.unwrap_or_default())
handle_response(res, args.format.unwrap_or_default())
}
Err(err) => error!("{err:?}"),
};
@ -108,6 +112,27 @@ fn run_with_args() {
}
}
#[cfg(feature = "cli")]
pub fn handle_response(response: Response, format: Format) {
let is_err = matches!(response, Response::Err { .. });
match format {
Format::Plain => match response {
Response::Ok => println!("ok"),
Response::OkValue { value } => println!("{value}"),
Response::Err { message } => eprintln!("error\n{}", message.unwrap_or_default()),
},
Format::Json => println!(
"{}",
serde_json::to_string(&response).expect("to be valid json")
),
}
if is_err {
exit(ExitCode::IpcResponseError as i32)
}
}
#[derive(Debug)]
pub struct Ironbar {
bars: Rc<RefCell<Vec<Bar>>>,