Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fully configurable plz menu #110

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,22 @@ halp [OPTIONS] plz <CMD>

```
Options:
-m, --man-cmd <MAN_CMD> Sets the manual page command to run
--cheat-sh-url <URL> Use a custom URL for cheat.sh [env: CHEAT_SH_URL=]
-p, --pager <PAGER> Sets the pager to use
--no-pager Disables the pager
-h, --help Print help
-p, --pager <PAGER>
Sets the pager to use

--no-pager
Disables the pager

-s, --selected-position <SELECTED_POSITION>
Sets the default selected position

Possible values:
- start: The first item in the menu
- center: The middle item in the menu (in case of even number of items, the first item in the second half)
- end: The last item in the menu

-h, --help
Print help (see a summary with '-h')
```

## Examples
Expand Down Expand Up @@ -313,12 +324,6 @@ To disable the pager:
halp plz --no-pager bat vim
```

##### Custom cheat.sh host URL

```sh
halp plz --cheat-sh-url https://cht.sh vim
```

## Configuration

`halp` can be configured with a configuration file that uses the [TOML](https://en.wikipedia.org/wiki/INI_file) format. It can be specified via `--config` or `HALP_CONFIG` environment variable. It can also be placed in one of the following global locations:
Expand All @@ -330,7 +335,7 @@ halp plz --cheat-sh-url https://cht.sh vim
`<config_dir>` depends on the platform as shown in the following table:

| Platform | Value | Example |
| -------- | ------------------------------------- | ---------------------------------------- |
|----------|---------------------------------------|------------------------------------------|
| Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/orhun/.config |
| macOS | `$HOME`/Library/Application Support | /Users/Orhun/Library/Application Support |
| Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Orhun\AppData\Roaming |
Expand Down
39 changes: 29 additions & 10 deletions config/halp.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
# configuration for https://github.com/orhun/halp

# check the version flag
check_version = true
# check the help flag
check_help = true
# arguments to check
check = [ [ "-v", "-V", "--version" ], [ "-h", "--help", "help", "-H" ] ]
# command to run for manual pages
check = [["-v", "-V", "--version", "version"], ["-h", "--help", "help", "-H"]]
man_command = "man"
# pager to use for command outputs
pager_command = "less -R"
# Cheat.sh URL
cheat_sh_url = "https://cheat.sh"

[plz_menu]
selected_pos = "Center"

[[plz_menu.entries]]
display_msg = "Show man page"

[plz_menu.entries.operation]
run = "man {cmd}"

[[plz_menu.entries]]
display_msg = "Show cheat.sh page"

[plz_menu.entries.operation]
user-agent = "fetch"
fetch = "https://cheat.sh/{cmd}{?/{subcommand}}{? {args}}"

[[plz_menu.entries]]
display_msg = "Show eg page"

[plz_menu.entries.operation]
fetch = "https://raw.githubusercontent.com/srsudar/eg/master/eg/examples/{cmd}.md"

[[plz_menu.entries]]
display_msg = "Show cheatsheets page"

[plz_menu.entries.operation]
fetch = "https://raw.githubusercontent.com/cheat/cheatsheets/master/{cmd}"
37 changes: 9 additions & 28 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config::plz_menu::PlzMenuSelection;
use crate::config::Config;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
Expand Down Expand Up @@ -45,24 +46,15 @@ pub enum CliCommands {
Plz {
/// Command or binary name.
cmd: String,
/// Sets the manual page command to run.
#[arg(short, long)]
man_cmd: Option<String>,
/// Use a custom URL for cheat.sh.
#[arg(long, env = "CHEAT_SH_URL", value_name = "URL")]
cheat_sh_url: Option<String>,
/// Use a custom provider URL for `eg` pages.
#[arg(long, env = "EG_PAGES_URL", value_name = "URL")]
eg_url: Option<String>,
/// Use a custom URL for cheat sheets.
#[arg(long, env = "CHEATSHEETS_URL", value_name = "URL")]
cheat_url: Option<String>,
/// Sets the pager to use.
#[arg(short, long)]
pager: Option<String>,
/// Disables the pager.
#[arg(long)]
no_pager: bool,
/// Sets the default selected position.
#[arg(long, short)]
selected_position: Option<PlzMenuSelection>,
},
}

Expand All @@ -80,28 +72,20 @@ impl CliArgs {
config.check_args = Some(args.iter().map(|s| vec![s.to_string()]).collect());
}
if let Some(CliCommands::Plz {
ref man_cmd,
ref cheat_sh_url,
ref eg_url,
no_pager,
ref pager,
ref selected_position,
..
}) = self.subcommand
{
if let Some(man_cmd) = man_cmd {
config.man_command = man_cmd.clone();
}
if let Some(cheat_sh_url) = cheat_sh_url {
config.cheat_sh_url = Some(cheat_sh_url.clone());
}
if let Some(eg_url) = eg_url {
config.eg_url = Some(eg_url.to_owned());
}
if no_pager {
config.pager_command = None;
} else if let Some(pager) = pager {
config.pager_command = Some(pager.clone());
}
if let Some(selected_position) = selected_position {
config.plz_menu.selected_pos = *selected_position;
}
}
}
}
Expand All @@ -125,10 +109,7 @@ mod tests {
subcommand: Some(CliCommands::Plz {
cmd: "ps".to_string(),
pager: Some("bat".to_string()),
cheat_sh_url: None,
cheat_url: None,
eg_url: None,
man_cmd: None,
selected_position: Some(PlzMenuSelection::Center),
no_pager: false,
}),
..Default::default()
Expand Down
24 changes: 7 additions & 17 deletions src/config.rs → src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/// The `plz` configuration stuff.
pub mod plz_menu;

use crate::config::plz_menu::PlzMenu;
use crate::error::Result;
use crate::helper::args::common::{HelpArg, VersionArg};
use crate::helper::args::FOUND_EMOTICON;
use crate::helper::docs::cheat_sh::DEFAULT_CHEAT_SHEET_PROVIDER;
use crate::helper::docs::cheatsheets::DEFAULT_CHEATSHEETS_PROVIDER;
use crate::helper::docs::eg::DEFAULT_EG_PAGES_PROVIDER;
use colored::*;
use serde::{Deserialize, Serialize};
use std::env;
Expand All @@ -25,12 +26,8 @@ pub struct Config {
pub man_command: String,
/// Pager to use for command outputs, None to disable.
pub pager_command: Option<String>,
/// Use a custom URL for cheat.sh.
pub cheat_sh_url: Option<String>,
/// Use a custom URL for `eg` pages provider.
pub eg_url: Option<String>,
/// Use a custom URL for cheatsheets provider.
pub cheatsheets_url: Option<String>,
/// Plz menu options.
pub plz_menu: PlzMenu,
}

impl Default for Config {
Expand All @@ -50,9 +47,7 @@ impl Default for Config {
]),
man_command: "man".to_string(),
pager_command: Some("less -R".to_string()),
cheat_sh_url: Some(DEFAULT_CHEAT_SHEET_PROVIDER.to_string()),
eg_url: Some(DEFAULT_EG_PAGES_PROVIDER.to_string()),
cheatsheets_url: Some(DEFAULT_CHEATSHEETS_PROVIDER.to_string()),
plz_menu: PlzMenu::default(),
}
}
}
Expand Down Expand Up @@ -122,7 +117,6 @@ impl Config {
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::path::PathBuf;

#[test]
Expand All @@ -136,10 +130,6 @@ mod tests {
let config = Config::parse(&path)?;
assert!(config.check_help);
assert!(config.check_version);
assert_eq!(
config.cheat_sh_url,
Some(DEFAULT_CHEAT_SHEET_PROVIDER.to_string())
);
Ok(())
}
}
83 changes: 83 additions & 0 deletions src/config/plz_menu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Default cheat sheet provider URL.
const CHEAT_SH_URL_TEMPLATE: &str = "https://cheat.sh/{cmd}{?/{subcommand}}{? {args}}";

/// User agent for the cheat sheet provider.
///
/// See <https://github.com/chubin/cheat.sh/blob/83bffa587b6c1048cbcc40ea8fa6af675203fd5f/bin/app.py#L76>
const CHEAT_SH_USER_AGENT: &str = "fetch";

/// EG page provider URL.
const EG_PAGES_URL_TEMPLATE: &str =
"https://raw.githubusercontent.com/srsudar/eg/master/eg/examples/{cmd}.md";

/// The default cheatsheets provider URL.
const CHEATSHEETS_URL_TEMPLATE: &str =
"https://raw.githubusercontent.com/cheat/cheatsheets/master/{cmd}";

/// Plz menu config.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlzMenu {
/// The default selected poison in the menu.
pub selected_pos: PlzMenuSelection,
/// The menu entries.
pub entries: Vec<PlzMenuEntry>,
}

/// Plz menu selection position.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ValueEnum)]
pub enum PlzMenuSelection {
/// The first item in the menu.
Start,
/// The middle item in the menu (in case of even number of items, the first item in the second half).
Center,
/// The last item in the menu.
End,
}

/// Plz menu item.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlzMenuEntry {
/// The string to display in the menu.
pub display_msg: String,
/// The operation to perform. and its arguments.
pub operation: HashMap<String, String>,
}

impl Default for PlzMenu {
fn default() -> Self {
PlzMenu {
selected_pos: PlzMenuSelection::Center,
entries: vec![
PlzMenuEntry {
display_msg: "Show man page".to_string(),
operation: HashMap::from([("run".to_string(), "man {cmd}".to_string())]),
},
PlzMenuEntry {
display_msg: "Show cheat.sh page".to_string(),
operation: HashMap::from([
("fetch".to_string(), CHEAT_SH_URL_TEMPLATE.to_string()),
("user-agent".to_string(), CHEAT_SH_USER_AGENT.to_string()),
]),
},
PlzMenuEntry {
display_msg: "Show eg page".to_string(),
operation: HashMap::from([(
"fetch".to_string(),
EG_PAGES_URL_TEMPLATE.to_string(),
)]),
},
PlzMenuEntry {
display_msg: "Show cheatsheets page".to_string(),
operation: HashMap::from([(
"fetch".to_string(),
CHEATSHEETS_URL_TEMPLATE.to_string(),
)]),
},
],
}
}
}
19 changes: 19 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::helper::docs::template::HalpTemplateError;
use thiserror::Error as ThisError;

/// Custom error type.
Expand All @@ -21,6 +22,24 @@ pub enum Error {
/// Error that might occur when tray to get help from an external provider.
#[error("External help provider error: `{0}`")]
ProviderError(String),
/// Error that might occur when trying to parse a template.
#[error("Template error: `{0}`")]
TemplateError(#[from] HalpTemplateError),
/// Error that might occur if the user provides invalid arguments for the operation handler.
#[error("Invalid argument: `{0}`")]
InvalidArgument(String),
/// Error that might occur if the user dosen't provide any operation handler.
#[error("No operation provided.")]
PlzMenuNoOperation,
/// Error that might occur if the user provides an invalid operation.
#[error("Invalid operation there is no operation named `{0}`.")]
PlzMenuInvalidOperation(String),
/// Error that might occur if the timeout is reached while executing a command.
#[error("Command timeout.")]
CommandTimeoutError,
/// Error that might occur when trying to execute a command or collect its output.
#[error("Command error: `{0}`")]
CommandError(String),
}

/// Type alias for the standard [`Result`] type.
Expand Down
8 changes: 4 additions & 4 deletions src/helper/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn check_args<'a, ArgsIter: Iterator<Item = &'a str>, Output: Write>(
/// Shows command-line help about the given command.
pub fn get_args_help<Output: Write>(
cmd: &str,
config: &Config,
config: Config,
verbose: bool,
output: &mut Output,
) -> Result<()> {
Expand Down Expand Up @@ -215,7 +215,7 @@ Options:
fn test_get_default_help() -> Result<()> {
let config = Config::default();
let mut output = Vec::new();
get_args_help(&get_test_bin(), &config, false, &mut output)?;
get_args_help(&get_test_bin(), config, false, &mut output)?;
println!("{}", String::from_utf8_lossy(&output));
assert_eq!(
r"(°ロ°) checking 'test -v'
Expand Down Expand Up @@ -250,7 +250,7 @@ Options:
..Default::default()
};
let mut output = Vec::new();
get_args_help(&get_test_bin(), &config, false, &mut output)?;
get_args_help(&get_test_bin(), config, false, &mut output)?;
println!("{}", String::from_utf8_lossy(&output));
assert_eq!(
r"(°ロ°) checking 'test -x'
Expand All @@ -277,7 +277,7 @@ halp 0.1.0
..Default::default()
};
let mut output = Vec::new();
get_args_help("", &config, false, &mut output)?;
get_args_help("", config, false, &mut output)?;
assert!(String::from_utf8_lossy(&output).is_empty());
Ok(())
}
Expand Down
Loading
Loading