diff options
Diffstat (limited to 'mingling_cli')
| -rw-r--r-- | mingling_cli/src/errors.rs | 1 | ||||
| -rw-r--r-- | mingling_cli/src/linter/cmd_explain.rs | 33 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr.rs | 45 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr/cmd_install.rs | 35 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs | 26 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs | 25 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr/cmd_pkg_show.rs | 27 | ||||
| -rw-r--r-- | mingling_cli/src/pkg_mgr/cmd_uninstall.rs | 67 | ||||
| -rw-r--r-- | mingling_cli/src/proj_mgr.rs | 1 | ||||
| -rw-r--r-- | mingling_cli/src/utils.rs | 1 | ||||
| -rw-r--r-- | mingling_cli/src/utils/cargo_style.rs | 218 |
11 files changed, 379 insertions, 100 deletions
diff --git a/mingling_cli/src/errors.rs b/mingling_cli/src/errors.rs index 5cdc43f..754dcc6 100644 --- a/mingling_cli/src/errors.rs +++ b/mingling_cli/src/errors.rs @@ -1,3 +1,2 @@ pub mod io_error; pub mod serde_json; - diff --git a/mingling_cli/src/linter/cmd_explain.rs b/mingling_cli/src/linter/cmd_explain.rs index d1758e1..5346255 100644 --- a/mingling_cli/src/linter/cmd_explain.rs +++ b/mingling_cli/src/linter/cmd_explain.rs @@ -1,6 +1,6 @@ -use crate::{Next, linter::registry::ResLintRegistry}; +use crate::{Next, eprintln_cargo, linter::registry::ResLintRegistry}; use mingling::{ - Grouped, LazyRes, Routable, ShellContext, Suggest, SuggestItem, + Grouped, LazyRes, RenderResult, Routable, ShellContext, Suggest, SuggestItem, macros::{ arg, buffer, chain, completion, dispatcher, metadata, pack, pack_err, r_println, renderer, routeify, @@ -71,22 +71,29 @@ pub fn render_explain_lint(r: ResultExplainLint) { r_println!("{}", r.summary); } -#[renderer(buffer)] -pub fn render_error_no_explain_lint_provided(_: ErrorNoExplainLintProvided) { - r_println!("No lint name provided"); - r_println!(""); - r_println!("Usage: mling explain <LINT>"); +#[renderer] +pub fn render_error_no_explain_lint_provided(_: ErrorNoExplainLintProvided) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "No lint name provided"); + r_println!(r, ""); + r_println!(r, "Usage: mling explain <LINT>"); + r } -#[renderer(buffer)] -pub fn render_error_no_such_lint(err: ErrorNoSuchLint, registry: &mut LazyRes<ResLintRegistry>) { +#[renderer] +pub fn render_error_no_such_lint( + err: ErrorNoSuchLint, + registry: &mut LazyRes<ResLintRegistry>, +) -> RenderResult { + let mut r = RenderResult::new(); let registry = registry.get_ref(); - r_println!("No such lint: \"{}\"", err.info); - r_println!(""); - r_println!("Available lints:"); + eprintln_cargo!(r, "No such lint: \"{}\"", err.info); + r_println!(r, ""); + r_println!(r, "Available lints:"); for entry in registry.lints.iter() { - r_println!(" {}", entry.name); + r_println!(r, " {}", entry.name); } + r } #[completion(EntryExplain)] diff --git a/mingling_cli/src/pkg_mgr.rs b/mingling_cli/src/pkg_mgr.rs index f244fa7..080c50f 100644 --- a/mingling_cli/src/pkg_mgr.rs +++ b/mingling_cli/src/pkg_mgr.rs @@ -8,11 +8,11 @@ pub mod cmd_uninstall; use std::path::PathBuf; use mingling::{ - Program, - macros::{buffer, pack_err, program_setup, r_println, renderer}, + Program, RenderResult, + macros::{pack_err, program_setup, r_println, renderer}, }; -use crate::ThisProgram; +use crate::{ThisProgram, eprintln_cargo, hprintln_cargo}; pack_err!(ErrorRootPackageNotFound); pack_err!(ErrorNoDataDirectory); @@ -33,24 +33,35 @@ pub fn package_manager_setup(program: &mut Program<ThisProgram>) { program.with_resource(ResPackagesDir { path }); } -#[renderer(buffer)] -pub fn render_error_root_package_not_found(_: ErrorRootPackageNotFound) { - r_println!("error: failed to determine the root package"); - r_println!(""); - r_println!("Run `mling install` / `mling uninstall` inside a Cargo workspace"); +#[renderer] +pub fn render_error_root_package_not_found(_: ErrorRootPackageNotFound) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "failed to determine the root package"); + r_println!(r, ""); + hprintln_cargo!( + r, + "Run `mling install` / `mling uninstall` inside a Cargo workspace" + ); + r } -#[renderer(buffer)] -pub fn render_error_no_data_directory(_: ErrorNoDataDirectory) { - r_println!("error: failed to determine the data directory"); +#[renderer] +pub fn render_error_no_data_directory(_: ErrorNoDataDirectory) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "failed to determine the data directory"); + r } -#[renderer(buffer)] -pub fn render_error_package_spec_invalid(err: ErrorPackageSpecInvalid) { - r_println!("error: invalid package spec: {}", err.info); +#[renderer] +pub fn render_error_package_spec_invalid(err: ErrorPackageSpecInvalid) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "invalid package spec: {}", err.info); + r } -#[renderer(buffer)] -pub fn render_error_package_name_required(_: ErrorPackageNameRequired) { - r_println!("error: a package name is required"); +#[renderer] +pub fn render_error_package_name_required(_: ErrorPackageNameRequired) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "a package name is required"); + r } diff --git a/mingling_cli/src/pkg_mgr/cmd_install.rs b/mingling_cli/src/pkg_mgr/cmd_install.rs index a399e83..2b4082a 100644 --- a/mingling_cli/src/pkg_mgr/cmd_install.rs +++ b/mingling_cli/src/pkg_mgr/cmd_install.rs @@ -2,15 +2,16 @@ use std::{env, fs, io, path::PathBuf, process::Command}; use cargo_metadata::TargetKind; use mingling::{ - Grouped, LazyRes, Routable, - macros::{buffer, chain, command, metadata, pack_err, r_println, renderer, routeify}, + Grouped, LazyRes, RenderResult, Routable, + macros::{chain, command, metadata, pack_err, renderer, routeify}, metadata::Description, }; use crate::{ - Next, + Next, eprintln_cargo, metadata::setup::ResMetadata, pkg_mgr::{ErrorNoDataDirectory, ErrorRootPackageNotFound, ResPackagesDir}, + println_cargo, }; pack_err!(ErrorBuildFailed = String); @@ -159,20 +160,26 @@ pub fn handle_state_install_copy( .to_chain() } -#[renderer(buffer)] -pub fn render_result_install(r: ResultInstall) { - r_println!("Installed to {}", r.install_dir.display()); - for file in r.installed { - r_println!(" {}", file.display()); +#[renderer] +pub fn render_result_install(result: ResultInstall) -> RenderResult { + let mut r = RenderResult::new(); + println_cargo!(r, "Installed: {}", result.install_dir.display()); + for file in result.installed { + println_cargo!(r, "Copy: {}", file.display()); } + r } -#[renderer(buffer)] -pub fn render_error_build_failed(err: ErrorBuildFailed) { - r_println!("error: {}", err.info); +#[renderer] +pub fn render_error_build_failed(err: ErrorBuildFailed) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "{}", err.info); + r } -#[renderer(buffer)] -pub fn render_error_binary_not_found(err: ErrorBinaryNotFound) { - r_println!("error: binary not found: {}", err.info); +#[renderer] +pub fn render_error_binary_not_found(err: ErrorBinaryNotFound) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "binary not found: {}", err.info); + r } diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs index 679421c..041bb09 100644 --- a/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs +++ b/mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs @@ -1,20 +1,18 @@ use std::{fs, io}; use mingling::{ - Grouped, Routable, ShellContext, Suggest, SuggestItem, - macros::{ - arg, buffer, chain, command, completion, metadata, pack, pack_err, r_println, renderer, - routeify, - }, + Grouped, RenderResult, Routable, ShellContext, Suggest, SuggestItem, + macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify}, metadata::Description, picker::{EntryPicker, PickerArg}, }; use crate::{ - Next, + Next, eprintln_cargo, pkg_mgr::{ ErrorNoDataDirectory, ErrorPackageNameRequired, ErrorPackageSpecInvalid, ResPackagesDir, }, + println_cargo, }; /// Positional argument: package name @@ -78,14 +76,18 @@ pub fn handle_state_pkg_disable(p: StatePkgDisable, packages_dir: &ResPackagesDi .to_chain() } -#[renderer(buffer)] -pub fn render_result_pkg_disable(r: ResultPkgDisable) { - r_println!("Disabled {}", r.name); +#[renderer] +pub fn render_result_pkg_disable(result: ResultPkgDisable) -> RenderResult { + let mut r = RenderResult::new(); + println_cargo!(r, "Disabled: {}", result.name); + r } -#[renderer(buffer)] -pub fn render_error_package_not_enabled(err: ErrorPackageNotEnabled) { - r_println!("error: package is not enabled: {}", err.info); +#[renderer] +pub fn render_error_package_not_enabled(err: ErrorPackageNotEnabled) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "package is not enabled: {}", err.info); + r } #[completion(EntryPkgDisable)] diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs index b545a4d..8f6a234 100644 --- a/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs +++ b/mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs @@ -1,20 +1,18 @@ use std::{fs, io}; use mingling::{ - Grouped, Routable, ShellContext, Suggest, SuggestItem, - macros::{ - arg, buffer, chain, command, completion, metadata, pack, pack_err, r_println, renderer, - routeify, - }, + Grouped, RenderResult, Routable, ShellContext, Suggest, SuggestItem, + macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify}, metadata::Description, picker::{EntryPicker, PickerArg}, }; use crate::{ - Next, + Next, eprintln_cargo, pkg_mgr::{ ErrorNoDataDirectory, ErrorPackageNameRequired, ErrorPackageSpecInvalid, ResPackagesDir, }, + println_cargo, }; /// Positional argument: package spec (`foo`, `foo@0`, `foo@0.1`, `foo@0.1.2`) @@ -102,9 +100,18 @@ pub fn handle_state_pkg_enable(p: StatePkgEnable, packages_dir: &ResPackagesDir) ResultPkgEnable { name, version }.to_chain() } -#[renderer(buffer)] -pub fn render_result_pkg_enable(r: ResultPkgEnable) { - r_println!("Enabled {}@{}", r.name, r.version); +#[renderer] +pub fn render_result_pkg_enable(result: ResultPkgEnable) -> RenderResult { + let mut r = RenderResult::new(); + println_cargo!(r, "Enabled: {}@{}", result.name, result.version); + r +} + +#[renderer] +pub fn render_error_no_matching_version(err: ErrorNoMatchingVersion) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "no matching version for: {}", err.info); + r } #[completion(EntryPkgEnable)] diff --git a/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs b/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs index 68aef2b..88c79ef 100644 --- a/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs +++ b/mingling_cli/src/pkg_mgr/cmd_pkg_show.rs @@ -2,13 +2,13 @@ use std::{collections::BTreeMap, fs, io}; use colored::Colorize; use mingling::{ - Grouped, Routable, - macros::{buffer, command, metadata, r_println, renderer, routeify}, + Grouped, RenderResult, Routable, + macros::{buffer, command, metadata, pack_err, r_println, renderer, routeify}, metadata::Description, }; use crate::{ - Next, + Next, eprintln_cargo, pkg_mgr::{ErrorNoDataDirectory, ResPackagesDir}, }; @@ -31,6 +31,8 @@ pub fn desc_pkg_show() -> Description { "Show locally installed packages".into() } +pack_err!(ErrorNoPackagesInstalled); + #[command(node = "pkg-show", routeify)] pub fn package_show(packages_dir: &ResPackagesDir) -> Next { let packages_dir = &packages_dir.path; @@ -79,17 +81,17 @@ pub fn package_show(packages_dir: &ResPackagesDir) -> Next { pkg.versions.sort_by(|a, b| compare_versions(b, a)); } - ResultPkgShow { - packages: entries.into_values().collect(), + let packages: Vec<PkgShowEntry> = entries.into_values().collect(); + + if packages.is_empty() { + return ErrorNoPackagesInstalled::default().into(); } - .to_chain() + + ResultPkgShow { packages }.to_chain() } #[renderer(buffer)] pub fn render_result_pkg_show(r: ResultPkgShow) { - if r.packages.is_empty() { - r_println!("No packages installed"); - } for pkg in r.packages { if let Some(enabled) = &pkg.enabled { r_println!("{}", format!("{} ({})", pkg.name, enabled).bright_cyan()); @@ -102,6 +104,13 @@ pub fn render_result_pkg_show(r: ResultPkgShow) { } } +#[renderer] +pub fn render_error_no_packages_installed(_: ErrorNoPackagesInstalled) -> RenderResult { + let mut r = RenderResult::new(); + eprintln_cargo!(r, "No packages installed"); + r +} + /// Newest first; unparsable versions sort last, compared lexicographically. fn compare_versions(a: &str, b: &str) -> std::cmp::Ordering { match (semver::Version::parse(a), semver::Version::parse(b)) { diff --git a/mingling_cli/src/pkg_mgr/cmd_uninstall.rs b/mingling_cli/src/pkg_mgr/cmd_uninstall.rs index 69c5aa5..988437c 100644 --- a/mingling_cli/src/pkg_mgr/cmd_uninstall.rs +++ b/mingling_cli/src/pkg_mgr/cmd_uninstall.rs @@ -1,32 +1,35 @@ use std::{fs, io, path::PathBuf}; use mingling::{ - Grouped, LazyRes, Routable, ShellContext, Suggest, SuggestItem, - macros::{ - arg, buffer, chain, command, completion, metadata, pack, r_println, renderer, routeify, - }, + LazyRes, RenderResult, Routable, ShellContext, Suggest, SuggestItem, + macros::{arg, chain, command, completion, metadata, pack, pack_err, renderer, routeify}, metadata::Description, picker::{EntryPicker, PickerArg}, }; use crate::{ - Next, + Next, eprintln_cargo, metadata::setup::ResMetadata, pkg_mgr::{ ErrorNoDataDirectory, ErrorPackageSpecInvalid, ErrorRootPackageNotFound, ResPackagesDir, }, + println_cargo, }; /// Optional positional argument: package spec (`name` or `name@version`) pub static ARG_PACKAGE: PickerArg<Option<String>> = arg![Option<String>]; +// Directory names to remove, e.g. `["omg@0.1.0", "omg@0.1.1"]` pack!(StateUninstallPackages = Vec<String>); -#[derive(Debug, Default, Grouped)] -pub struct ResultUninstall { - pub removed: Vec<PathBuf>, - pub not_installed: Vec<PathBuf>, -} +// Directories that were successfully removed. +pack!(ResultPackageUninstalled = Vec<PathBuf>); + +// Directories that were not installed. +pack_err!(ErrorPackageNotInstall = Vec<PathBuf>); + +// No installed package matched the given spec. +pack_err!(ErrorNoMatchingPackages); /// `{data_dir}/.mingling` #[metadata(EntryUninstall)] @@ -96,6 +99,7 @@ pub fn handle_state_uninstall_packages( if packages_dir.as_os_str().is_empty() { return ErrorNoDataDirectory::default().to_chain(); } + let mut removed = Vec::new(); let mut not_installed = Vec::new(); @@ -111,25 +115,38 @@ pub fn handle_state_uninstall_packages( removed.push(dir); } - ResultUninstall { - removed, - not_installed, + if removed.is_empty() && not_installed.is_empty() { + return ErrorNoMatchingPackages::default().to_chain(); + } + if !removed.is_empty() { + return ResultPackageUninstalled::new(removed).to_chain(); } - .to_chain() + ErrorPackageNotInstall::new(not_installed).to_chain() } -#[renderer(buffer)] -pub fn render_result_uninstall(r: ResultUninstall) { - if r.removed.is_empty() && r.not_installed.is_empty() { - r_println!("No matching packages installed"); - } else { - for dir in r.removed { - r_println!("Uninstalled: {}", dir.display()); - } - for dir in r.not_installed { - r_println!("Not installed: {}", dir.display()); - } +#[renderer] +pub fn render_result_package_uninstalled(r: ResultPackageUninstalled) -> RenderResult { + let mut result = RenderResult::new(); + for dir in r.inner { + println_cargo!(result, "Uninstalled: {}", dir.display()); + } + result +} + +#[renderer] +pub fn render_error_package_not_install(err: ErrorPackageNotInstall) -> RenderResult { + let mut result = RenderResult::new(); + for dir in err.info { + eprintln_cargo!(result, "not installed: {}", dir.display()); } + result +} + +#[renderer] +pub fn render_error_no_matching_packages(_: ErrorNoMatchingPackages) -> RenderResult { + let mut result = RenderResult::new(); + eprintln_cargo!(result, "no matching packages installed"); + result } #[completion(EntryUninstall)] diff --git a/mingling_cli/src/proj_mgr.rs b/mingling_cli/src/proj_mgr.rs index e69de29..8b13789 100644 --- a/mingling_cli/src/proj_mgr.rs +++ b/mingling_cli/src/proj_mgr.rs @@ -0,0 +1 @@ + diff --git a/mingling_cli/src/utils.rs b/mingling_cli/src/utils.rs index 8754563..6acf1c6 100644 --- a/mingling_cli/src/utils.rs +++ b/mingling_cli/src/utils.rs @@ -1 +1,2 @@ +pub mod cargo_style; pub mod display; diff --git a/mingling_cli/src/utils/cargo_style.rs b/mingling_cli/src/utils/cargo_style.rs new file mode 100644 index 0000000..8c19c07 --- /dev/null +++ b/mingling_cli/src/utils/cargo_style.rs @@ -0,0 +1,218 @@ +use colored::Colorize; + +/// Formats a message in cargo-style format with a bold green prefix. +/// +/// The message should be in the format `prefix: content`. The prefix will be +/// bold green and right-padded to 12 characters. If there is no colon in the +/// string, the entire string is printed as content with an empty prefix. +/// +/// # Macros +/// +/// - `format_cargo!("prefix: {}", arg)` — format-style invocation +/// - `format_cargo!(expr)` — direct expression invocation +/// +/// # Panics +/// +/// Panics if the prefix (text before the first `:`) exceeds 12 characters. +/// +/// # Examples +/// +/// ```ignore +/// format_cargo!("Compiling: hello.rs"); +/// // Output: " Compiling hello.rs" (green bold "Compiling" padded to 12) +/// ``` +#[macro_export] +macro_rules! format_cargo { + ($fmt:literal, $($arg:tt)*) => { + $crate::utils::cargo_style::get_cargo_info_format(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::utils::cargo_style::get_cargo_info_format($cmd) + }; +} + +/// Formats an error message in cargo-style format with a bold red "error" prefix. +/// +/// # Macros +/// +/// - `eformat_cargo!("prefix: {}", arg)` — format-style invocation +/// - `eformat_cargo!(expr)` — direct expression invocation +/// +/// # Examples +/// +/// ```ignore +/// eformat_cargo!("failed to parse input"); +/// // Output: "error: failed to parse input" (red bold "error") +/// ``` +#[macro_export] +macro_rules! eformat_cargo { + ($fmt:literal, $($arg:tt)*) => { + $crate::utils::cargo_style::get_cargo_error_format(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::utils::cargo_style::get_cargo_error_format($cmd) + }; +} + +/// Formats a help message in cargo-style format with a bright white "help" prefix. +/// +/// # Macros +/// +/// - `hformat_cargo!("prefix: {}", arg)` — format-style invocation +/// - `hformat_cargo!(expr)` — direct expression invocation +/// +/// # Examples +/// +/// ```ignore +/// hformat_cargo!("use --verbose for more info"); +/// // Output: "help: use --verbose for more info" (bright white "help") +/// ``` +#[macro_export] +macro_rules! hformat_cargo { + ($fmt:literal, $($arg:tt)*) => { + $crate::utils::cargo_style::get_cargo_help_format(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::utils::cargo_style::get_cargo_help_format($cmd) + }; +} + +/// Print a message in cargo-style format into a `RenderResult` buffer. +/// +/// The first argument must be the renderer's buffer variable (e.g. +/// `__render_result_buffer` inside a `#[renderer(buffer)]` function). +/// +/// # Examples +/// +/// ```ignore +/// #[renderer(buffer)] +/// fn render_ok(_: ResOk) { +/// println_cargo!(__render_result_buffer, "Compiling: {}", name); +/// } +/// ``` +#[macro_export] +macro_rules! println_cargo { + ($buf:ident, $fmt:literal, $($arg:tt)*) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_info_format(format!($fmt, $($arg)*))) + }; + ($buf:ident, $cmd:expr) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_info_format($cmd)) + }; +} + +/// Print an error message in cargo-style format into a `RenderResult` buffer. +/// +/// # Examples +/// +/// ```ignore +/// #[renderer(buffer)] +/// fn render_err(_: ResErr) { +/// eprintln_cargo!(__render_result_buffer, "failed to parse input"); +/// } +/// ``` +#[macro_export] +macro_rules! eprintln_cargo { + ($buf:ident, $fmt:literal, $($arg:tt)*) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_error_format(format!($fmt, $($arg)*))) + }; + ($buf:ident, $cmd:expr) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_error_format($cmd)) + }; +} + +/// Print a help message in cargo-style format into a `RenderResult` buffer. +/// +/// # Examples +/// +/// ```ignore +/// #[renderer(buffer)] +/// fn render_help(_: ResHelp) { +/// hprintln_cargo!(__render_result_buffer, "use --verbose for more info"); +/// } +/// ``` +#[macro_export] +macro_rules! hprintln_cargo { + ($buf:ident, $fmt:literal, $($arg:tt)*) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_help_format(format!($fmt, $($arg)*))) + }; + ($buf:ident, $cmd:expr) => { + ::mingling::macros::r_println!($buf, "{}", $crate::utils::cargo_style::get_cargo_help_format($cmd)) + }; +} + +/// Format a message in cargo style format, with bold green prefix. +/// +/// The input string is split at the first `:`. The part before the colon becomes +/// the prefix (bold green, right-padded to 12 characters), and the part after +/// becomes the content. +/// +/// If no colon is found, the entire string is treated as content and no prefix +/// is shown. +/// +/// # Panics +/// +/// Panics if the prefix (text before the first `:`) exceeds 12 characters. +/// +/// # Examples +/// +/// ```ignore +/// get_cargo_info_format("Compiling: my_program.rs"); +/// // returns " Compiling my_program.rs" +/// ``` +pub fn get_cargo_info_format(str: impl Into<String>) -> String { + let s = str.into(); + let (prefix, content) = if let Some(pos) = s.find(':') { + ( + s[..pos].trim().to_string(), + s[pos + 1..].trim_start().to_string(), + ) + } else { + (String::new(), s.trim().to_string()) + }; + + assert!( + prefix.len() <= 12, + "prefix length exceeds 12: '{}' has length {}", + prefix, + prefix.len() + ); + + let padding = " ".repeat(12 - prefix.len()); + + format!( + "{}{} {}", + padding, + prefix.bold().bright_green(), + content.trim() + ) +} + +/// Format an error message in cargo style format, with bold red "error" prefix. +/// +/// The input string is printed as the error content, prefixed by a bold red +/// `error:` label. +/// +/// # Examples +/// +/// ```ignore +/// get_cargo_error_format("something went wrong"); +/// // returns "error: something went wrong" +/// ``` +pub fn get_cargo_error_format(str: impl Into<String>) -> String { + format!("{}: {}", "error".bold().bright_red(), str.into()) +} + +/// Format a help message in cargo style format, with bright white "help" prefix. +/// +/// The input string is printed as the help content, prefixed by a bright white +/// `help:` label (not bold). +/// +/// # Examples +/// +/// ```ignore +/// get_cargo_help_format("use --verbose for more info"); +/// // returns "help: use --verbose for more info" +/// ``` +pub fn get_cargo_help_format(str: impl Into<String>) -> String { + format!("{}: {}", "help".bright_white(), str.into()) +} |
