use std::{ffi::OsStr, path::Path};
const DEFAULT_BINARY_NAME: &str = "truth-mirror";
pub fn async_review_drain_hint(global_args: &str) -> String {
async_review_drain_hint_for_binary(&active_binary_name(), global_args)
}
pub fn diagnostic_prefix() -> String {
diagnostic_prefix_for_binary(&active_binary_name())
}
pub fn command_for_cli(global_args: &str, subcommand: &str) -> String {
command_for_binary(&active_binary_name(), global_args, subcommand)
}
fn async_review_drain_hint_for_binary(binary_name: &str, global_args: &str) -> String {
format!(
"run `{}` to drain now or `{}` to keep draining",
command_for_binary(binary_name, global_args, "watch --once"),
command_for_binary(binary_name, global_args, "watch")
)
}
pub fn async_review_drain_hint_for_cli(config_path: Option<&Path>, state_dir: &Path) -> String {
async_review_drain_hint_for_cli_with_binary(&active_binary_name(), config_path, state_dir)
}
fn async_review_drain_hint_for_cli_with_binary(
binary_name: &str,
config_path: Option<&Path>,
state_dir: &Path,
) -> String {
let mut args = Vec::new();
if let Some(config_path) = config_path {
args.push(format!("--config {}", quote_path(config_path)));
}
if state_dir != Path::new(crate::config::DEFAULT_STATE_DIR) {
args.push(format!("--state-dir {}", quote_path(state_dir)));
}
let global_args = if args.is_empty() {
String::new()
} else {
format!("{} ", args.join(" "))
};
async_review_drain_hint_for_binary(binary_name, &global_args)
}
fn diagnostic_prefix_for_binary(binary_name: &str) -> String {
format!("{binary_name}:")
}
fn command_for_binary(binary_name: &str, global_args: &str, subcommand: &str) -> String {
let binary = quote_binary_name(binary_name);
let global_args = global_args.trim();
if global_args.is_empty() {
format!("{binary} {subcommand}")
} else {
format!("{binary} {global_args} {subcommand}")
}
}
fn active_binary_name() -> String {
binary_name_from_argv0(std::env::args_os().next().as_deref())
}
fn binary_name_from_argv0(path: Option<&OsStr>) -> String {
path.and_then(|path| {
Path::new(path)
.file_stem()
.and_then(|name| name.to_str())
.map(str::to_owned)
})
.unwrap_or_else(|| DEFAULT_BINARY_NAME.to_owned())
}
fn quote_path(path: &Path) -> String {
let value = path.to_string_lossy();
quote_shell_arg(&value)
}
fn quote_binary_name(binary_name: &str) -> String {
if !binary_name.is_empty()
&& binary_name.chars().all(|character| {
character.is_ascii_alphanumeric() || matches!(character, '-' | '_' | '.' | '/')
})
{
binary_name.to_owned()
} else {
quote_shell_arg(binary_name)
}
}
fn quote_shell_arg(value: &str) -> String {
format!("'{}'", value.replace('\'', "'\\''"))
}
#[cfg(test)]
mod tests {
use std::{ffi::OsStr, path::Path};
use super::{
DEFAULT_BINARY_NAME, async_review_drain_hint_for_binary,
async_review_drain_hint_for_cli_with_binary, binary_name_from_argv0, command_for_binary,
diagnostic_prefix_for_binary,
};
#[test]
fn default_state_dir_uses_bare_watch_commands() {
assert_eq!(
async_review_drain_hint_for_cli_with_binary("truth-mirror", None, Path::new(".truth")),
async_review_drain_hint_for_binary("truth-mirror", "")
);
}
#[test]
fn custom_state_dir_is_quoted_in_watch_commands() {
assert_eq!(
async_review_drain_hint_for_cli_with_binary(
"truth-mirror",
None,
Path::new("/tmp/truth state's queue")
),
"run `truth-mirror --state-dir '/tmp/truth state'\\''s queue' watch --once` to drain now or `truth-mirror --state-dir '/tmp/truth state'\\''s queue' watch` to keep draining"
);
}
#[test]
fn custom_config_is_quoted_in_watch_commands() {
assert_eq!(
async_review_drain_hint_for_cli_with_binary(
"truth-mirror",
Some(Path::new("/tmp/truth config.toml")),
Path::new(".truth")
),
"run `truth-mirror --config '/tmp/truth config.toml' watch --once` to drain now or `truth-mirror --config '/tmp/truth config.toml' watch` to keep draining"
);
}
#[test]
fn binary_name_is_used_in_watch_commands() {
assert_eq!(
async_review_drain_hint_for_binary("truth", ""),
"run `truth watch --once` to drain now or `truth watch` to keep draining"
);
}
#[test]
fn diagnostic_prefix_uses_injected_binary_name() {
assert_eq!(diagnostic_prefix_for_binary("truth"), "truth:");
}
#[test]
fn command_builder_inserts_global_arg_spacing() {
assert_eq!(
command_for_binary("truth", "--state-dir .truth", "install-hooks"),
"truth --state-dir .truth install-hooks"
);
assert_eq!(
command_for_binary("truth", "--state-dir .truth ", "install-hooks"),
"truth --state-dir .truth install-hooks"
);
}
#[test]
fn binary_name_with_shell_specials_is_quoted_in_watch_commands() {
assert_eq!(
async_review_drain_hint_for_binary("truth mirror", ""),
"run `'truth mirror' watch --once` to drain now or `'truth mirror' watch` to keep draining"
);
}
#[test]
fn binary_name_from_argv0_uses_file_stem_or_default() {
assert_eq!(
binary_name_from_argv0(Some(OsStr::new("/usr/local/bin/truth"))),
"truth"
);
assert_eq!(
binary_name_from_argv0(Some(OsStr::new("/tmp/truth mirror"))),
"truth mirror"
);
assert_eq!(
binary_name_from_argv0(Some(OsStr::new(""))),
DEFAULT_BINARY_NAME
);
assert_eq!(binary_name_from_argv0(None), DEFAULT_BINARY_NAME);
}
}