diff --git a/implementations/rust/ockam/ockam_api/src/ui/command.rs b/implementations/rust/ockam/ockam_api/src/ui/command.rs new file mode 100644 index 00000000000..a9bade32a23 --- /dev/null +++ b/implementations/rust/ockam/ockam_api/src/ui/command.rs @@ -0,0 +1,92 @@ +use std::fmt::{Debug, Formatter}; + +#[derive(Clone, Debug)] +pub struct Commands { + pub(crate) commands: Vec, +} + +#[derive(Clone)] +pub(crate) struct Command { + pub(crate) name: &'static str, + pub(crate) custom_name: &'static str, +} + +impl Debug for Command { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Command") + .field("name", &self.name) + .field("custom_name", &self.custom_name) + .finish() + } +} + +impl Commands { + pub fn new(commands: &'static str) -> Self { + let commands = commands + .split(',') + .filter_map(|c| { + if c.is_empty() { + return None; + } + let mut parts = c.split('='); + let name = match parts.next() { + Some(name) => name, + None => return None, + }; + let custom_name = parts.next().unwrap_or(name); + Some(Command { name, custom_name }) + }) + .collect(); + Self { commands } + } + + pub fn hide(&self, command_name: &'static str) -> bool { + // No restrictions + if self.commands.is_empty() { + return false; + } + // Check if the command is in the list of hidden commands + !self.commands.iter().any(|c| c.name == command_name) + } + + pub fn name(&self, command_name: &'static str) -> &'static str { + // No restrictions + if self.commands.is_empty() { + return command_name; + } + // Check the custom name in the list of renamed commands + self.commands + .iter() + .find(|c| c.name == command_name) + .map_or(command_name, |c| c.custom_name) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hide() { + let commands = Commands::new("node=host,project,enroll"); + assert!(!commands.hide("node")); + assert!(!commands.hide("project")); + assert!(!commands.hide("enroll")); + assert!(commands.hide("command4")); + + let commands = Commands::new(""); + assert!(!commands.hide("command1")); + } + + #[test] + fn test_commands() { + let commands = Commands::new("node=host,project,enroll"); + assert_eq!(commands.name("node"), "host"); + assert_eq!(commands.name("project"), "project"); + assert_eq!(commands.name("enroll"), "enroll"); + assert_eq!(commands.name("command4"), "command4"); + + let commands = Commands::new(""); + assert_eq!(commands.name("command1"), "command1"); + } +} diff --git a/implementations/rust/ockam/ockam_api/src/ui/mod.rs b/implementations/rust/ockam/ockam_api/src/ui/mod.rs index 7a425cd6a62..b14335a0989 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/mod.rs @@ -1,3 +1,4 @@ pub mod colors; +pub mod command; pub mod output; pub mod terminal; diff --git a/implementations/rust/ockam/ockam_api/src/ui/output/branding.rs b/implementations/rust/ockam/ockam_api/src/ui/output/branding.rs new file mode 100644 index 00000000000..5721a051830 --- /dev/null +++ b/implementations/rust/ockam/ockam_api/src/ui/output/branding.rs @@ -0,0 +1,51 @@ +use crate::command::Commands; + +#[derive(Clone, Debug)] +pub struct OutputBranding { + pub brand_name: String, + pub bin_name: String, + pub commands: Commands, +} + +impl OutputBranding { + pub fn new(brand_name: String, bin_name: String, commands: Commands) -> Self { + Self { + brand_name, + bin_name, + commands, + } + } + + pub fn replace(&self, text: &str) -> String { + // brand name + let mut text = if self.brand_name != "Ockam" { + text.replace("Ockam", &self.brand_name) + } else { + text.to_string() + }; + // command names + for command in &self.commands.commands { + text = text.replace( + &format!("ockam {}", command.name), + &format!("ockam {}", command.custom_name), + ); + } + // bin name + text = if self.bin_name != "ockam" { + text.replace("ockam", &self.bin_name) + } else { + text + }; + text + } +} + +impl Default for OutputBranding { + fn default() -> Self { + Self { + brand_name: "Ockam".to_string(), + bin_name: "ockam".to_string(), + commands: Commands::new(""), + } + } +} diff --git a/implementations/rust/ockam/ockam_api/src/ui/output/mod.rs b/implementations/rust/ockam/ockam_api/src/ui/output/mod.rs index be4469badca..f94b83b02e2 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/output/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/output/mod.rs @@ -1,8 +1,10 @@ +mod branding; mod encode_format; mod ockam_abac; mod output_format; mod utils; +pub use branding::OutputBranding; pub use encode_format::EncodeFormat; pub use output_format::OutputFormat; pub use utils::*; diff --git a/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs b/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs index 751d789b947..619c463b00a 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/terminal/mod.rs @@ -7,6 +7,7 @@ pub mod term; pub use fmt::{get_separator_width, ICON_PADDING, INDENTATION, PADDING}; pub use highlighting::TextHighlighter; +use crate::output::OutputBranding; use crate::ui::output::OutputFormat; use crate::{Result, UiError}; use colorful::Colorful; @@ -69,24 +70,13 @@ impl Terminal { pub struct TerminalStream { pub writer: T, pub no_color: bool, - bin_name: String, - brand_name: String, + branding: OutputBranding, } impl TerminalStream { pub fn prepare_msg(&self, msg: impl AsRef) -> Result { let msg = msg.as_ref().to_string(); - let mut msg = if self.brand_name != "Ockam" { - msg.replace("Ockam", &self.brand_name) - } else { - msg - }; - msg = if self.bin_name != "ockam" { - msg.replace("ockam", &self.bin_name) - } else { - msg - }; - + let msg = self.branding.replace(&msg); if self.no_color { Ok(strip_ansi_escapes::strip_str(&msg)) } else { @@ -97,8 +87,8 @@ impl TerminalStream { /// Trait defining the main methods to write messages to a terminal stream. pub trait TerminalWriter: Clone { - fn stdout(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self; - fn stderr(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self; + fn stdout(no_color: bool, branding: OutputBranding) -> Self; + fn stderr(no_color: bool, branding: OutputBranding) -> Self; fn is_tty(&self) -> bool; fn color(&self) -> bool; @@ -117,15 +107,12 @@ impl Terminal { no_color: bool, no_input: bool, output_format: OutputFormat, - bin_name: impl Into, - brand_name: impl Into, + branding: OutputBranding, ) -> Self { - let bin_name = bin_name.into(); - let brand_name = brand_name.into(); let no_color = Self::should_disable_color(no_color); let no_input = Self::should_disable_user_input(no_input); - let stdout = W::stdout(no_color, &bin_name, &brand_name); - let stderr = W::stderr(no_color, bin_name, brand_name); + let stdout = W::stdout(no_color, branding.clone()); + let stderr = W::stderr(no_color, branding); let max_width_col_count = get_size().map(|it| it.col_count).unwrap_or(ch!(80)).into(); Self { stdout, @@ -149,8 +136,7 @@ impl Terminal { false, false, OutputFormat::Plain, - "ockam", - "Ockam", + OutputBranding::default(), ) } diff --git a/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs b/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs index d307d03b1ae..4360ae517af 100644 --- a/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs +++ b/implementations/rust/ockam/ockam_api/src/ui/terminal/term.rs @@ -1,31 +1,29 @@ //! Implementation of the `TerminalWriter` using the `Term` crate -use dialoguer::console::Term; -use std::io::Write; - +use crate::output::OutputBranding; use crate::terminal::{TerminalStream, TerminalWriter}; use crate::Result; +use dialoguer::console::Term; +use std::io::Write; impl TerminalWriter for TerminalStream { - fn stdout(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self { + fn stdout(no_color: bool, branding: OutputBranding) -> Self { let writer = Term::stdout(); let no_color = no_color || !writer.features().colors_supported(); Self { writer, no_color, - bin_name: bin_name.into(), - brand_name: brand_name.into(), + branding, } } - fn stderr(no_color: bool, bin_name: impl Into, brand_name: impl Into) -> Self { + fn stderr(no_color: bool, branding: OutputBranding) -> Self { let writer = Term::stderr(); let no_color = no_color || !writer.features().colors_supported(); Self { writer, no_color, - bin_name: bin_name.into(), - brand_name: brand_name.into(), + branding, } } @@ -62,7 +60,7 @@ mod tests { use colorful::Colorful; use dialoguer::console::Term; - use crate::output::OutputFormat; + use crate::output::{OutputBranding, OutputFormat}; use crate::terminal::{Terminal, TerminalStream}; #[test] @@ -74,8 +72,7 @@ mod tests { false, false, OutputFormat::Plain, - "", - "", + OutputBranding::default(), ); sut.write("1").unwrap(); sut.rewrite("1-r\n").unwrap(); diff --git a/implementations/rust/ockam/ockam_command/src/authority/create.rs b/implementations/rust/ockam/ockam_command/src/authority/create.rs index 5da85ddced3..ccbef6fd61b 100644 --- a/implementations/rust/ockam/ockam_command/src/authority/create.rs +++ b/implementations/rust/ockam/ockam_command/src/authority/create.rs @@ -21,7 +21,7 @@ use crate::node::node_callback::NodeCallback; use crate::node::util::run_ockam; use crate::util::foreground_args::{wait_for_exit_signal, ForegroundArgs}; use crate::util::parsers::internet_address_parser; -use crate::{docs, CommandGlobalOpts, Result}; +use crate::{branding, docs, CommandGlobalOpts, Result}; const LONG_ABOUT: &str = include_str!("./static/create/long_about.txt"); const PREVIEW_TAG: &str = include_str!("../static/preview_tag.txt"); @@ -144,7 +144,7 @@ impl CreateCommand { 0 => "-vv".to_string(), v => format!("-{}", "v".repeat(v as usize)), }, - "authority".to_string(), + branding::command::name("authority").to_string(), "create".to_string(), "--foreground".to_string(), "--child-process".to_string(), diff --git a/implementations/rust/ockam/ockam_command/src/bin/brand.rs b/implementations/rust/ockam/ockam_command/src/bin/brand.rs index f9ae9fbb4bb..3d4aaacbec1 100644 --- a/implementations/rust/ockam/ockam_command/src/bin/brand.rs +++ b/implementations/rust/ockam/ockam_command/src/bin/brand.rs @@ -1,10 +1,13 @@ -use clap::Parser; +use clap::{CommandFactory, Parser}; +use colorful::Colorful; use miette::{miette, IntoDiagnostic, Result, WrapErr}; use ockam::identity::Identifier; use ockam_api::colors::{color_primary, color_warn}; -use ockam_api::fmt_log; use ockam_api::terminal::INDENTATION; +use ockam_api::{fmt_log, fmt_warn}; use ockam_command::branding::compile_env_vars::*; +use ockam_command::entry_point::top_level_command_names; +use ockam_command::OckamCommand; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; @@ -50,14 +53,21 @@ fn main() -> Result<()> { }; // Build the binaries + let cmd = OckamCommand::command(); + let top_level_commands = top_level_command_names(&cmd); for (bin_name, brand_config) in config.items { - build_binary(bin_name, brand_config, args.dry_run)?; + build_binary(bin_name, brand_config, &top_level_commands, args.dry_run)?; } Ok(()) } /// Builds the binary with the passed settings -fn build_binary(bin_name: String, brand_settings: Brand, dry_run: bool) -> Result<()> { +fn build_binary( + bin_name: String, + brand_settings: Brand, + top_level_commands: &[String], + dry_run: bool, +) -> Result<()> { brand_settings.validate()?; eprintln!( @@ -65,6 +75,10 @@ fn build_binary(bin_name: String, brand_settings: Brand, dry_run: bool) -> Resul fmt_log!("Building binary {} with", color_primary(&bin_name)) ); + let commands = brand_settings.commands(top_level_commands); + let brand_name = brand_settings.brand_name(&bin_name); + let home_dir = brand_settings.home_dir(&bin_name); + if dry_run { return Ok(()); } @@ -75,9 +89,7 @@ fn build_binary(bin_name: String, brand_settings: Brand, dry_run: bool) -> Resul if let Some(build_args) = &brand_settings.build_args { cmd.args(build_args); } - let commands = brand_settings.commands(); - let brand_name = brand_settings.brand_name(&bin_name); - let home_dir = brand_settings.home_dir(&bin_name); + cmd.envs([ (COMPILE_OCKAM_DEVELOPER, "false".to_string()), ( @@ -169,26 +181,41 @@ impl Brand { } } - fn commands(&self) -> String { + fn commands(&self, top_level_commands: &[String]) -> String { match &self.commands { None => String::new(), Some(commands) => { - let process_command_name = |c: &str| { + let process_command_name = |name: &str, custom_name: Option<&str>| { + if !top_level_commands.iter().any(|t| t == name) { + eprintln!( + "{}", + fmt_warn!( + "Command {} is not a top level command, it can't be renamed or hidden. Skipping...", + color_primary(name) + ) + ); + return None; + } + // replace _ and - with space to support writing // commands as "node create", "node-create" or "node_create - c.replace("_", " ").replace("-", " ") + let name = match custom_name { + Some(custom_name) => format!("{}={}", name, custom_name), + None => name.to_string(), + }; + Some(name.replace("_", " ").replace("-", " ")) }; // A comma separated list of commands in the format `command1=customName,command2,command3` commands .iter() - .map(|c| match c { - Command::Simple(c) => process_command_name(c), + .filter_map(|c| match c { + Command::Simple(c) => process_command_name(c, None), Command::Mapped(map) => map .iter() - .map(|(k, v)| process_command_name(&format!("{}={}", k, v))) - .collect::>() - .join(","), + .map(|(k, v)| process_command_name(k, Some(v))) + .collect::>>() + .map(|v| v.join(",")), }) .collect::>() .join(",") diff --git a/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml b/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml index b64067647cb..fa5de29f5c1 100644 --- a/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml +++ b/implementations/rust/ockam/ockam_command/src/bin/brand.sample.yaml @@ -2,10 +2,10 @@ acme: support_email: support@acme.com # the following fields are optional brand_name: Acme # if not set, will default to Bin - home_dir: $HOME/.acme # if not set, will default to $HOME/.bin + home_dir: $HOME/.acme # if not set, will default to $HOME/.acme orchestrator_identifier: I25242aa3d4a7b5aa986fb2bec15b3780aad0530660e5e5a46c7f9ce429e9ec99 # if not set, will default to the OCKAM_CONTROLLER_IDENTIFIER env var orchestrator_address: /dnsaddr/acme.io/tcp/6252/service/api # if not set, will default to the OCKAM_CONTROLLER_ADDRESS env var -# build_args: # if not set, will default to empty string + build_args: # if not set, will default to empty string # - --no-default-features # - --features # - 'no_std alloc software_vault rust-crypto' diff --git a/implementations/rust/ockam/ockam_command/src/branding/command.rs b/implementations/rust/ockam/ockam_command/src/branding/command.rs index fb237c76a02..4f2440fce6a 100644 --- a/implementations/rust/ockam/ockam_command/src/branding/command.rs +++ b/implementations/rust/ockam/ockam_command/src/branding/command.rs @@ -1,107 +1,13 @@ -use crate::Result; +use ockam_api::command::Commands; use once_cell::sync::Lazy; -use std::fmt::{Debug, Formatter}; -pub(crate) fn name(name: &str) -> &'static str { +pub(crate) fn name(name: &'static str) -> &'static str { CUSTOM_COMMANDS.name(name) } -pub(crate) fn hide(name: &str) -> bool { +pub(crate) fn hide(name: &'static str) -> bool { CUSTOM_COMMANDS.hide(name) } -static CUSTOM_COMMANDS: Lazy = - Lazy::new(|| Commands::from_env().expect("Failed to load custom commands")); - -pub(crate) struct Commands { - commands: Vec, -} - -pub(crate) struct Command { - name: String, - custom_name: String, -} - -impl Debug for Command { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Command") - .field("name", &self.name) - .field("custom_name", &self.custom_name) - .finish() - } -} - -impl Commands { - fn new(commands: &str) -> Result { - let commands = commands - .split(',') - .filter_map(|c| { - if c.is_empty() { - return None; - } - let mut parts = c.split('='); - let name = match parts.next() { - Some(name) => name, - None => return None, - }; - let custom_name = parts.next().unwrap_or(name); - Some(Command { - name: name.to_string(), - custom_name: custom_name.to_string(), - }) - }) - .collect(); - Ok(Self { commands }) - } - - pub fn from_env() -> Result { - Self::new(super::BrandingCompileEnvVars::commands()) - } - - pub fn hide(&self, command_name: &str) -> bool { - if self.commands.is_empty() { - return false; - } - !self.commands.iter().any(|c| c.name == command_name) - } - - pub fn name(&self, command_name: &str) -> &'static str { - if self.commands.is_empty() { - return Box::leak(command_name.to_string().into_boxed_str()); - } - self.commands - .iter() - .find(|c| c.name == command_name) - .map(|c| Box::leak(c.custom_name.clone().into_boxed_str())) - .unwrap_or(Box::leak(command_name.to_string().into_boxed_str())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hide() { - let commands = Commands::new("node create=host create,project,enroll").unwrap(); - assert!(!commands.hide("node create")); - assert!(!commands.hide("project")); - assert!(!commands.hide("enroll")); - assert!(commands.hide("command4")); - - let commands = Commands::new("").unwrap(); - assert!(!commands.hide("command1")); - } - - #[test] - fn test_commands() { - let commands = Commands::new("node create=host create,project,enroll").unwrap(); - assert_eq!(commands.name("node create"), "host create"); - assert_eq!(commands.name("project"), "project"); - assert_eq!(commands.name("enroll"), "enroll"); - assert_eq!(commands.name("command4"), "command4"); - - let commands = Commands::new("").unwrap(); - assert_eq!(commands.name("command1"), "command1"); - } -} +pub(crate) static CUSTOM_COMMANDS: Lazy = + Lazy::new(|| Commands::new(super::BrandingCompileEnvVars::commands())); diff --git a/implementations/rust/ockam/ockam_command/src/branding/compile_env_vars.rs b/implementations/rust/ockam/ockam_command/src/branding/compile_env_vars.rs index 4efe5ee66d8..700e0ebff20 100644 --- a/implementations/rust/ockam/ockam_command/src/branding/compile_env_vars.rs +++ b/implementations/rust/ockam/ockam_command/src/branding/compile_env_vars.rs @@ -77,6 +77,7 @@ static BRANDING_ENV_VARS: Lazy = Lazy::new(|| { BrandingCompileEnvVars::new( COMPILE_BIN_NAME, COMPILE_BRAND_NAME, + COMPILE_HOME, COMPILE_SUPPORT_EMAIL, COMPILE_COMMANDS, bool::from_string(COMPILE_DEVELOPER).unwrap_or(false), @@ -86,6 +87,7 @@ static BRANDING_ENV_VARS: Lazy = Lazy::new(|| { pub struct BrandingCompileEnvVars { bin_name: &'static str, brand_name: &'static str, + home_dir: &'static str, support_email: &'static str, commands: &'static str, is_ockam_developer: bool, @@ -95,6 +97,7 @@ impl BrandingCompileEnvVars { pub fn new( bin_name: &'static str, brand_name: &'static str, + home_dir: &'static str, support_email: &'static str, commands: &'static str, is_ockam_developer: bool, @@ -102,6 +105,7 @@ impl BrandingCompileEnvVars { Self { bin_name, brand_name, + home_dir, support_email, commands, is_ockam_developer, @@ -120,6 +124,10 @@ impl BrandingCompileEnvVars { Self::get().brand_name } + pub fn home_dir() -> &'static str { + Self::get().home_dir + } + pub fn support_email() -> &'static str { Self::get().support_email } diff --git a/implementations/rust/ockam/ockam_command/src/branding/mod.rs b/implementations/rust/ockam/ockam_command/src/branding/mod.rs index ce02c453efd..cb5de803fdf 100644 --- a/implementations/rust/ockam/ockam_command/src/branding/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/branding/mod.rs @@ -2,3 +2,14 @@ pub mod command; pub mod compile_env_vars; pub use compile_env_vars::{load_compile_time_vars, BrandingCompileEnvVars}; +use ockam_api::command::Commands; +use ockam_api::output::OutputBranding; +use once_cell::sync::Lazy; + +pub(crate) static OUTPUT_BRANDING: Lazy = Lazy::new(|| { + OutputBranding::new( + BrandingCompileEnvVars::brand_name().to_string(), + BrandingCompileEnvVars::bin_name().to_string(), + Commands::new(BrandingCompileEnvVars::commands()), + ) +}); diff --git a/implementations/rust/ockam/ockam_command/src/command.rs b/implementations/rust/ockam/ockam_command/src/command.rs index 1260be9fd94..88cba3a9980 100644 --- a/implementations/rust/ockam/ockam_command/src/command.rs +++ b/implementations/rust/ockam/ockam_command/src/command.rs @@ -1,4 +1,4 @@ -use crate::branding::{load_compile_time_vars, BrandingCompileEnvVars}; +use crate::branding::{load_compile_time_vars, BrandingCompileEnvVars, OUTPUT_BRANDING}; use crate::command_events::add_command_event; use crate::command_global_opts::CommandGlobalOpts; use crate::global_args::GlobalArgs; @@ -80,7 +80,8 @@ impl OckamCommand { BrandingCompileEnvVars::bin_name() ) ); - let ockam_home = std::env::var("OCKAM_HOME").unwrap_or("~/.ockam".to_string()); + let ockam_home = std::env::var("OCKAM_HOME") + .unwrap_or(BrandingCompileEnvVars::home_dir().to_string()); eprintln!( "{}", fmt_log!( @@ -246,8 +247,7 @@ impl OckamCommand { self.global_args.no_color, self.global_args.no_input, self.global_args.output_format(), - BrandingCompileEnvVars::bin_name(), - BrandingCompileEnvVars::brand_name(), + OUTPUT_BRANDING.clone(), ); let options = CommandGlobalOpts::new(self.global_args.clone(), cli_state, terminal); diff --git a/implementations/rust/ockam/ockam_command/src/completion/mod.rs b/implementations/rust/ockam/ockam_command/src/completion/mod.rs index 669e7ec4a44..899f84ba5e0 100644 --- a/implementations/rust/ockam/ockam_command/src/completion/mod.rs +++ b/implementations/rust/ockam/ockam_command/src/completion/mod.rs @@ -3,6 +3,7 @@ use std::io; use clap::{Args, CommandFactory}; use clap_complete::{generate, Shell}; +use crate::branding::BrandingCompileEnvVars; use crate::{docs, OckamCommand}; const LONG_ABOUT: &str = include_str!("./static/long_about.txt"); @@ -28,7 +29,7 @@ impl CompletionCommand { generate( self.shell, &mut OckamCommand::command(), - "ockam", + BrandingCompileEnvVars::bin_name(), &mut io::stdout(), ); Ok(()) diff --git a/implementations/rust/ockam/ockam_command/src/docs.rs b/implementations/rust/ockam/ockam_command/src/docs.rs index a87a5c72c52..47e475137d2 100644 --- a/implementations/rust/ockam/ockam_command/src/docs.rs +++ b/implementations/rust/ockam/ockam_command/src/docs.rs @@ -1,4 +1,4 @@ -use crate::branding::BrandingCompileEnvVars; +use crate::branding::{BrandingCompileEnvVars, OUTPUT_BRANDING}; use crate::Result; use colorful::Colorful; use ockam_api::terminal::TextHighlighter; @@ -90,7 +90,7 @@ pub(crate) fn after_help(text: &str) -> &'static str { /// Render the string if the document should be displayed in a terminal /// Otherwise, if it is a Markdown document just return a static string fn render(body: &str) -> &'static str { - let body = process_branding(body); + let body = OUTPUT_BRANDING.replace(body); if *IS_MARKDOWN { Box::leak(body.into_boxed_str()) } else { @@ -99,20 +99,6 @@ fn render(body: &str) -> &'static str { } } -fn process_branding(text: &str) -> String { - let mut text = if BrandingCompileEnvVars::brand_name() != "Ockam" { - text.replace("Ockam", BrandingCompileEnvVars::brand_name()) - } else { - text.to_string() - }; - text = if BrandingCompileEnvVars::bin_name() != "ockam" { - text.replace("ockam", BrandingCompileEnvVars::bin_name()) - } else { - text - }; - text -} - /// Use a shell syntax highlighter to render the fenced code blocks in terminals fn process_terminal_docs(input: String) -> String { let mut output: Vec = Vec::new(); diff --git a/implementations/rust/ockam/ockam_command/src/entry_point.rs b/implementations/rust/ockam/ockam_command/src/entry_point.rs index 91f3632c9aa..675f4d0abd2 100644 --- a/implementations/rust/ockam/ockam_command/src/entry_point.rs +++ b/implementations/rust/ockam/ockam_command/src/entry_point.rs @@ -1,8 +1,9 @@ use std::process::exit; -use clap::Parser; +use clap::{Command, Parser}; use miette::IntoDiagnostic; +use crate::branding::BrandingCompileEnvVars; use crate::{ add_command_error_event, has_help_flag, has_version_flag, pager, replace_hyphen_with_stdin, util::exitcode, version::Version, OckamCommand, @@ -102,3 +103,35 @@ fn print_version_and_exit() { ); exit(exitcode::OK); } + +/// Return the names of the top-level commands, i.e., the commands defined in [crate::OckamSubcommand] +pub fn top_level_command_names(cmd: &Command) -> Vec { + let mut names = vec![]; + let bin_name = BrandingCompileEnvVars::bin_name(); + for subcmd in cmd.get_subcommands() { + names.push(subcmd.get_name().replace(bin_name, "")); + } + names +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::CommandFactory; + + #[test] + fn test_top_level_command_names() { + let cmd = OckamCommand::command(); + let top_level_commands = top_level_command_names(&cmd); + + let top_level_commands_sample = vec!["node", "project", "tcp-inlet"]; + for name in top_level_commands_sample { + assert!(top_level_commands.contains(&name.to_string())); + } + + let subcommands_sample = vec!["node create", "tcp-inlet delete"]; + for name in subcommands_sample { + assert!(!top_level_commands.contains(&name.to_string())); + } + } +} diff --git a/implementations/rust/ockam/ockam_command/src/node/create.rs b/implementations/rust/ockam/ockam_command/src/node/create.rs index ab01a4e8050..4073eb1f2da 100644 --- a/implementations/rust/ockam/ockam_command/src/node/create.rs +++ b/implementations/rust/ockam/ockam_command/src/node/create.rs @@ -409,7 +409,7 @@ mod tests { use super::*; use crate::run::parser::resource::utils::parse_cmd_from_args; use crate::GlobalArgs; - use ockam_api::output::OutputFormat; + use ockam_api::output::{OutputBranding, OutputFormat}; use ockam_api::terminal::Terminal; use ockam_api::CliState; use std::sync::Arc; @@ -549,8 +549,7 @@ mod tests { true, false, OutputFormat::Plain, - "", - "", + OutputBranding::default(), ), global_args: GlobalArgs::default(), }; @@ -608,8 +607,7 @@ mod tests { true, false, OutputFormat::Plain, - "", - "", + OutputBranding::default(), ), global_args: GlobalArgs::default(), }; diff --git a/implementations/rust/ockam/ockam_command/src/node/util.rs b/implementations/rust/ockam/ockam_command/src/node/util.rs index 5996581f7c5..a609570f87e 100644 --- a/implementations/rust/ockam/ockam_command/src/node/util.rs +++ b/implementations/rust/ockam/ockam_command/src/node/util.rs @@ -1,7 +1,8 @@ +use crate::branding::BrandingCompileEnvVars; use crate::node::CreateCommand; use crate::run::parser::resource::utils::subprocess_stdio; use crate::shared_args::TrustOpts; -use crate::{Command, CommandGlobalOpts}; +use crate::{branding, Command as CommandTrait, CommandGlobalOpts}; use miette::IntoDiagnostic; use miette::{miette, Context as _}; use ockam_core::env::get_env_with_default; @@ -81,7 +82,7 @@ pub fn spawn_node(opts: &CommandGlobalOpts, cmd: CreateCommand) -> miette::Resul 0 => "-vv".to_string(), v => format!("-{}", "v".repeat(v as usize)), }, - "node".to_string(), + branding::command::name("node").to_string(), "create".to_string(), "--tcp-listener-address".to_string(), tcp_listener_address.to_string(), @@ -202,7 +203,7 @@ pub fn run_ockam(args: Vec, quiet: bool) -> miette::Result { // development) re-executing the current binary is a more // deterministic way of starting a node. let ockam_exe = current_exe().unwrap_or_else(|_| { - get_env_with_default("OCKAM", "ockam".to_string()) + get_env_with_default("OCKAM", BrandingCompileEnvVars::bin_name().to_string()) .unwrap() .into() }); diff --git a/implementations/rust/ockam/ockam_command/src/subcommand.rs b/implementations/rust/ockam/ockam_command/src/subcommand.rs index 6a6923bcc2a..bbaebe1d58a 100644 --- a/implementations/rust/ockam/ockam_command/src/subcommand.rs +++ b/implementations/rust/ockam/ockam_command/src/subcommand.rs @@ -17,6 +17,7 @@ use ockam_node::Context; use crate::admin::AdminCommand; use crate::authority::{AuthorityCommand, AuthoritySubcommand}; +use crate::branding::command; use crate::command_global_opts::CommandGlobalOpts; use crate::completion::CompletionCommand; use crate::credential::CredentialCommand; @@ -63,7 +64,6 @@ use crate::vault::VaultCommand; use crate::worker::WorkerCommand; use crate::Error; use crate::Result; -use crate::{branding, branding::command}; #[derive(Clone, Debug, Subcommand)] #[command(about = docs::about("List of commands which can be executed with `ockam`"))] @@ -363,11 +363,11 @@ pub trait Command: Debug + Clone + Sized + Send + Sync + 'static { const NAME: &'static str; fn name(&self) -> String { - branding::command::name(Self::NAME).to_string() + command::name(Self::NAME).to_string() } fn hide() -> bool { - branding::command::hide(Self::NAME) + command::hide(Self::NAME) } fn retry_opts(&self) -> Option {