Skip to content

Commit

Permalink
feat: add man pages and shell completions
Browse files Browse the repository at this point in the history
Auto-generated with clap.
  • Loading branch information
donovanglover committed Nov 2, 2024
1 parent c7cf896 commit e329f3f
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 28 deletions.
27 changes: 27 additions & 0 deletions Cargo.lock

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

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,10 @@ schemars = { version = "0.8.21", optional = true }

# -- PATCH --
# temp fix for tracing-appender/time
time = "0.3.36"
time = "0.3.36"

[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 changes: 69 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 11 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
luajit,
luajitPackages,
pkg-config,
installShellFiles,
hicolor-icon-theme,
rustPlatform,
lib,
Expand All @@ -41,6 +42,7 @@
pkg-config
wrapGAppsHook
gobject-introspection
installShellFiles
];

buildInputs = [
Expand Down Expand Up @@ -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;
Expand Down
155 changes: 130 additions & 25 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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)
}
}
3 changes: 2 additions & 1 deletion src/ipc/client.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
27 changes: 26 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,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;

Expand Down Expand Up @@ -100,7 +104,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:?}"),
};
Expand All @@ -110,6 +114,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>>>,
Expand Down

0 comments on commit e329f3f

Please sign in to comment.