From d1726e52d64b5fd32b78bda3f12ca6027a2cb38a Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Tue, 22 Jul 2025 19:32:20 +0200 Subject: [PATCH] feat: add info for 'official' plugins (#2835) * feat: add info for 'official' plugins * fix default features --- crates/atuin-client/src/lib.rs | 1 + crates/atuin-client/src/plugin.rs | 97 ++++++++++++++++++++++++++++ crates/atuin/src/command/external.rs | 24 ++++++- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 crates/atuin-client/src/plugin.rs diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index 443ff3f8..78819548 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -15,6 +15,7 @@ pub mod import; pub mod login; pub mod logout; pub mod ordering; +pub mod plugin; pub mod record; pub mod register; pub mod secrets; diff --git a/crates/atuin-client/src/plugin.rs b/crates/atuin-client/src/plugin.rs new file mode 100644 index 00000000..21a2bcef --- /dev/null +++ b/crates/atuin-client/src/plugin.rs @@ -0,0 +1,97 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct OfficialPlugin { + pub name: String, + pub description: String, + pub install_message: String, +} + +impl OfficialPlugin { + pub fn new(name: &str, description: &str, install_message: &str) -> Self { + Self { + name: name.to_string(), + description: description.to_string(), + install_message: install_message.to_string(), + } + } +} + +pub struct OfficialPluginRegistry { + plugins: HashMap, +} + +impl OfficialPluginRegistry { + pub fn new() -> Self { + let mut registry = Self { + plugins: HashMap::new(), + }; + + // Register official plugins + registry.register_official_plugins(); + + registry + } + + fn register_official_plugins(&mut self) { + // atuin-update plugin + self.plugins.insert( + "update".to_string(), + OfficialPlugin::new( + "update", + "Update atuin to the latest version", + "The 'atuin update' command is provided by the atuin-update plugin.\n\ + It is only installed if you used the install script\n \ + If you used a package manager (brew, apt, etc), please continue to use it for updates" + ), + ); + } + + pub fn get_plugin(&self, name: &str) -> Option<&OfficialPlugin> { + self.plugins.get(name) + } + + pub fn is_official_plugin(&self, name: &str) -> bool { + self.plugins.contains_key(name) + } + + pub fn get_install_message(&self, name: &str) -> Option<&str> { + self.plugins + .get(name) + .map(|plugin| plugin.install_message.as_str()) + } +} + +impl Default for OfficialPluginRegistry { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_registry_creation() { + let registry = OfficialPluginRegistry::new(); + assert!(registry.is_official_plugin("update")); + assert!(!registry.is_official_plugin("nonexistent")); + } + + #[test] + fn test_get_plugin() { + let registry = OfficialPluginRegistry::new(); + let plugin = registry.get_plugin("update"); + assert!(plugin.is_some()); + assert_eq!(plugin.unwrap().name, "update"); + } + + #[test] + fn test_get_install_message() { + let registry = OfficialPluginRegistry::new(); + let message = registry.get_install_message("update"); + assert!(message.is_some()); + assert!(message.unwrap().contains("atuin-update")); + } +} diff --git a/crates/atuin/src/command/external.rs b/crates/atuin/src/command/external.rs index 03deacbf..657aea56 100644 --- a/crates/atuin/src/command/external.rs +++ b/crates/atuin/src/command/external.rs @@ -2,6 +2,8 @@ use std::fmt::Write as _; use std::process::Command; use std::{io, process}; +#[cfg(feature = "client")] +use atuin_client::plugin::OfficialPluginRegistry; use clap::CommandFactory; use clap::builder::{StyledStr, Styles}; use eyre::Result; @@ -44,13 +46,31 @@ pub fn run(args: &[String]) -> Result<()> { fn render_not_found(subcommand: &str, bin: &str) -> StyledStr { let mut output = StyledStr::new(); let styles = Styles::styled(); - let mut atuin_cmd = Atuin::command(); - let usage = atuin_cmd.render_usage(); let error = styles.get_error(); let invalid = styles.get_invalid(); let literal = styles.get_literal(); + #[cfg(feature = "client")] + { + let registry = OfficialPluginRegistry::new(); + + // Check if this is an official plugin + if let Some(install_message) = registry.get_install_message(subcommand) { + let _ = write!(output, "{error}error:{error:#} "); + let _ = write!( + output, + "'{invalid}{subcommand}{invalid:#}' is an official atuin plugin, but it's not installed" + ); + let _ = write!(output, "\n\n"); + let _ = write!(output, "{install_message}"); + return output; + } + } + + let mut atuin_cmd = Atuin::command(); + let usage = atuin_cmd.render_usage(); + let _ = write!(output, "{error}error:{error:#} "); let _ = write!( output,