From ac5ea03a16f71fa7305e931619a132e150c83c29 Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Sun, 4 Oct 2020 09:46:31 -0700 Subject: Display available extensions in the help text With recent changes we are able to execute user-provided extensions through the program. However, discoverability is arguably lacking, because nitrocli provides no insight into what extensions are available to begin with. This patch changes this state of affairs by listing available extensions in the help text. --- src/commands.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src/commands.rs') diff --git a/src/commands.rs b/src/commands.rs index 549ebec..7612b76 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,6 +8,7 @@ use std::convert::TryFrom as _; use std::env; use std::ffi; use std::fmt; +use std::fs; use std::io; use std::mem; use std::ops; @@ -33,6 +34,8 @@ use crate::output; use crate::pinentry; use crate::Context; +const NITROCLI_EXT_PREFIX: &str = "nitrocli-"; + /// Set `libnitrokey`'s log level based on the execution context's verbosity. fn set_log_level(ctx: &mut Context<'_>) { let log_lvl = match ctx.config.verbosity { @@ -1087,15 +1090,52 @@ pub fn pws_status(ctx: &mut Context<'_>, all: bool) -> anyhow::Result<()> { }) } +/// Find and list all available extensions. +/// +/// The logic used in this function should use the same criteria as +/// `resolve_extension`. +pub(crate) fn discover_extensions(path_var: &ffi::OsStr) -> anyhow::Result> { + let dirs = env::split_paths(path_var); + let mut commands = Vec::new(); + + for dir in dirs { + match fs::read_dir(&dir) { + Ok(entries) => { + for entry in entries { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let name = entry.file_name(); + let file = name.to_string_lossy(); + if file.starts_with(NITROCLI_EXT_PREFIX) { + let mut file = file.into_owned(); + file.replace_range(..NITROCLI_EXT_PREFIX.len(), ""); + commands.push(file); + } + } + } + } + Err(ref err) if err.kind() == io::ErrorKind::NotFound => (), + x => x + .map(|_| ()) + .with_context(|| format!("Failed to iterate entries of directory {}", dir.display()))?, + } + } + Ok(commands) +} + /// Resolve an extension provided by name to an actual path. /// /// Extensions are (executable) files that have the "nitrocli-" prefix /// and are discoverable via the `PATH` environment variable. +/// +/// The logic used in this function should use the same criteria as +/// `discover_extensions`. pub(crate) fn resolve_extension( path_var: &ffi::OsStr, ext_name: &ffi::OsStr, ) -> anyhow::Result { - let mut bin_name = ffi::OsString::from("nitrocli-"); + let mut bin_name = ffi::OsString::from(NITROCLI_EXT_PREFIX); bin_name.push(ext_name); for dir in env::split_paths(path_var) { -- cgit v1.2.1