aboutsummaryrefslogtreecommitdiff
path: root/mingling_cli
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_cli')
-rw-r--r--mingling_cli/src/errors.rs1
-rw-r--r--mingling_cli/src/linter/cmd_explain.rs33
-rw-r--r--mingling_cli/src/pkg_mgr.rs45
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_install.rs35
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_disable.rs26
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_enable.rs25
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_pkg_show.rs27
-rw-r--r--mingling_cli/src/pkg_mgr/cmd_uninstall.rs67
-rw-r--r--mingling_cli/src/proj_mgr.rs1
-rw-r--r--mingling_cli/src/utils.rs1
-rw-r--r--mingling_cli/src/utils/cargo_style.rs218
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())
+}