aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dev_tools/Cargo.toml7
-rw-r--r--dev_tools/src/bin/ci.rs16
-rw-r--r--dev_tools/src/bin/docsify-sidebar-gen.rs16
-rw-r--r--dev_tools/src/bin/refresh-docs.rs8
-rw-r--r--dev_tools/src/bin/refresh-feature-mod.rs2
-rw-r--r--dev_tools/src/bin/test-examples.rs4
-rw-r--r--dev_tools/src/lib.rs30
-rw-r--r--examples/example-argument-parse/src/main.rs6
-rw-r--r--examples/example-async-support/src/main.rs1
-rw-r--r--examples/example-basic/src/main.rs1
-rw-r--r--examples/example-clap-binding/src/main.rs4
-rw-r--r--examples/example-completion/src/main.rs1
-rw-r--r--examples/example-custom-pickable/src/main.rs10
-rw-r--r--examples/example-dispatch-tree/src/main.rs3
-rw-r--r--examples/example-enum-tag/src/main.rs1
-rw-r--r--examples/example-error-handling/src/main.rs5
-rw-r--r--examples/example-exitcode/src/main.rs2
-rw-r--r--examples/example-general-renderer/src/main.rs6
-rw-r--r--examples/example-hook/src/main.rs9
-rw-r--r--examples/example-panic-unwind/src/main.rs6
-rw-r--r--examples/example-repl-basic/src/main.rs17
-rw-r--r--examples/example-resources/src/main.rs1
-rw-r--r--examples/example-setup/src/main.rs2
-rw-r--r--examples/example-unit-test/src/main.rs5
-rw-r--r--mingling/src/example_docs.rs80
-rw-r--r--mingling/src/lib.rs10
-rw-r--r--mingling/src/parser/args.rs40
-rw-r--r--mingling/src/parser/picker.rs60
-rw-r--r--mingling/src/parser/picker/bools.rs4
-rw-r--r--mingling/src/parser/picker/builtin.rs4
-rw-r--r--mingling/src/parser/picker/path.rs2
-rw-r--r--mingling/src/parser/picker/path/rule.rs12
-rw-r--r--mingling/src/res/exit_code.rs1
-rw-r--r--mingling/src/setups/exit_code.rs2
-rw-r--r--mingling/src/setups/general_renderer.rs2
-rw-r--r--mingling_core/Cargo.toml4
-rw-r--r--mingling_core/src/any.rs18
-rw-r--r--mingling_core/src/asset/chain.rs2
-rw-r--r--mingling_core/src/asset/chain/error.rs10
-rw-r--r--mingling_core/src/asset/dispatcher.rs4
-rw-r--r--mingling_core/src/asset/enum_tag.rs2
-rw-r--r--mingling_core/src/asset/global_resource.rs19
-rw-r--r--mingling_core/src/asset/node.rs1
-rw-r--r--mingling_core/src/asset/renderer.rs2
-rw-r--r--mingling_core/src/builds/comp.rs6
-rw-r--r--mingling_core/src/comp.rs12
-rw-r--r--mingling_core/src/comp/flags.rs2
-rw-r--r--mingling_core/src/comp/shell_ctx.rs12
-rw-r--r--mingling_core/src/comp/suggest.rs23
-rw-r--r--mingling_core/src/program.rs19
-rw-r--r--mingling_core/src/program/collection.rs21
-rw-r--r--mingling_core/src/program/config.rs2
-rw-r--r--mingling_core/src/program/error.rs5
-rw-r--r--mingling_core/src/program/exec.rs35
-rw-r--r--mingling_core/src/program/exec/error.rs23
-rw-r--r--mingling_core/src/program/flag.rs2
-rw-r--r--mingling_core/src/program/hook.rs42
-rw-r--r--mingling_core/src/program/once_exec.rs31
-rw-r--r--mingling_core/src/program/repl_exec.rs6
-rw-r--r--mingling_core/src/program/single_instance.rs5
-rw-r--r--mingling_core/src/renderer/general.rs34
-rw-r--r--mingling_core/src/renderer/general/error.rs1
-rw-r--r--mingling_core/src/tester/chain_process_tester.rs6
-rw-r--r--mingling_macros/Cargo.toml4
-rw-r--r--mingling_macros/src/chain.rs8
-rw-r--r--mingling_macros/src/dispatch_tree_gen.rs14
-rw-r--r--mingling_macros/src/dispatcher.rs3
-rw-r--r--mingling_macros/src/entry.rs23
-rw-r--r--mingling_macros/src/enum_tag.rs15
-rw-r--r--mingling_macros/src/groupped.rs22
-rw-r--r--mingling_macros/src/lib.rs24
-rw-r--r--mingling_macros/src/pack.rs1
-rw-r--r--mingling_macros/src/renderer.rs41
-rw-r--r--mingling_macros/src/res_injection.rs3
-rw-r--r--mling/Cargo.toml4
-rw-r--r--mling/src/cli.rs7
-rw-r--r--mling/src/cli/list.rs24
-rw-r--r--mling/src/cli/read.rs9
-rw-r--r--mling/src/display.rs44
-rw-r--r--mling/src/namespace_manager.rs24
-rw-r--r--mling/src/project_installer.rs25
-rw-r--r--mling/src/project_solver.rs27
82 files changed, 651 insertions, 400 deletions
diff --git a/dev_tools/Cargo.toml b/dev_tools/Cargo.toml
index 56f89d3..280a50c 100644
--- a/dev_tools/Cargo.toml
+++ b/dev_tools/Cargo.toml
@@ -2,6 +2,13 @@
name = "tools"
version = "0.1.0"
edition = "2024"
+authors = ["Weicao-CatilGrass"]
+description = "Development tools for mingling"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/catilgrass/mingling"
+readme = "../README.md"
+keywords = ["cli", "development", "tools"]
+categories = ["command-line-interface", "development-tools"]
[dependencies]
just_template = "0.1.3"
diff --git a/dev_tools/src/bin/ci.rs b/dev_tools/src/bin/ci.rs
index 3169b95..86b930c 100644
--- a/dev_tools/src/bin/ci.rs
+++ b/dev_tools/src/bin/ci.rs
@@ -1,4 +1,4 @@
-use std::io::Write;
+use std::io::Write as _;
use std::process::exit;
use tools::{cargo_tomls, eprintln_cargo_style, println_cargo_style, run_cmd};
@@ -26,7 +26,7 @@ fn main() {
}
if let Err(exit_code) = ci() {
- restore_workspace().unwrap();
+ restore_workspace(needs_commit_temp).unwrap();
exit(exit_code)
}
@@ -39,7 +39,7 @@ fn main() {
let _ = run_cmd!("git status");
if needs_commit_temp {
- restore_workspace().unwrap();
+ restore_workspace(true).unwrap();
}
exit(1)
}
@@ -47,14 +47,16 @@ fn main() {
println_cargo_style!("Done: All check passed!");
if needs_commit_temp {
- restore_workspace().unwrap();
+ restore_workspace(true).unwrap();
}
}
-fn restore_workspace() -> Result<(), i32> {
+fn restore_workspace(undo_commit: bool) -> Result<(), i32> {
run_cmd!("git reset --hard --quiet")?;
- run_cmd!("git reset --soft HEAD~1 --quiet")?;
- run_cmd!("git reset --quiet")?;
+ if undo_commit {
+ run_cmd!("git reset --soft HEAD~1 --quiet")?;
+ run_cmd!("git reset --quiet")?;
+ }
Ok(())
}
diff --git a/dev_tools/src/bin/docsify-sidebar-gen.rs b/dev_tools/src/bin/docsify-sidebar-gen.rs
index e0f9370..ccd2641 100644
--- a/dev_tools/src/bin/docsify-sidebar-gen.rs
+++ b/dev_tools/src/bin/docsify-sidebar-gen.rs
@@ -1,4 +1,5 @@
use std::collections::BTreeMap;
+use std::fmt::Write;
use std::path::Path;
use tools::println_cargo_style;
@@ -56,7 +57,7 @@ fn gen_translation_sidebars() {
}
}
-/// Build sidebar content: scan .md files in pages_dir and return a formatted sidebar string
+/// Build sidebar content: scan .md files in `pages_dir` and return a formatted sidebar string
fn build_sidebar_content(base_dir: &Path, pages_dir: &Path, sidebar_head: &str) -> String {
let mut lines = String::from(sidebar_head);
@@ -95,7 +96,7 @@ fn build_sidebar_content(base_dir: &Path, pages_dir: &Path, sidebar_head: &str)
// Append root-level files
for f in &root_files {
- lines.push_str(&format!("* [{}]({})\n", f.title, f.link));
+ let _ = writeln!(lines, "* [{}]({})", f.title, f.link);
}
// Append subdirectory groups
@@ -104,9 +105,9 @@ fn build_sidebar_content(base_dir: &Path, pages_dir: &Path, sidebar_head: &str)
sorted_entries.sort_by(|a, b| a.link.cmp(&b.link));
// Directory header with 2-space indent
- lines.push_str(&format!("* {}\n", dir_name));
+ let _ = writeln!(lines, "* {dir_name}");
for f in &sorted_entries {
- lines.push_str(&format!(" * [{}]({})\n", f.title, f.link));
+ let _ = writeln!(lines, " * [{}]({})", f.title, f.link);
}
}
@@ -160,9 +161,10 @@ fn extract_title(path: &Path) -> String {
}
}
// Fallback: use file stem
- path.file_stem()
- .map(|s| s.to_string_lossy().to_string())
- .unwrap_or_else(|| "Untitled".to_string())
+ path.file_stem().map_or_else(
+ || "Untitled".to_string(),
+ |s| s.to_string_lossy().to_string(),
+ )
}
fn find_git_repo() -> Option<std::path::PathBuf> {
diff --git a/dev_tools/src/bin/refresh-docs.rs b/dev_tools/src/bin/refresh-docs.rs
index ffa80a2..71143d1 100644
--- a/dev_tools/src/bin/refresh-docs.rs
+++ b/dev_tools/src/bin/refresh-docs.rs
@@ -49,7 +49,7 @@ fn gen_example_doc_module() {
let template_str = template.to_string();
let template_str = template_str
.lines()
- .map(|line| line.trim_end())
+ .map(str::trim_end)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
@@ -91,19 +91,19 @@ impl ExampleContent {
let cargo_toml = cargo_toml
.lines()
- .map(|line| format!("/// {}", line))
+ .map(|line| format!("/// {line}"))
.collect::<Vec<_>>()
.join("\n");
let header = header
.lines()
- .map(|line| format!("/// {}", line))
+ .map(|line| format!("/// {line}"))
.collect::<Vec<_>>()
.join("\n");
let code = code
.lines()
- .map(|line| format!("/// {}", line))
+ .map(|line| format!("/// {line}"))
.collect::<Vec<_>>()
.join("\n");
diff --git a/dev_tools/src/bin/refresh-feature-mod.rs b/dev_tools/src/bin/refresh-feature-mod.rs
index 6265e15..2255dbc 100644
--- a/dev_tools/src/bin/refresh-feature-mod.rs
+++ b/dev_tools/src/bin/refresh-feature-mod.rs
@@ -41,7 +41,7 @@ fn gen_feature_module() {
let template_str = template.to_string();
let template_str = template_str
.lines()
- .map(|line| line.trim_end())
+ .map(str::trim_end)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
diff --git a/dev_tools/src/bin/test-examples.rs b/dev_tools/src/bin/test-examples.rs
index 21abaef..ddf5f7c 100644
--- a/dev_tools/src/bin/test-examples.rs
+++ b/dev_tools/src/bin/test-examples.rs
@@ -75,7 +75,7 @@ fn run_all_tests(config: &TestConfig) -> (usize, usize) {
/// Build the example binary, return true on success
fn build_example(example_name: &str) -> bool {
- let manifest = format!("examples/{}/Cargo.toml", example_name);
+ let manifest = format!("examples/{example_name}/Cargo.toml");
run_cmd!("cargo build --manifest-path {}", manifest).is_ok()
}
@@ -132,7 +132,7 @@ fn run_single_test(example_name: &str, test_case: &TestCase) -> bool {
fn get_binary_name(example_name: &str) -> String {
let base = example_name;
if cfg!(target_os = "windows") {
- format!("{}.exe", base)
+ format!("{base}.exe")
} else {
base.to_string()
}
diff --git a/dev_tools/src/lib.rs b/dev_tools/src/lib.rs
index 1e62a2d..59eed0a 100644
--- a/dev_tools/src/lib.rs
+++ b/dev_tools/src/lib.rs
@@ -30,6 +30,11 @@ macro_rules! eprintln_cargo_style {
};
}
+/// Print a message in cargo style format, with bold green prefix.
+///
+/// # Panics
+///
+/// Panics if the prefix (text before the first `:`) exceeds 12 characters.
pub fn println_cargo_style(str: impl Into<String>) {
let s = str.into();
let (prefix, content) = if let Some(pos) = s.find(':') {
@@ -38,16 +43,15 @@ pub fn println_cargo_style(str: impl Into<String>) {
s[pos + 1..].trim_start().to_string(),
)
} else {
- ("".to_string(), s.trim().to_string())
+ (String::new(), s.trim().to_string())
};
- if prefix.len() > 12 {
- panic!(
- "prefix length exceeds 12: '{}' has length {}",
- prefix,
- prefix.len()
- );
- }
+ assert!(
+ prefix.len() <= 12,
+ "prefix length exceeds 12: '{}' has length {}",
+ prefix,
+ prefix.len()
+ );
let padding = " ".repeat(12 - prefix.len());
@@ -63,6 +67,15 @@ pub fn eprintln_cargo_style(str: impl Into<String>) {
println!("{}: {}", "error".bold().bright_red(), str.into());
}
+/// Run a shell command and return its exit status.
+///
+/// # Panics
+///
+/// Panics if the shell command cannot be spawned (e.g. the shell binary is not found).
+///
+/// # Errors
+///
+/// Returns `Err` with the exit code if the command finishes with a non-zero exit code.
pub fn run_cmd(cmd: impl Into<String>) -> Result<(), i32> {
let shell = if cfg!(target_os = "windows") {
"powershell"
@@ -84,6 +97,7 @@ pub fn run_cmd(cmd: impl Into<String>) -> Result<(), i32> {
}
}
+#[must_use]
pub fn cargo_tomls() -> Vec<std::path::PathBuf> {
let mut cargo_tomls = Vec::new();
let mut dirs = vec![std::path::PathBuf::from(".")];
diff --git a/examples/example-argument-parse/src/main.rs b/examples/example-argument-parse/src/main.rs
index f63fdad..316e52f 100644
--- a/examples/example-argument-parse/src/main.rs
+++ b/examples/example-argument-parse/src/main.rs
@@ -39,7 +39,7 @@ fn handle_transfer_parse(args: EntryTransfer) -> Next {
// Name
// ^^^^_ finally, pick positional arg
.pick::<String>(())
- .after(|str| str.trim().replace(" ", ""))
+ .after(|str| str.trim().replace(' ', ""))
// Unpack to tuple (is_dir, size, name)
.unpack()
// Convert into ResultFile
@@ -60,7 +60,7 @@ fn handle_strict_transfer_parse(args: EntryStrictTransfer) -> Next {
.pick_or::<usize>("--size", 1024 * 1024_usize)
// Finally parse the positional argument; if not found, route to `ErrorNoNameProvided`
.pick_or_route::<String, _>((), ErrorNoNameProvided::default().to_chain())
- .after(|str| str.trim().replace(" ", ""))
+ .after(|str| str.trim().replace(' ', ""))
.unpack()
}
// Convert into ResultFile
@@ -69,6 +69,7 @@ fn handle_strict_transfer_parse(args: EntryStrictTransfer) -> Next {
result.to_chain()
}
+/// Renders the parsed transfer result (file/dir, size, name).
#[renderer]
fn render_result_file(result: ResultFile) {
let (is_dir, size, name) = result.into();
@@ -80,6 +81,7 @@ fn render_result_file(result: ResultFile) {
)
}
+/// Renders the error when no name is provided.
#[renderer]
fn render_error_no_name_provided(_: ErrorNoNameProvided) {
r_println!("Error: name is not provided")
diff --git a/examples/example-async-support/src/main.rs b/examples/example-async-support/src/main.rs
index 12b1b9c..5ded5e5 100644
--- a/examples/example-async-support/src/main.rs
+++ b/examples/example-async-support/src/main.rs
@@ -53,6 +53,7 @@ pub async fn handle_download(args: EntryDownload) -> Next {
fake_download(file_name).await
}
+/// Renders the downloaded file name.
#[renderer]
// But renderers cannot use the `async` keyword
pub fn render_downloaded(result: ResultDownloaded) {
diff --git a/examples/example-basic/src/main.rs b/examples/example-basic/src/main.rs
index d741c3b..3d94a4b 100644
--- a/examples/example-basic/src/main.rs
+++ b/examples/example-basic/src/main.rs
@@ -58,6 +58,7 @@ fn handle_greet(args: EntryGreet) -> Next {
}
// Define renderer `render_name`, used to render `ResultName`
+/// Renders the greeting message with the provided name.
#[renderer]
fn render_name(name: ResultName) {
r_println!("Hello, {}!", *name);
diff --git a/examples/example-clap-binding/src/main.rs b/examples/example-clap-binding/src/main.rs
index d3ad573..19d794b 100644
--- a/examples/example-clap-binding/src/main.rs
+++ b/examples/example-clap-binding/src/main.rs
@@ -87,6 +87,7 @@ pub struct EntryGreet {
repeat: i32,
}
+/// Renders the greet output with optional repetition.
#[renderer]
fn render_greet(greet: EntryGreet) {
let name = greet.name;
@@ -102,9 +103,10 @@ fn render_greet(greet: EntryGreet) {
r_println!("!");
}
+/// Renders the error message when greet argument parsing fails.
#[renderer]
fn render_greet_parse_failed(err: ErrorGreetParsed) {
- r_println!("{}", err.to_string());
+ r_println!("{}", *err);
}
gen_program!();
diff --git a/examples/example-completion/src/main.rs b/examples/example-completion/src/main.rs
index a34ccab..0c64d03 100644
--- a/examples/example-completion/src/main.rs
+++ b/examples/example-completion/src/main.rs
@@ -116,6 +116,7 @@ fn handle_greet(args: EntryGreet) -> Next {
result
}
+/// Renders the greeting with the result name and repeat count.
#[renderer]
fn render_name(result: ResultName) {
let (repeat, name) = result.inner;
diff --git a/examples/example-custom-pickable/src/main.rs b/examples/example-custom-pickable/src/main.rs
index 466ae43..a2aa662 100644
--- a/examples/example-custom-pickable/src/main.rs
+++ b/examples/example-custom-pickable/src/main.rs
@@ -14,7 +14,7 @@
//! Failed to parse address
//! ```
-use mingling::{Groupped, macros::route, parser::Pickable, prelude::*};
+use mingling::{macros::route, parser::Pickable, prelude::*, Groupped};
// Define types that can be recognized by Mingling
// ________________________ `Pickable` trait needs to implement Default
@@ -32,7 +32,7 @@ impl Pickable for Address {
type Output = Address;
fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> {
// Extract the raw string from Argument using the Flag
- let raw: String = args.pick_argument(flag)?.to_string();
+ let raw: String = args.pick_argument(flag)?.clone();
// Use TryFrom to parse the address
Address::try_from(raw).ok()
@@ -50,11 +50,13 @@ fn handle_connect(prev: EntryConnect) -> Next {
connect.to_chain()
}
+/// Renders the connected address.
#[renderer]
fn render_address(addr: Address) {
r_println!("Connected to \"{}\"", addr.to_string());
}
+/// Renders the error message when address parsing fails.
#[renderer]
fn render_error_parse_address_failed(_: ErrorParseAddressFailed) {
r_println!("Failed to parse address");
@@ -93,13 +95,13 @@ impl TryFrom<String> for Address {
for (i, part) in ip_parts.iter().enumerate() {
ip[i] = part
.parse::<u8>()
- .map_err(|_| format!("Invalid IP octet: {}", part))?;
+ .map_err(|_| format!("Invalid IP octet: {part}"))?;
}
// Parse port
let port = port_str
.parse::<u16>()
- .map_err(|_| format!("Invalid port: {}", port_str))?;
+ .map_err(|_| format!("Invalid port: {port_str}"))?;
Ok(Address { ip, port })
}
diff --git a/examples/example-dispatch-tree/src/main.rs b/examples/example-dispatch-tree/src/main.rs
index 08714d1..e087b1e 100644
--- a/examples/example-dispatch-tree/src/main.rs
+++ b/examples/example-dispatch-tree/src/main.rs
@@ -48,9 +48,10 @@ fn main() {
// // it'll be collected automatically once the `dispatch_tree` feature is enabled
// program.with_dispatcher(...);
- program.exec_and_exit()
+ program.exec_and_exit();
}
+/// Renders the confirmation message for the `cmd5` command.
#[renderer]
fn render_cmd5(_: Entry5) {
r_println!("It's works!");
diff --git a/examples/example-enum-tag/src/main.rs b/examples/example-enum-tag/src/main.rs
index 30439f5..ad9db82 100644
--- a/examples/example-enum-tag/src/main.rs
+++ b/examples/example-enum-tag/src/main.rs
@@ -81,6 +81,7 @@ fn handle_language_selection(args: EntryLanguageSelection) -> Next {
lang
}
+/// Renders the selected programming language with its name and description.
#[renderer]
fn render_programming_language(lang: ProgrammingLanguages) {
// You can use `enum_info()` to get the name and description of the current enum
diff --git a/examples/example-error-handling/src/main.rs b/examples/example-error-handling/src/main.rs
index d4d073e..ae79b97 100644
--- a/examples/example-error-handling/src/main.rs
+++ b/examples/example-error-handling/src/main.rs
@@ -59,29 +59,34 @@ fn handle_hello(args: EntryHello) -> Next {
ResultName::new(name).to_render()
}
+/// Renders a successful greeting with the given name.
#[renderer]
fn render_result_name(name: ResultName) {
r_println!("Hello, {}", *name);
}
+/// Renders the error when no name is provided.
#[renderer]
fn render_error_no_name_provided(_: ErrorNoNameProvided) {
// Prompt when no name is provided
r_println!("No name provided");
}
+/// Renders the error when the name is already taken.
#[renderer]
fn render_error_name_not_available(_: ErrorNameNotAvailable) {
// Prompt when name is already taken
r_println!("Name not available");
}
+/// Renders the error when the name exceeds the maximum length.
#[renderer]
fn render_error_name_too_long(len: ErrorNameTooLong) {
// Prompt when name is too long, showing actual length
r_println!("Name too long: {} > 10", *len);
}
+/// Renders the error when the dispatcher (subcommand) is not found.
#[renderer]
fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
// Prompt when command is not found, showing the input command
diff --git a/examples/example-exitcode/src/main.rs b/examples/example-exitcode/src/main.rs
index 178fa78..e1f60ee 100644
--- a/examples/example-exitcode/src/main.rs
+++ b/examples/example-exitcode/src/main.rs
@@ -44,6 +44,7 @@ fn handle_hello(args: EntryHello) -> Next {
ResultName::new(name).to_render()
}
+/// Renders a successful greeting with the given name.
#[renderer]
fn render_result_name(name: ResultName) {
r_println!("Hello, {}", *name);
@@ -51,6 +52,7 @@ fn render_result_name(name: ResultName) {
// Define renderer, render error message _____________ Inject exit code resource
// /
+/// Renders the error when no name is provided |
#[renderer] // vvvvvvvvvvvvv
fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ExitCode) {
ec.exit_code = 1;
diff --git a/examples/example-general-renderer/src/main.rs b/examples/example-general-renderer/src/main.rs
index 3ba4433..c923d28 100644
--- a/examples/example-general-renderer/src/main.rs
+++ b/examples/example-general-renderer/src/main.rs
@@ -18,7 +18,7 @@
//! ```
use mingling::prelude::*;
-use mingling::{Groupped, parser::Picker, setup::GeneralRendererSetup};
+use mingling::{parser::Picker, setup::GeneralRendererSetup, Groupped};
use serde::Serialize;
dispatcher!("render", CMDRender => EntryRender);
@@ -28,7 +28,7 @@ fn main() {
// Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters
program.with_setup(GeneralRendererSetup);
program.with_dispatcher(CMDRender);
- program.exec();
+ let _ = program.exec();
}
// --------- IMPORTANT ---------
@@ -61,7 +61,7 @@ fn parse_render(prev: EntryRender) -> Next {
Info { name, age }.to_render()
}
-// Implement default renderer for when general_renderer is not specified
+/// Implement default renderer for when general_renderer is not specified
#[renderer]
fn render_info(prev: Info) {
r_println!("{} is {} years old", prev.name, prev.age);
diff --git a/examples/example-hook/src/main.rs b/examples/example-hook/src/main.rs
index 373c76d..d6a2dd0 100644
--- a/examples/example-hook/src/main.rs
+++ b/examples/example-hook/src/main.rs
@@ -31,17 +31,17 @@ fn main() {
program.with_hook(
ProgramHook::<ThisProgram>::empty()
.on_begin(|| println!("[DEBUG] Program is begin"))
- .on_pre_dispatch(|args| println!("[DEBUG] Pre dispatch: {:?}", args))
- .on_post_dispatch(|c: &_| println!("[DEBUG] Post dispatch: {:?}", c))
+ .on_pre_dispatch(|args| println!("[DEBUG] Pre dispatch: {args:?}"))
+ .on_post_dispatch(|c: &_| println!("[DEBUG] Post dispatch: {c:?}"))
.on_pre_chain(|c: &_, _| {
- println!("[DEBUG] Pre chain: {}", c);
+ println!("[DEBUG] Pre chain: {c}");
})
.on_post_chain(|any_output| println!("[DEBUG] Post chain: {}", any_output.member_id))
.on_finish(|| {
println!("[DEBUG] Loop end");
0 // Override exit code
})
- .on_pre_render(|c: &_, _| println!("[DEBUG] Pre render: {}", c))
+ .on_pre_render(|c: &_, _| println!("[DEBUG] Pre render: {c}"))
.on_post_render(|_| println!("[DEBUG] Post render")),
);
// --------- IMPORTANT ---------
@@ -63,6 +63,7 @@ fn handle_greet(args: EntryGreet) -> Next {
name
}
+/// Renders the greeting message with the provided name.
#[renderer]
fn render_name(name: ResultName) {
r_println!("Hello, {}!", *name);
diff --git a/examples/example-panic-unwind/src/main.rs b/examples/example-panic-unwind/src/main.rs
index 18cf4d6..bb25541 100644
--- a/examples/example-panic-unwind/src/main.rs
+++ b/examples/example-panic-unwind/src/main.rs
@@ -29,11 +29,10 @@ fn main() {
program.stdout_setting.silence_panic = true;
// Define a hook to output &ProgramPanic when a Panic occurs
- program
- .with_hook(ProgramHook::empty().on_exec_panic(|info| println!("Program panic: {}", info)));
+ program.with_hook(ProgramHook::empty().on_exec_panic(|info| println!("Program panic: {info}")));
// --------- IMPORTANT ---------
- program.exec();
+ let _ = program.exec();
}
#[chain]
@@ -48,6 +47,7 @@ fn handle_panic(prev: EntryPanic) -> Next {
}
}
+/// Renders the message when no panic occurs.
#[renderer]
fn render(_: NotPanic) {
r_println!("Program not panic");
diff --git a/examples/example-repl-basic/src/main.rs b/examples/example-repl-basic/src/main.rs
index 630c419..d44c92a 100644
--- a/examples/example-repl-basic/src/main.rs
+++ b/examples/example-repl-basic/src/main.rs
@@ -66,7 +66,7 @@ fn main() {
// Add hooks to handle REPL-related events
program.with_hook(ProgramHook::empty().on_repl_begin(|| {
// Print welcome message
- println!("Welcome!")
+ println!("Welcome!");
}));
// Start the REPL loop
@@ -118,11 +118,11 @@ fn handle_ls(_prev: EntryLs, current_dir: &ResCurrentDir) -> Next {
let dir = &current_dir.dir;
let entries: Vec<String> = std::fs::read_dir(dir)
.into_iter()
- .flat_map(|rd| rd.filter_map(|e| e.ok()))
+ .flat_map(|rd| rd.filter_map(std::result::Result::ok))
.map(|e| {
let name = e.file_name().to_string_lossy().to_string();
if e.file_type().map(|t| t.is_dir()).unwrap_or(false) {
- format!("{}/", name)
+ format!("{name}/")
} else {
name
}
@@ -133,11 +133,11 @@ fn handle_ls(_prev: EntryLs, current_dir: &ResCurrentDir) -> Next {
ResultList::new(entries).to_render()
}
-// Render ResultList data
+/// Render ResultList data
#[renderer]
fn render_list(list: ResultList) {
for item in list.inner {
- r_println!("{}", item)
+ r_println!("{}", item);
}
}
@@ -151,20 +151,21 @@ fn handle_exit(
repl.exit = true;
}
-// Handle clear command event
+/// Handle clear command event
#[chain]
fn handle_clear(_prev: EntryClear) {
// Clear the terminal screen
print!("\x1B[2J\x1B[1;1H");
}
-// Handle path not found event
+/// Handle path not found event
#[renderer]
fn render_error_directory_not_exist(err: ErrorDirectoryNotExist) {
r_println!("Directory not found: {}", err.inner.display())
}
-// Handle dispatcher not found event
+/// Handle dispatcher not found event
+/// Renders the error when a command is not found.
#[renderer]
fn dispatcher_not_found(prev: ErrorDispatcherNotFound) {
r_println!("Command not found: \"{}\"", prev.join(", "))
diff --git a/examples/example-resources/src/main.rs b/examples/example-resources/src/main.rs
index ae2c8f2..6c5bf84 100644
--- a/examples/example-resources/src/main.rs
+++ b/examples/example-resources/src/main.rs
@@ -56,6 +56,7 @@ fn render_modify_current(args: EntryModifyCurrent, current_dir: &mut ResCurrentD
// Define renderer for output current path _____________ Injected resource
// /
+/// Renders the current directory path. |
#[renderer] // vvvvvvvvvvvvvv
fn render_current(_: EntryCurrent, current_dir: &ResCurrentDir) {
r_println!("Current directory: {}", current_dir.current_dir.display());
diff --git a/examples/example-setup/src/main.rs b/examples/example-setup/src/main.rs
index c445276..cd432dd 100644
--- a/examples/example-setup/src/main.rs
+++ b/examples/example-setup/src/main.rs
@@ -2,7 +2,7 @@
//!
//! > This example demonstrates how to build a custom Setup for modular management of project components
-use mingling::{Program, macros::program_setup, prelude::*};
+use mingling::{macros::program_setup, prelude::*, Program};
fn main() {
let mut program = ThisProgram::new();
diff --git a/examples/example-unit-test/src/main.rs b/examples/example-unit-test/src/main.rs
index 62c7fbf..7808d0d 100644
--- a/examples/example-unit-test/src/main.rs
+++ b/examples/example-unit-test/src/main.rs
@@ -89,26 +89,31 @@ fn handle_hello(args: EntryHello) -> Next {
ResultName::new(name).to_render()
}
+/// Renders a successful greeting with the given name.
#[renderer]
fn render_result_name(name: ResultName) -> String {
r_println!("Hello, {}!", *name);
}
+/// Renders the error when no name is provided.
#[renderer]
fn render_error_no_name_provided(_: ErrorNoNameProvided) -> String {
r_println!("No name provided");
}
+/// Renders the error when the name is already taken.
#[renderer]
fn render_error_name_not_available(_: ErrorNameNotAvailable) -> String {
r_println!("Name not available");
}
+/// Renders the error when the name exceeds the maximum length.
#[renderer]
fn render_error_name_too_long(len: ErrorNameTooLong) -> String {
r_println!("Name too long: {} > 10", *len);
}
+/// Renders the error when the dispatcher (subcommand) is not found.
#[renderer]
fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
r_println!("Command not found: \"{}\"", err.inner.join(" "));
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
index b665485..7e87d9c 100644
--- a/mingling/src/example_docs.rs
+++ b/mingling/src/example_docs.rs
@@ -57,7 +57,7 @@
/// // Name
/// // ^^^^_ finally, pick positional arg
/// .pick::<String>(())
-/// .after(|str| str.trim().replace(" ", ""))
+/// .after(|str| str.trim().replace(' ', ""))
/// // Unpack to tuple (is_dir, size, name)
/// .unpack()
/// // Convert into ResultFile
@@ -78,7 +78,7 @@
/// .pick_or::<usize>("--size", 1024 * 1024_usize)
/// // Finally parse the positional argument; if not found, route to `ErrorNoNameProvided`
/// .pick_or_route::<String, _>((), ErrorNoNameProvided::default().to_chain())
-/// .after(|str| str.trim().replace(" ", ""))
+/// .after(|str| str.trim().replace(' ', ""))
/// .unpack()
/// }
/// // Convert into ResultFile
@@ -87,6 +87,7 @@
/// result.to_chain()
/// }
///
+/// /// Renders the parsed transfer result (file/dir, size, name).
/// #[renderer]
/// fn render_result_file(result: ResultFile) {
/// let (is_dir, size, name) = result.into();
@@ -98,6 +99,7 @@
/// )
/// }
///
+/// /// Renders the error when no name is provided.
/// #[renderer]
/// fn render_error_no_name_provided(_: ErrorNoNameProvided) {
/// r_println!("Error: name is not provided")
@@ -189,6 +191,7 @@ pub mod example_argument_parse {}
/// fake_download(file_name).await
/// }
///
+/// /// Renders the downloaded file name.
/// #[renderer]
/// // But renderers cannot use the `async` keyword
/// pub fn render_downloaded(result: ResultDownloaded) {
@@ -277,6 +280,7 @@ pub mod example_async_support {}
/// }
///
/// // Define renderer `render_name`, used to render `ResultName`
+/// /// Renders the greeting message with the provided name.
/// #[renderer]
/// fn render_name(name: ResultName) {
/// r_println!("Hello, {}!", *name);
@@ -403,6 +407,7 @@ pub mod example_basic {}
/// repeat: i32,
/// }
///
+/// /// Renders the greet output with optional repetition.
/// #[renderer]
/// fn render_greet(greet: EntryGreet) {
/// let name = greet.name;
@@ -418,9 +423,10 @@ pub mod example_basic {}
/// r_println!("!");
/// }
///
+/// /// Renders the error message when greet argument parsing fails.
/// #[renderer]
/// fn render_greet_parse_failed(err: ErrorGreetParsed) {
-/// r_println!("{}", err.to_string());
+/// r_println!("{}", *err);
/// }
///
/// gen_program!();
@@ -575,6 +581,7 @@ pub mod example_clap_binding {}
/// result
/// }
///
+/// /// Renders the greeting with the result name and repeat count.
/// #[renderer]
/// fn render_name(result: ResultName) {
/// let (repeat, name) = result.inner;
@@ -619,7 +626,7 @@ pub mod example_completion {}
///
/// Source code (./src/main.rs)
/// ```ignore
-/// use mingling::{Groupped, macros::route, parser::Pickable, prelude::*};
+/// use mingling::{macros::route, parser::Pickable, prelude::*, Groupped};
///
/// // Define types that can be recognized by Mingling
/// // ________________________ `Pickable` trait needs to implement Default
@@ -637,7 +644,7 @@ pub mod example_completion {}
/// type Output = Address;
/// fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> {
/// // Extract the raw string from Argument using the Flag
-/// let raw: String = args.pick_argument(flag)?.to_string();
+/// let raw: String = args.pick_argument(flag)?.clone();
///
/// // Use TryFrom to parse the address
/// Address::try_from(raw).ok()
@@ -655,11 +662,13 @@ pub mod example_completion {}
/// connect.to_chain()
/// }
///
+/// /// Renders the connected address.
/// #[renderer]
/// fn render_address(addr: Address) {
/// r_println!("Connected to \"{}\"", addr.to_string());
/// }
///
+/// /// Renders the error message when address parsing fails.
/// #[renderer]
/// fn render_error_parse_address_failed(_: ErrorParseAddressFailed) {
/// r_println!("Failed to parse address");
@@ -698,13 +707,13 @@ pub mod example_completion {}
/// for (i, part) in ip_parts.iter().enumerate() {
/// ip[i] = part
/// .parse::<u8>()
-/// .map_err(|_| format!("Invalid IP octet: {}", part))?;
+/// .map_err(|_| format!("Invalid IP octet: {part}"))?;
/// }
///
/// // Parse port
/// let port = port_str
/// .parse::<u16>()
-/// .map_err(|_| format!("Invalid port: {}", port_str))?;
+/// .map_err(|_| format!("Invalid port: {port_str}"))?;
///
/// Ok(Address { ip, port })
/// }
@@ -796,9 +805,10 @@ pub mod example_custom_pickable {}
/// // // it'll be collected automatically once the `dispatch_tree` feature is enabled
/// // program.with_dispatcher(...);
///
-/// program.exec_and_exit()
+/// program.exec_and_exit();
/// }
///
+/// /// Renders the confirmation message for the `cmd5` command.
/// #[renderer]
/// fn render_cmd5(_: Entry5) {
/// r_println!("It's works!");
@@ -908,6 +918,7 @@ pub mod example_dispatch_tree {}
/// lang
/// }
///
+/// /// Renders the selected programming language with its name and description.
/// #[renderer]
/// fn render_programming_language(lang: ProgrammingLanguages) {
/// // You can use `enum_info()` to get the name and description of the current enum
@@ -1005,29 +1016,34 @@ pub mod example_enum_tag {}
/// ResultName::new(name).to_render()
/// }
///
+/// /// Renders a successful greeting with the given name.
/// #[renderer]
/// fn render_result_name(name: ResultName) {
/// r_println!("Hello, {}", *name);
/// }
///
+/// /// Renders the error when no name is provided.
/// #[renderer]
/// fn render_error_no_name_provided(_: ErrorNoNameProvided) {
/// // Prompt when no name is provided
/// r_println!("No name provided");
/// }
///
+/// /// Renders the error when the name is already taken.
/// #[renderer]
/// fn render_error_name_not_available(_: ErrorNameNotAvailable) {
/// // Prompt when name is already taken
/// r_println!("Name not available");
/// }
///
+/// /// Renders the error when the name exceeds the maximum length.
/// #[renderer]
/// fn render_error_name_too_long(len: ErrorNameTooLong) {
/// // Prompt when name is too long, showing actual length
/// r_println!("Name too long: {} > 10", *len);
/// }
///
+/// /// Renders the error when the dispatcher (subcommand) is not found.
/// #[renderer]
/// fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
/// // Prompt when command is not found, showing the input command
@@ -1102,6 +1118,7 @@ pub mod example_error_handling {}
/// ResultName::new(name).to_render()
/// }
///
+/// /// Renders a successful greeting with the given name.
/// #[renderer]
/// fn render_result_name(name: ResultName) {
/// r_println!("Hello, {}", *name);
@@ -1109,6 +1126,7 @@ pub mod example_error_handling {}
///
/// // Define renderer, render error message _____________ Inject exit code resource
/// // /
+/// /// Renders the error when no name is provided |
/// #[renderer] // vvvvvvvvvvvvv
/// fn render_error_no_name_provided(_: ErrorNoNameProvided, ec: &mut ExitCode) {
/// ec.exit_code = 1;
@@ -1160,7 +1178,7 @@ pub mod example_exitcode {}
/// Source code (./src/main.rs)
/// ```ignore
/// use mingling::prelude::*;
-/// use mingling::{Groupped, parser::Picker, setup::GeneralRendererSetup};
+/// use mingling::{parser::Picker, setup::GeneralRendererSetup, Groupped};
/// use serde::Serialize;
///
/// dispatcher!("render", CMDRender => EntryRender);
@@ -1170,7 +1188,7 @@ pub mod example_exitcode {}
/// // Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters
/// program.with_setup(GeneralRendererSetup);
/// program.with_dispatcher(CMDRender);
-/// program.exec();
+/// let _ = program.exec();
/// }
///
/// // --------- IMPORTANT ---------
@@ -1203,7 +1221,7 @@ pub mod example_exitcode {}
/// Info { name, age }.to_render()
/// }
///
-/// // Implement default renderer for when general_renderer is not specified
+/// /// Implement default renderer for when general_renderer is not specified
/// #[renderer]
/// fn render_info(prev: Info) {
/// r_println!("{} is {} years old", prev.name, prev.age);
@@ -1314,17 +1332,17 @@ pub mod example_help {}
/// program.with_hook(
/// ProgramHook::<ThisProgram>::empty()
/// .on_begin(|| println!("[DEBUG] Program is begin"))
-/// .on_pre_dispatch(|args| println!("[DEBUG] Pre dispatch: {:?}", args))
-/// .on_post_dispatch(|c: &_| println!("[DEBUG] Post dispatch: {:?}", c))
+/// .on_pre_dispatch(|args| println!("[DEBUG] Pre dispatch: {args:?}"))
+/// .on_post_dispatch(|c: &_| println!("[DEBUG] Post dispatch: {c:?}"))
/// .on_pre_chain(|c: &_, _| {
-/// println!("[DEBUG] Pre chain: {}", c);
+/// println!("[DEBUG] Pre chain: {c}");
/// })
/// .on_post_chain(|any_output| println!("[DEBUG] Post chain: {}", any_output.member_id))
/// .on_finish(|| {
/// println!("[DEBUG] Loop end");
/// 0 // Override exit code
/// })
-/// .on_pre_render(|c: &_, _| println!("[DEBUG] Pre render: {}", c))
+/// .on_pre_render(|c: &_, _| println!("[DEBUG] Pre render: {c}"))
/// .on_post_render(|_| println!("[DEBUG] Post render")),
/// );
/// // --------- IMPORTANT ---------
@@ -1346,6 +1364,7 @@ pub mod example_help {}
/// name
/// }
///
+/// /// Renders the greeting message with the provided name.
/// #[renderer]
/// fn render_name(name: ResultName) {
/// r_println!("Hello, {}!", *name);
@@ -1446,11 +1465,10 @@ pub mod example_implicit_dispatcher {}
/// program.stdout_setting.silence_panic = true;
///
/// // Define a hook to output &ProgramPanic when a Panic occurs
-/// program
-/// .with_hook(ProgramHook::empty().on_exec_panic(|info| println!("Program panic: {}", info)));
+/// program.with_hook(ProgramHook::empty().on_exec_panic(|info| println!("Program panic: {info}")));
/// // --------- IMPORTANT ---------
///
-/// program.exec();
+/// let _ = program.exec();
/// }
///
/// #[chain]
@@ -1465,6 +1483,7 @@ pub mod example_implicit_dispatcher {}
/// }
/// }
///
+/// /// Renders the message when no panic occurs.
/// #[renderer]
/// fn render(_: NotPanic) {
/// r_println!("Program not panic");
@@ -1558,7 +1577,7 @@ pub mod example_panic_unwind {}
/// // Add hooks to handle REPL-related events
/// program.with_hook(ProgramHook::empty().on_repl_begin(|| {
/// // Print welcome message
-/// println!("Welcome!")
+/// println!("Welcome!");
/// }));
///
/// // Start the REPL loop
@@ -1610,11 +1629,11 @@ pub mod example_panic_unwind {}
/// let dir = &current_dir.dir;
/// let entries: Vec<String> = std::fs::read_dir(dir)
/// .into_iter()
-/// .flat_map(|rd| rd.filter_map(|e| e.ok()))
+/// .flat_map(|rd| rd.filter_map(std::result::Result::ok))
/// .map(|e| {
/// let name = e.file_name().to_string_lossy().to_string();
/// if e.file_type().map(|t| t.is_dir()).unwrap_or(false) {
-/// format!("{}/", name)
+/// format!("{name}/")
/// } else {
/// name
/// }
@@ -1625,11 +1644,11 @@ pub mod example_panic_unwind {}
/// ResultList::new(entries).to_render()
/// }
///
-/// // Render ResultList data
+/// /// Render ResultList data
/// #[renderer]
/// fn render_list(list: ResultList) {
/// for item in list.inner {
-/// r_println!("{}", item)
+/// r_println!("{}", item);
/// }
/// }
///
@@ -1643,20 +1662,21 @@ pub mod example_panic_unwind {}
/// repl.exit = true;
/// }
///
-/// // Handle clear command event
+/// /// Handle clear command event
/// #[chain]
/// fn handle_clear(_prev: EntryClear) {
/// // Clear the terminal screen
/// print!("\x1B[2J\x1B[1;1H");
/// }
///
-/// // Handle path not found event
+/// /// Handle path not found event
/// #[renderer]
/// fn render_error_directory_not_exist(err: ErrorDirectoryNotExist) {
/// r_println!("Directory not found: {}", err.inner.display())
/// }
///
-/// // Handle dispatcher not found event
+/// /// Handle dispatcher not found event
+/// /// Renders the error when a command is not found.
/// #[renderer]
/// fn dispatcher_not_found(prev: ErrorDispatcherNotFound) {
/// r_println!("Command not found: \"{}\"", prev.join(", "))
@@ -1737,6 +1757,7 @@ pub mod example_repl_basic {}
///
/// // Define renderer for output current path _____________ Injected resource
/// // /
+/// /// Renders the current directory path. |
/// #[renderer] // vvvvvvvvvvvvvv
/// fn render_current(_: EntryCurrent, current_dir: &ResCurrentDir) {
/// r_println!("Current directory: {}", current_dir.current_dir.display());
@@ -1762,7 +1783,7 @@ pub mod example_resources {}
///
/// Source code (./src/main.rs)
/// ```ignore
-/// use mingling::{Program, macros::program_setup, prelude::*};
+/// use mingling::{macros::program_setup, prelude::*, Program};
///
/// fn main() {
/// let mut program = ThisProgram::new();
@@ -1897,26 +1918,31 @@ pub mod example_setup {}
/// ResultName::new(name).to_render()
/// }
///
+/// /// Renders a successful greeting with the given name.
/// #[renderer]
/// fn render_result_name(name: ResultName) -> String {
/// r_println!("Hello, {}!", *name);
/// }
///
+/// /// Renders the error when no name is provided.
/// #[renderer]
/// fn render_error_no_name_provided(_: ErrorNoNameProvided) -> String {
/// r_println!("No name provided");
/// }
///
+/// /// Renders the error when the name is already taken.
/// #[renderer]
/// fn render_error_name_not_available(_: ErrorNameNotAvailable) -> String {
/// r_println!("Name not available");
/// }
///
+/// /// Renders the error when the name exceeds the maximum length.
/// #[renderer]
/// fn render_error_name_too_long(len: ErrorNameTooLong) -> String {
/// r_println!("Name too long: {} > 10", *len);
/// }
///
+/// /// Renders the error when the dispatcher (subcommand) is not found.
/// #[renderer]
/// fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
/// r_println!("Command not found: \"{}\"", err.inner.join(" "));
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index ee01fb7..db9b063 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -100,11 +100,11 @@ pub mod macros {
/// Used to create a wrapper type for use with `Chain` and `Renderer`
pub use mingling_macros::pack;
#[cfg(feature = "comp")]
- /// Internal macro for 'gen_program' used to finally generate the completion structure
+ /// Internal macro for '`gen_program`' used to finally generate the completion structure
pub use mingling_macros::program_comp_gen;
- /// Internal macro for 'gen_program' used to finally generate the fallback
+ /// Internal macro for '`gen_program`' used to finally generate the fallback
pub use mingling_macros::program_fallback_gen;
- /// Internal macro for 'gen_program' used to finally generate the program
+ /// Internal macro for '`gen_program`' used to finally generate the program
pub use mingling_macros::program_final_gen;
/// Used to generate program setup
#[cfg(feature = "extra_macros")]
@@ -115,7 +115,7 @@ pub mod macros {
pub use mingling_macros::r_println;
/// Used to register a chain
pub use mingling_macros::register_chain;
- /// Used to register a dispatcher for dispatch_tree feature
+ /// Used to register a dispatcher for `dispatch_tree` feature
pub use mingling_macros::register_dispatcher;
/// Used to register a help
pub use mingling_macros::register_help;
@@ -136,7 +136,7 @@ pub mod macros {
pub use mingling_macros::suggest_enum;
}
-/// derive macro EnumTag
+/// derive macro `EnumTag`
pub use mingling_macros::EnumTag;
/// derive macro Groupped
diff --git a/mingling/src/parser/args.rs b/mingling/src/parser/args.rs
index 2dc0feb..23275c2 100644
--- a/mingling/src/parser/args.rs
+++ b/mingling/src/parser/args.rs
@@ -11,7 +11,10 @@ pub struct Argument {
impl From<Vec<&str>> for Argument {
fn from(vec: Vec<&str>) -> Self {
Argument {
- vec: vec.into_iter().map(|s| s.to_string()).collect(),
+ vec: vec
+ .into_iter()
+ .map(std::string::ToString::to_string)
+ .collect(),
}
}
}
@@ -85,18 +88,17 @@ impl Argument {
}
let flag: Flag = flag.into();
- if !flag.is_empty() {
- // Has any flag
- for argument in flag.iter() {
- let value = special_argument!(self.vec, argument);
- if value.is_some() {
- return value;
- }
- }
- } else {
+ if flag.is_empty() {
// No flag
return Some(self.vec.remove(0));
}
+ // Has any flag
+ for argument in flag.iter() {
+ let value = special_argument!(self.vec, argument);
+ if value.is_some() {
+ return value;
+ }
+ }
None
}
@@ -135,15 +137,7 @@ impl Argument {
}
let flag: Flag = flag.into();
- if !flag.is_empty() {
- // Has any flag
- for argument in flag.iter() {
- let enabled = special_flag!(self.vec, argument);
- if enabled {
- return enabled;
- }
- }
- } else {
+ if flag.is_empty() {
let first = self.vec.remove(0);
let first_lower = first.to_lowercase();
let trimmed = first_lower.trim();
@@ -154,6 +148,13 @@ impl Argument {
};
return result;
}
+ // Has any flag
+ for argument in flag.iter() {
+ let enabled = special_flag!(self.vec, argument);
+ if enabled {
+ return enabled;
+ }
+ }
false
}
@@ -167,6 +168,7 @@ impl Argument {
///
/// This method filters out all command-line style flags from the arguments,
/// returning a new `Argument` instance containing only non-flag arguments.
+ #[must_use]
pub fn strip_all_flags(mut self) -> Self {
self.vec.retain(|f| !f.starts_with('-'));
self
diff --git a/mingling/src/parser/picker.rs b/mingling/src/parser/picker.rs
index b0cdb70..21ba9a6 100644
--- a/mingling/src/parser/picker.rs
+++ b/mingling/src/parser/picker.rs
@@ -71,15 +71,12 @@ impl Picker {
where
TNext: Pickable<Output = TNext> + Default,
{
- let v = match TNext::pick(&mut self.args, val.into()) {
- Some(value) => value,
- None => {
- return PickWithRoute1 {
- args: self.args,
- val_1: TNext::default(),
- route: Some(route),
- };
- }
+ let Some(v) = TNext::pick(&mut self.args, val.into()) else {
+ return PickWithRoute1 {
+ args: self.args,
+ val_1: TNext::default(),
+ route: Some(route),
+ };
};
PickWithRoute1 {
args: self.args,
@@ -112,6 +109,7 @@ impl Picker {
/// Takes a closure that receives the current `Argument` and returns a new `Argument`.
/// The returned `Argument` replaces the original arguments in the builder.
/// This method can be used to modify or transform the parsed arguments before extracting values.
+ #[must_use]
pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
self.args = operation(self.args);
self
@@ -141,7 +139,7 @@ pub trait Pickable {
// Non-routed Pick structs (no R parameter, no route field)
/// Internal macro: generates the struct definition and common methods
-/// (after, after_or_route, operate_args) for non-routed Pick structs.
+/// (after, `after_or_route`, `operate_args`) for non-routed Pick structs.
macro_rules! define_pick_struct {
($n:ident $final:ident $final_val:ident $route_self:ident $($T:ident $val:ident),+ $(,)?) => {
#[doc(hidden)]
@@ -163,6 +161,7 @@ macro_rules! define_pick_struct {
/// Takes a closure that receives the last extracted value and returns a new value of the same type.
/// The transformed value replaces the original value in the builder.
/// This method can be used to modify or validate the extracted value before final unpacking.
+ #[must_use]
pub fn after<F>(mut self, mut edit: F) -> Self
where
F: FnMut($final) -> $final,
@@ -177,6 +176,7 @@ macro_rules! define_pick_struct {
/// If the closure returns `Ok(new_value)`, the new value replaces the original value in the builder.
/// If the closure returns `Err(route)`, the provided `route` is stored in the builder for later error handling.
/// If a route was already stored from a previous `pick_or_route` call, the existing route is preserved.
+ #[must_use]
pub fn after_or_route<F, R>(mut self, mut edit: F) -> $route_self<$($T,)+ R>
where
F: FnMut(&$final) -> Result<$final, R>,
@@ -205,6 +205,7 @@ macro_rules! define_pick_struct {
/// Takes a closure that receives the current `Argument` and returns a new `Argument`.
/// The returned `Argument` replaces the original arguments in the builder.
/// This method can be used to modify or transform the parsed arguments before extracting values.
+ #[must_use]
pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
self.args = operation(self.args);
self
@@ -359,17 +360,14 @@ macro_rules! impl_pick_next {
where
TNext: Pickable<Output = TNext> + Default,
{
- let v = match TNext::pick(&mut self.args, val.into()) {
- Some(value) => value,
- None => {
- return $route_next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: TNext::default(),
- route: Some(route),
- };
- }
- };
+ let Some(v) = TNext::pick(&mut self.args, val.into()) else {
+ return $route_next {
+ args: self.args,
+ $($val: self.$val,)+
+ $next_val: TNext::default(),
+ route: Some(route),
+ };
+ };
$route_next {
args: self.args,
$($val: self.$val,)+
@@ -413,7 +411,7 @@ impl_pick_next! { Pick11 Pick12 val_12 PickWithRoute12 T1 val_1, T2 val_2, T3 va
// Routed PickWithRoute structs (with R parameter, route field)
/// Internal macro: generates the routed struct definition and common methods
-/// (after, after_or_route, operate_args) for PickWithRoute structs.
+/// (after, `after_or_route`, `operate_args`) for `PickWithRoute` structs.
macro_rules! define_pick_with_route_struct {
($n:ident $final:ident $final_val:ident $($T:ident $val:ident),+) => {
#[doc(hidden)]
@@ -436,6 +434,7 @@ macro_rules! define_pick_with_route_struct {
/// Takes a closure that receives the last extracted value and returns a new value of the same type.
/// The transformed value replaces the original value in the builder.
/// This method can be used to modify or validate the extracted value before final unpacking.
+ #[must_use]
pub fn after<F>(mut self, mut edit: F) -> Self
where
F: FnMut($final) -> $final,
@@ -450,6 +449,7 @@ macro_rules! define_pick_with_route_struct {
/// If the closure returns `Ok(new_value)`, the new value replaces the original value in the builder.
/// If the closure returns `Err(route)`, the provided `route` is stored in the builder for later error handling.
/// If a route was already stored from a previous `pick_or_route` call, the existing route is preserved.
+ #[must_use]
pub fn after_or_route<F>(mut self, mut edit: F) -> Self
where
F: FnMut(&$final) -> Result<$final, R>,
@@ -475,6 +475,7 @@ macro_rules! define_pick_with_route_struct {
/// Takes a closure that receives the current `Argument` and returns a new `Argument`.
/// The returned `Argument` replaces the original arguments in the builder.
/// This method can be used to modify or transform the parsed arguments before extracting values.
+ #[must_use]
pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
self.args = operation(self.args);
self
@@ -483,7 +484,7 @@ macro_rules! define_pick_with_route_struct {
};
}
-/// Internal macro: generates `From` impl for routed PickWithRouteN into a tuple.
+/// Internal macro: generates `From` impl for routed `PickWithRouteN` into a tuple.
macro_rules! impl_pick_with_route_from_tuple {
($n:ident $($T:ident $val:ident),+) => {
impl<$($T,)+ R> From<$n<$($T,)+ R>> for ($($T,)+)
@@ -497,7 +498,7 @@ macro_rules! impl_pick_with_route_from_tuple {
};
}
-/// Internal macro: generates `unpack` and `unpack_directly` for routed PickWithRouteN (N >= 2).
+/// Internal macro: generates `unpack` and `unpack_directly` for routed `PickWithRouteN` (N >= 2).
macro_rules! impl_pick_with_route_unpack_tuple {
($n:ident $($T:ident $val:ident),+) => {
impl<$($T,)+ R> $n<$($T,)+ R>
@@ -508,6 +509,10 @@ macro_rules! impl_pick_with_route_unpack_tuple {
///
/// Returns `Ok((T1, T2, ...))` if no route was stored.
/// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
pub fn unpack(self) -> Result<($($T,)+), R> {
match self.route {
Some(route) => Err(route),
@@ -518,6 +523,7 @@ macro_rules! impl_pick_with_route_unpack_tuple {
/// Unpacks the builder into a tuple of extracted values.
///
/// Returns the tuple of extracted values regardless of route state.
+ #[must_use]
pub fn unpack_directly(self) -> ($($T,)+) {
($(self.$val,)+)
}
@@ -546,6 +552,10 @@ where
///
/// Returns `Ok(T1)` if no route was stored.
/// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
pub fn unpack(self) -> Result<T1, R> {
match self.route {
Some(route) => Err(route),
@@ -556,6 +566,7 @@ where
/// Unpacks the builder into the extracted value.
///
/// Returns the extracted value regardless of route state.
+ #[must_use]
pub fn unpack_directly(self) -> T1 {
self.val_1
}
@@ -650,6 +661,7 @@ macro_rules! impl_pick_with_route_next {
///
/// If a route was already stored from a previous `pick_or_route` or `after_or_route` call,
/// the existing route is preserved and the new `route` parameter is ignored.
+ #[allow(clippy::manual_let_else)]
pub fn pick_or_route<TNext>(mut self, val: impl Into<mingling_core::Flag>, route: R) -> $next<$($T,)+ TNext, R>
where
TNext: Pickable<Output = TNext> + Default,
diff --git a/mingling/src/parser/picker/bools.rs b/mingling/src/parser/picker/bools.rs
index aa2335a..ede8812 100644
--- a/mingling/src/parser/picker/bools.rs
+++ b/mingling/src/parser/picker/bools.rs
@@ -37,10 +37,12 @@ impl std::ops::Deref for Yes {
}
impl Yes {
+ #[must_use]
pub fn is_yes(&self) -> bool {
matches!(self, Yes::Yes)
}
+ #[must_use]
pub fn is_no(&self) -> bool {
matches!(self, Yes::No)
}
@@ -92,10 +94,12 @@ impl std::ops::Deref for True {
}
impl True {
+ #[must_use]
pub fn is_true(&self) -> bool {
matches!(self, True::True)
}
+ #[must_use]
pub fn is_false(&self) -> bool {
matches!(self, True::False)
}
diff --git a/mingling/src/parser/picker/builtin.rs b/mingling/src/parser/picker/builtin.rs
index e7a178d..6194955 100644
--- a/mingling/src/parser/picker/builtin.rs
+++ b/mingling/src/parser/picker/builtin.rs
@@ -68,7 +68,7 @@ impl Pickable for usize {
let picked = args.pick_argument(flag)?;
let size_parse = Size::from_str(picked.as_str());
match size_parse {
- Ok(size) => Some(size.bytes() as usize),
+ Ok(size) => usize::try_from(size.bytes()).ok(),
Err(_) => None,
}
}
@@ -84,7 +84,7 @@ impl Pickable for Vec<usize> {
for picked in picked_vec {
let size_parse = Size::from_str(picked.as_str());
match size_parse {
- Ok(size) => result.push(size.bytes() as usize),
+ Ok(size) => result.push(usize::try_from(size.bytes()).unwrap_or(usize::MAX)),
Err(_) => return None,
}
}
diff --git a/mingling/src/parser/picker/path.rs b/mingling/src/parser/picker/path.rs
index c97250f..961542e 100644
--- a/mingling/src/parser/picker/path.rs
+++ b/mingling/src/parser/picker/path.rs
@@ -91,7 +91,7 @@ impl<T: Into<PathBuf>> PathChecker for T where T: Into<PathBuf> {}
fn check_paths(path: impl Into<Vec<PathBuf>>, rule: &PathCheckRule) -> Result<(), ()> {
let paths = path.into();
- for p in paths.iter() {
+ for p in &paths {
check_exist(p, rule)?;
check_type(p, rule)?;
}
diff --git a/mingling/src/parser/picker/path/rule.rs b/mingling/src/parser/picker/path/rule.rs
index 07df705..bf5cab3 100644
--- a/mingling/src/parser/picker/path/rule.rs
+++ b/mingling/src/parser/picker/path/rule.rs
@@ -25,6 +25,7 @@ pub struct PathTypeCheck {
impl PathCheckRule {
/// Creates a new `PathCheckRule` with default values
+ #[must_use]
pub fn new() -> Self {
Self {
exist_check: None,
@@ -33,6 +34,7 @@ impl PathCheckRule {
}
/// Allows the path to be a file
+ #[must_use]
pub fn allow_file(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -55,6 +57,7 @@ impl PathCheckRule {
}
/// Allows the path to be a directory
+ #[must_use]
pub fn allow_dir(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -77,6 +80,7 @@ impl PathCheckRule {
}
/// Allows the path to be a symlink
+ #[must_use]
pub fn allow_symlink(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -99,6 +103,7 @@ impl PathCheckRule {
}
/// Denies the path from being a file
+ #[must_use]
pub fn deny_file(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -121,6 +126,7 @@ impl PathCheckRule {
}
/// Denies the path from being a directory
+ #[must_use]
pub fn deny_dir(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -143,6 +149,7 @@ impl PathCheckRule {
}
/// Denies the path from being a symlink
+ #[must_use]
pub fn deny_symlink(self) -> Self {
match self.type_check {
Some(type_check) => Self {
@@ -165,6 +172,7 @@ impl PathCheckRule {
}
/// Requires the path to be a file (overrides type checks)
+ #[must_use]
pub fn must_file(self) -> Self {
Self {
type_check: Some(PathTypeCheck {
@@ -177,6 +185,7 @@ impl PathCheckRule {
}
/// Requires the path to be a directory (overrides type checks)
+ #[must_use]
pub fn must_dir(self) -> Self {
Self {
type_check: Some(PathTypeCheck {
@@ -189,6 +198,7 @@ impl PathCheckRule {
}
/// Requires the path to be a symlink (overrides type checks)
+ #[must_use]
pub fn must_symlink(self) -> Self {
Self {
type_check: Some(PathTypeCheck {
@@ -201,6 +211,7 @@ impl PathCheckRule {
}
/// Requires the path to exist
+ #[must_use]
pub fn must_exist(self) -> Self {
Self {
exist_check: Some(PathExistCheck::Exists),
@@ -209,6 +220,7 @@ impl PathCheckRule {
}
/// Requires the path to not exist
+ #[must_use]
pub fn must_not_exist(self) -> Self {
Self {
exist_check: Some(PathExistCheck::NotExists),
diff --git a/mingling/src/res/exit_code.rs b/mingling/src/res/exit_code.rs
index b483139..e90d067 100644
--- a/mingling/src/res/exit_code.rs
+++ b/mingling/src/res/exit_code.rs
@@ -24,6 +24,7 @@ where
/// Retrieves the globally stored exit code for the given `ProgramCollect` type.
/// Returns `0` if no exit code has been set.
+#[must_use]
pub fn exit_code<C>() -> i32
where
C: ProgramCollect<Enum = C> + 'static,
diff --git a/mingling/src/setups/exit_code.rs b/mingling/src/setups/exit_code.rs
index 0412a78..9513363 100644
--- a/mingling/src/setups/exit_code.rs
+++ b/mingling/src/setups/exit_code.rs
@@ -18,7 +18,7 @@ where
{
fn default() -> Self {
Self {
- _collect: Default::default(),
+ _collect: PhantomData,
}
}
}
diff --git a/mingling/src/setups/general_renderer.rs b/mingling/src/setups/general_renderer.rs
index 81b6cd0..e0a0d61 100644
--- a/mingling/src/setups/general_renderer.rs
+++ b/mingling/src/setups/general_renderer.rs
@@ -35,7 +35,7 @@ where
fn setup(&mut self, program: &mut Program<C>) {
#[cfg(feature = "json_serde_fmt")]
program.global_flag("--json", |p| {
- p.general_renderer_name = crate::GeneralRendererSetting::Json
+ p.general_renderer_name = crate::GeneralRendererSetting::Json;
});
#[cfg(feature = "json_serde_fmt")]
program.global_flag("--json-pretty", |p| {
diff --git a/mingling_core/Cargo.toml b/mingling_core/Cargo.toml
index 26e642b..4ce9ecd 100644
--- a/mingling_core/Cargo.toml
+++ b/mingling_core/Cargo.toml
@@ -2,9 +2,13 @@
name = "mingling_core"
version.workspace = true
edition.workspace = true
+authors = ["Weicao-CatilGrass"]
license.workspace = true
description = "Core of the mingling library"
+readme = "README.md"
repository.workspace = true
+keywords = ["cli", "cli-framework", "framework", "procedural", "command-line"]
+categories = ["command-line-interface"]
[features]
nightly = []
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index 3dedea4..46ebced 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -10,7 +10,7 @@ pub mod group;
/// Any type output
///
/// Accepts any type that implements `Send + Groupped<G>`
-/// After being passed into AnyOutput, it will be converted to `Box<dyn Any + Send + 'static>`
+/// After being passed into `AnyOutput`, it will be converted to `Box<dyn Any + Send + 'static>`
///
/// Note:
/// - If an enum value that does not belong to this type is incorrectly specified, it will be **unsafely** unwrapped by the scheduler
@@ -24,7 +24,7 @@ pub struct AnyOutput<G> {
}
impl<G> AnyOutput<G> {
- /// Create an AnyOutput from a `Send + Groupped<G> + Serialize` type
+ /// Create an `AnyOutput` from a `Send + Groupped<G> + Serialize` type
#[cfg(feature = "general_renderer")]
pub fn new<T>(value: T) -> Self
where
@@ -37,7 +37,7 @@ impl<G> AnyOutput<G> {
}
}
- /// Create an AnyOutput from a `Send + Groupped<G>` type
+ /// Create an `AnyOutput` from a `Send + Groupped<G>` type
#[cfg(not(feature = "general_renderer"))]
pub fn new<T>(value: T) -> Self
where
@@ -50,7 +50,15 @@ impl<G> AnyOutput<G> {
}
}
- /// Downcast the AnyOutput to a concrete type T
+ /// Attempt to downcast the `AnyOutput` to a concrete type.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(self)` if the downcast fails.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the inner value is not of type `T`.
pub fn downcast<T: 'static>(self) -> Result<T, Self> {
if self.type_id == std::any::TypeId::of::<T>() {
Ok(*self.inner.downcast::<T>().unwrap())
@@ -75,7 +83,7 @@ impl<G> AnyOutput<G> {
}
#[cfg(feature = "general_renderer")]
- /// Restore AnyOutput back to the original Serialize type
+ /// Restore `AnyOutput` back to the original Serialize type
pub fn restore<T: Serialize + 'static>(self) -> Option<T> {
if self.type_id == std::any::TypeId::of::<T>() {
match self.inner.downcast::<T>() {
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs
index 1b488fe..423e218 100644
--- a/mingling_core/src/asset/chain.rs
+++ b/mingling_core/src/asset/chain.rs
@@ -3,7 +3,7 @@ use crate::ChainProcess;
#[doc(hidden)]
pub mod error;
-/// Takes over a type (G: Previous) and converts it to another [AnyOutput](./struct.AnyOutput.html)
+/// Takes over a type (G: Previous) and converts it to another [`AnyOutput`](./struct.AnyOutput.html)
pub trait Chain<G> {
/// The previous type in the chain
type Previous;
diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs
index ce69bc5..29abba1 100644
--- a/mingling_core/src/asset/chain/error.rs
+++ b/mingling_core/src/asset/chain/error.rs
@@ -13,8 +13,8 @@ pub enum ChainProcessError {
impl std::fmt::Display for ChainProcessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- ChainProcessError::Other(s) => write!(f, "Other error: {}", s),
- ChainProcessError::IO(e) => write!(f, "IO error: {}", e),
+ ChainProcessError::Other(s) => write!(f, "Other error: {s}"),
+ ChainProcessError::IO(e) => write!(f, "IO error: {e}"),
}
}
}
@@ -41,14 +41,14 @@ impl From<ProgramInternalExecuteError> for ChainProcessError {
ChainProcessError::Other("DispatcherNotFound".into())
}
ProgramInternalExecuteError::RendererNotFound(r) => {
- ChainProcessError::Other(format!("RendererNotFound: {}", r))
+ ChainProcessError::Other(format!("RendererNotFound: {r}"))
}
ProgramInternalExecuteError::Other(e) => ChainProcessError::Other(e),
ProgramInternalExecuteError::IO(e) => {
- ChainProcessError::Other(format!("IOError: {:?}", e))
+ ChainProcessError::Other(format!("IOError: {e:?}"))
}
ProgramInternalExecuteError::REPLPanic(program_panic) => {
- ChainProcessError::Other(format!("REPLPanic: {}", program_panic))
+ ChainProcessError::Other(format!("REPLPanic: {program_panic}"))
}
}
}
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index 95b3305..b62a0d0 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::{ChainProcess, Program, ProgramCollect, asset::node::Node};
-/// Dispatches user input commands to specific [ChainProcess](./enum.ChainProcess.html)
+/// Dispatches user input commands to specific [`ChainProcess`](./enum.ChainProcess.html)
///
/// Note: If you are using [mingling_macros](https://crates.io/crates/mingling_macros),
/// you can use the `dispatcher!("node.subnode", CommandType => Entry)` macro to declare a `Dispatcher`
@@ -10,7 +10,7 @@ pub trait Dispatcher<C> {
/// Returns a command node for matching user input
fn node(&self) -> Node;
- /// Returns a [ChainProcess](./enum.ChainProcess.html) based on user input arguments,
+ /// Returns a [`ChainProcess`](./enum.ChainProcess.html) based on user input arguments,
/// to be sent to the specific invocation
fn begin(&self, args: Vec<String>) -> ChainProcess<C>;
diff --git a/mingling_core/src/asset/enum_tag.rs b/mingling_core/src/asset/enum_tag.rs
index 28428a6..d830e62 100644
--- a/mingling_core/src/asset/enum_tag.rs
+++ b/mingling_core/src/asset/enum_tag.rs
@@ -1,4 +1,4 @@
-/// Marker trait for EnumTag
+/// Marker trait for `EnumTag`
pub trait EnumTag {
/// Get the name and description of this enum
fn enum_info(&self) -> (&'static str, &'static str);
diff --git a/mingling_core/src/asset/global_resource.rs b/mingling_core/src/asset/global_resource.rs
index 98a8160..d03c6ea 100644
--- a/mingling_core/src/asset/global_resource.rs
+++ b/mingling_core/src/asset/global_resource.rs
@@ -25,11 +25,8 @@ where
Res: 'static + Default + ResourceMarker + Send + Sync,
Return: Default,
{
- let mut guard = match self.resources.lock() {
- Ok(guard) => guard,
- Err(_) => {
- return Return::default();
- }
+ let Ok(mut guard) = self.resources.lock() else {
+ return Return::default();
};
if let Some(arc_res) = guard
.get_mut(&TypeId::of::<Res>())
@@ -56,12 +53,9 @@ where
Res: 'static + Default + ResourceMarker + Send + Sync,
Return: Into<ChainProcess<C>>,
{
- let mut guard = match self.resources.lock() {
- Ok(guard) => guard,
- Err(_) => {
- let mut default_res = Res::res_default();
- return f(&mut default_res);
- }
+ let Ok(mut guard) = self.resources.lock() else {
+ let mut default_res = Res::res_default();
+ return f(&mut default_res);
};
if let Some(arc_res) = guard
.get_mut(&TypeId::of::<Res>())
@@ -81,6 +75,7 @@ where
}
/// Get an resources by type, returning `Res` if present
+ #[must_use]
pub fn res<Res: 'static + Send + Sync>(&self) -> Option<GlobalResource<Res>> {
let guard = self.resources.lock().ok()?;
let boxed_any = guard.get(&TypeId::of::<Res>())?;
@@ -100,6 +95,7 @@ where
}
/// Get a resource by type, returning `GlobalResource<Res>` or inserting a default
+ #[must_use]
pub fn res_or_default<Res: 'static + Send + Sync + ResourceMarker>(
&self,
) -> GlobalResource<Res> {
@@ -144,6 +140,7 @@ impl<ResType: 'static + Send + Sync> AsRef<ResType> for GlobalResource<ResType>
/// Resource marker trait, types that implement the Clone and Default traits can be considered as resources
pub trait ResourceMarker {
+ #[must_use]
fn res_clone(&self) -> Self;
fn res_default() -> Self;
fn modify<C>(f: impl FnOnce(&mut Self))
diff --git a/mingling_core/src/asset/node.rs b/mingling_core/src/asset/node.rs
index 035d227..4dfdb48 100644
--- a/mingling_core/src/asset/node.rs
+++ b/mingling_core/src/asset/node.rs
@@ -11,6 +11,7 @@ pub struct Node {
impl Node {
/// Append a new part to the node path.
+ #[must_use]
pub fn join(self, node: impl Into<String>) -> Node {
let mut new_node = self.node;
new_node.push(node.into());
diff --git a/mingling_core/src/asset/renderer.rs b/mingling_core/src/asset/renderer.rs
index de417a2..1d5a2c1 100644
--- a/mingling_core/src/asset/renderer.rs
+++ b/mingling_core/src/asset/renderer.rs
@@ -1,6 +1,6 @@
use crate::RenderResult;
-/// Takes over a type (Self::Previous) and converts it to a [`RenderResult`](./struct.RenderResult.html)
+/// Takes over a type (`Self::Previous`) and converts it to a [`RenderResult`](./struct.RenderResult.html)
pub trait Renderer {
/// The previous type in the chain
type Previous;
diff --git a/mingling_core/src/builds/comp.rs b/mingling_core/src/builds/comp.rs
index 8b884c8..aa08627 100644
--- a/mingling_core/src/builds/comp.rs
+++ b/mingling_core/src/builds/comp.rs
@@ -10,7 +10,7 @@ const TMPL_COMP_FISH: &str = include_str!("../../tmpls/comps/fish.fish");
const TMPL_COMP_PWSH: &str = include_str!("../../tmpls/comps/pwsh.ps1");
/// Generate shell completion scripts for a given binary name.
-/// On Windows, generates PowerShell completion.
+/// On Windows, generates `PowerShell` completion.
/// On Linux, generates Zsh, Bash, and Fish completions.
/// Scripts are written to the `OUT_DIR` (or `target/` if `OUT_DIR` is not set).
///
@@ -63,7 +63,7 @@ pub fn build_comp_scripts(name: &str) -> Result<(), std::io::Error> {
/// ```
pub fn build_comp_script(shell_flag: &ShellFlag, bin_name: &str) -> Result<(), std::io::Error> {
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
- let target_dir = out_dir.join("../../../").to_path_buf();
+ let target_dir = out_dir.join("../../../").clone();
build_comp_script_to(shell_flag, bin_name, &target_dir.to_string_lossy())
}
@@ -89,7 +89,7 @@ pub fn build_comp_script_to(
tmpl_param!(tmpl, bin_name = bin_name);
let target_path = std::path::PathBuf::from(target_dir);
std::fs::create_dir_all(&target_path)?;
- let output_path = target_path.join(format!("{}_comp{}", bin_name, ext));
+ let output_path = target_path.join(format!("{bin_name}_comp{ext}"));
std::fs::write(&output_path, tmpl.to_string())
}
diff --git a/mingling_core/src/comp.rs b/mingling_core/src/comp.rs
index 8d55c5d..4f3890a 100644
--- a/mingling_core/src/comp.rs
+++ b/mingling_core/src/comp.rs
@@ -43,6 +43,7 @@ pub trait CompletionEntry {
/// format appropriate for the target shell.
pub struct CompletionHelper;
impl CompletionHelper {
+ #[must_use]
pub fn exec_completion<P>(ctx: &ShellContext) -> Suggest
where
P: ProgramCollect<Enum = P> + Display + PartialEq + 'static + std::fmt::Debug,
@@ -114,6 +115,7 @@ impl CompletionHelper {
}
}
+ #[allow(clippy::needless_pass_by_value)]
pub fn render_suggest<P>(ctx: ShellContext, suggest: Suggest)
where
P: ProgramCollect<Enum = P> + Display + 'static,
@@ -130,15 +132,15 @@ impl CompletionHelper {
match ctx.shell_flag {
ShellFlag::Zsh | ShellFlag::Powershell => {
trace!("using zsh/pwsh format");
- print_suggest_with_description(suggestions)
+ print_suggest_with_description(suggestions);
}
ShellFlag::Fish => {
trace!("using fish format");
- print_suggest_with_description_fish(suggestions)
+ print_suggest_with_description_fish(suggestions);
}
_ => {
trace!("using default format");
- print_suggest(suggestions)
+ print_suggest(suggestions);
}
}
}
@@ -162,7 +164,7 @@ where
if ctx.word_index < 1 {
debug!("word_index < 1, returning file suggestions");
return file_suggest();
- };
+ }
// Get the current input path
let input_end = ctx.word_index.min(ctx.all_words.len());
@@ -178,7 +180,7 @@ where
.unwrap_or(&[])
.iter()
.filter(|s| !s.is_empty())
- .map(|s| s.as_str())
+ .map(std::string::String::as_str)
.collect();
debug!(
"input_path={:?}, current_word='{}'",
diff --git a/mingling_core/src/comp/flags.rs b/mingling_core/src/comp/flags.rs
index 452126b..424fe8b 100644
--- a/mingling_core/src/comp/flags.rs
+++ b/mingling_core/src/comp/flags.rs
@@ -14,7 +14,7 @@ pub enum ShellFlag {
Zsh,
/// Represents the Fish shell.
Fish,
- /// Represents PowerShell.
+ /// Represents `PowerShell`.
Powershell,
/// A custom or unsupported shell type, identified by the provided string.
Other(String),
diff --git a/mingling_core/src/comp/shell_ctx.rs b/mingling_core/src/comp/shell_ctx.rs
index 3134cd6..35758e9 100644
--- a/mingling_core/src/comp/shell_ctx.rs
+++ b/mingling_core/src/comp/shell_ctx.rs
@@ -73,8 +73,7 @@ impl TryFrom<Vec<String>> for ShellContext {
let shell_flag = arg_map
.get("-F")
.cloned()
- .map(ShellFlag::from)
- .unwrap_or(ShellFlag::Other("unknown".to_string()));
+ .map_or(ShellFlag::Other("unknown".to_string()), ShellFlag::from);
let all_words = command_line
.split_whitespace()
@@ -120,7 +119,7 @@ impl ShellContext {
let flag = flag.into();
if self.filling_argument(&flag) {
let mut flag_appears = 0;
- for w in self.all_words.iter() {
+ for w in &self.all_words {
for f in flag.iter() {
if *f == w {
flag_appears += 1;
@@ -190,6 +189,7 @@ impl ShellContext {
/// // }
/// }
/// ```
+ #[must_use]
pub fn typing_argument(&self) -> bool {
#[cfg(target_os = "windows")]
{
@@ -207,6 +207,7 @@ impl ShellContext {
/// in the command line. It is useful for preventing duplicate flag suggestions
/// when the user has already typed certain flags. The method processes both
/// regular suggestion sets and file completion suggestions differently.
+ #[must_use]
pub fn strip_typed_argument(&self, suggest: Suggest) -> Suggest {
let typed = Self::get_typed_arguments(self);
match suggest {
@@ -223,11 +224,12 @@ impl ShellContext {
/// This method collects all words in the shell context that start with a dash (`-`),
/// which typically represent command-line flags or options. It returns a vector
/// containing these flag strings, converted to owned `String` values.
+ #[must_use]
pub fn get_typed_arguments(&self) -> HashSet<String> {
self.all_words
.iter()
- .filter(|word| word.starts_with("-"))
- .map(|word| word.to_string())
+ .filter(|word| word.starts_with('-'))
+ .cloned()
.collect()
}
}
diff --git a/mingling_core/src/comp/suggest.rs b/mingling_core/src/comp/suggest.rs
index 6d64341..cd025a4 100644
--- a/mingling_core/src/comp/suggest.rs
+++ b/mingling_core/src/comp/suggest.rs
@@ -16,17 +16,20 @@ pub enum Suggest {
}
impl Suggest {
- /// Creates a new Suggest variant containing a BTreeSet of suggestions.
+ /// Creates a new Suggest variant containing a `BTreeSet` of suggestions.
+ #[must_use]
pub fn new() -> Self {
Self::Suggest(BTreeSet::new())
}
- /// Creates a FileCompletion variant.
+ /// Creates a `FileCompletion` variant.
+ #[must_use]
pub fn file_comp() -> Self {
Self::FileCompletion
}
/// Filters out already typed flag arguments from suggestion results.
+ #[must_use]
pub fn strip_typed_argument(self, ctx: &ShellContext) -> Self {
ctx.strip_typed_argument(self)
}
@@ -103,40 +106,44 @@ impl Ord for SuggestItem {
impl SuggestItem {
/// Creates a new simple suggestion without description.
+ #[must_use]
pub fn new(suggest: String) -> Self {
Self::Simple(suggest)
}
/// Creates a new suggestion with a description.
+ #[must_use]
pub fn new_with_desc(suggest: String, description: String) -> Self {
Self::WithDescription(suggest, description)
}
/// Adds a description to this suggestion, replacing any existing description.
+ #[must_use]
pub fn with_desc(self, description: String) -> Self {
match self {
- Self::Simple(suggest) => Self::WithDescription(suggest, description),
- Self::WithDescription(suggest, _) => Self::WithDescription(suggest, description),
+ Self::Simple(suggest) | Self::WithDescription(suggest, _) => {
+ Self::WithDescription(suggest, description)
+ }
}
}
/// Returns the suggestion text.
+ #[must_use]
pub fn suggest(&self) -> &String {
match self {
- Self::Simple(suggest) => suggest,
- Self::WithDescription(suggest, _) => suggest,
+ Self::Simple(suggest) | Self::WithDescription(suggest, _) => suggest,
}
}
/// Updates the suggestion text.
pub fn set_suggest(&mut self, new_suggest: String) {
match self {
- Self::Simple(suggest) => *suggest = new_suggest,
- Self::WithDescription(suggest, _) => *suggest = new_suggest,
+ Self::Simple(suggest) | Self::WithDescription(suggest, _) => *suggest = new_suggest,
}
}
/// Returns the description if present.
+ #[must_use]
pub fn description(&self) -> Option<&String> {
match self {
Self::Simple(_) => None,
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 912975d..2e861e4 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -70,6 +70,7 @@ where
C: ProgramCollect<Enum = C>,
{
/// Creates a new Program instance, initializing command-line arguments from the environment.
+ #[must_use]
pub fn new() -> Self {
#[cfg(not(windows))]
return Self::new_with_args(env::args().collect::<Vec<String>>());
@@ -96,8 +97,8 @@ where
#[cfg(not(feature = "dispatch_tree"))]
dispatcher: Vec::new(),
- stdout_setting: Default::default(),
- user_context: Default::default(),
+ stdout_setting: ProgramStdoutSetting::default(),
+ user_context: ProgramUserContext::default(),
#[cfg(feature = "general_renderer")]
general_renderer_name: GeneralRendererSetting::Disable,
@@ -109,6 +110,10 @@ where
}
/// Returns a reference to the current program instance, if set.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the program has not been initialized yet.
pub fn this_program() -> &'static Program<C>
where
C: 'static,
@@ -123,6 +128,7 @@ where
}
/// Get all registered dispatcher names from the program
+ #[must_use]
pub fn get_nodes(
&'static self,
) -> Vec<(String, &'static (dyn Dispatcher<C> + Send + Sync + 'static))> {
@@ -130,11 +136,17 @@ where
}
/// Dynamically dispatch input arguments to registered entry types
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(ChainProcessError)` if the dispatch fails,
+ /// e.g., if no dispatcher is found for the given arguments.
pub fn dispatch_args_dynamic(
&'static self,
args: impl Into<StringVec>,
) -> Result<AnyOutput<C>, ChainProcessError> {
- match exec::dispatch_args_dynamic(self, &args.into().into()) {
+ let sv: Vec<String> = args.into().into();
+ match exec::dispatch_args_dynamic(self, &sv) {
Ok(ok) => Ok(ok),
Err(e) => Err(e.into()),
}
@@ -229,6 +241,7 @@ macro_rules! __dispatch_program_chains {
/// Get all registered dispatcher names from the program
#[allow(unused_variables)]
+#[must_use]
pub fn get_nodes<C: ProgramCollect<Enum = C>>(
program: &'static Program<C>,
) -> Vec<(String, &'static (dyn Dispatcher<C> + Send + Sync + 'static))> {
diff --git a/mingling_core/src/program/collection.rs b/mingling_core/src/program/collection.rs
index ff26411..d3d18d6 100644
--- a/mingling_core/src/program/collection.rs
+++ b/mingling_core/src/program/collection.rs
@@ -25,23 +25,23 @@ pub trait ProgramCollect {
/// Use a prefix tree to quickly match arguments and dispatch to an Entry
#[cfg(feature = "dispatch_tree")]
fn dispatch_args_trie(
- raw: &Vec<String>,
+ raw: &[String],
) -> Result<AnyOutput<Self::Enum>, crate::error::ProgramInternalExecuteError>;
/// Get all registered dispatcher names from the program
#[cfg(feature = "dispatch_tree")]
fn get_nodes() -> Vec<(String, &'static (dyn Dispatcher<Self::Enum> + Send + Sync))>;
- /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a renderer was not found
+ /// Build an [`AnyOutput`](./struct.AnyOutput.html) to indicate that a renderer was not found
fn build_renderer_not_found(member_id: Self::Enum) -> AnyOutput<Self::Enum>;
- /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a dispatcher was not found
+ /// Build an [`AnyOutput`](./struct.AnyOutput.html) to indicate that a dispatcher was not found
fn build_dispatcher_not_found(args: Vec<String>) -> AnyOutput<Self::Enum>;
- /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that the chain returned an empty result
+ /// Build an [`AnyOutput`](./struct.AnyOutput.html) to indicate that the chain returned an empty result
fn build_empty_result() -> AnyOutput<Self::Enum>;
- /// Render the input [AnyOutput](./struct.AnyOutput.html)
+ /// Render the input [`AnyOutput`](./struct.AnyOutput.html)
fn render(any: AnyOutput<Self::Enum>, r: &mut RenderResult);
/// Render help for Entry
@@ -53,7 +53,7 @@ pub trait ProgramCollect {
any: AnyOutput<Self::Enum>,
) -> Pin<Box<dyn Future<Output = ChainProcess<Self::Enum>> + Send>>;
- /// Find a matching chain to continue execution based on the input [AnyOutput](./struct.AnyOutput.html), returning a new [AnyOutput](./struct.AnyOutput.html)
+ /// Find a matching chain to continue execution based on the input [`AnyOutput`](./struct.AnyOutput.html), returning a new [`AnyOutput`](./struct.AnyOutput.html)
#[cfg(not(feature = "async"))]
fn do_chain(any: AnyOutput<Self::Enum>) -> ChainProcess<Self::Enum>;
@@ -61,13 +61,18 @@ pub trait ProgramCollect {
#[cfg(feature = "comp")]
fn do_comp(any: &AnyOutput<Self::Enum>, ctx: &ShellContext) -> Suggest;
- /// Whether the program has a renderer that can handle the current [AnyOutput](./struct.AnyOutput.html)
+ /// Whether the program has a renderer that can handle the current [`AnyOutput`](./struct.AnyOutput.html)
fn has_renderer(any: &AnyOutput<Self::Enum>) -> bool;
- /// Whether the program has a chain that can handle the current [AnyOutput](./struct.AnyOutput.html)
+ /// Whether the program has a chain that can handle the current [`AnyOutput`](./struct.AnyOutput.html)
fn has_chain(any: &AnyOutput<Self::Enum>) -> bool;
/// Perform general rendering and presentation of any type
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization of the
+ /// output value fails.
#[cfg(feature = "general_renderer")]
fn general_render(
any: AnyOutput<Self::Enum>,
diff --git a/mingling_core/src/program/config.rs b/mingling_core/src/program/config.rs
index b5b46a0..5c104ab 100644
--- a/mingling_core/src/program/config.rs
+++ b/mingling_core/src/program/config.rs
@@ -109,7 +109,7 @@ impl std::str::FromStr for GeneralRendererSetting {
"ron" => Ok(GeneralRendererSetting::Ron),
#[cfg(feature = "ron_serde_fmt")]
"ron-pretty" => Ok(GeneralRendererSetting::RonPretty),
- _ => Err(format!("Invalid renderer: '{}'", s)),
+ _ => Err(format!("Invalid renderer: '{s}'")),
}
}
}
diff --git a/mingling_core/src/program/error.rs b/mingling_core/src/program/error.rs
index 03e9af6..822e429 100644
--- a/mingling_core/src/program/error.rs
+++ b/mingling_core/src/program/error.rs
@@ -9,9 +9,9 @@ pub struct ProgramPanic {
impl fmt::Display for ProgramPanic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = self.payload.downcast_ref::<&str>() {
- write!(f, "{}", s)
+ write!(f, "{s}")
} else if let Some(s) = self.payload.downcast_ref::<String>() {
- write!(f, "{}", s)
+ write!(f, "{s}")
} else {
write!(f, "")
}
@@ -19,6 +19,7 @@ impl fmt::Display for ProgramPanic {
}
impl ProgramPanic {
+ #[must_use]
pub fn new(payload: Box<dyn Any + Send>) -> Self {
ProgramPanic { payload }
}
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 72a20b9..0cadc6a 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -15,8 +15,7 @@ pub async fn exec<C>(
where
C: ProgramCollect<Enum = C>,
{
- let args = program.args.clone();
- exec_with_args(program, args).await
+ exec_with_args(program, &program.args).await
}
#[cfg(not(feature = "async"))]
@@ -24,26 +23,25 @@ pub fn exec<C>(program: &'static Program<C>) -> Result<RenderResult, ProgramInte
where
C: ProgramCollect<Enum = C>,
{
- let args = program.args.clone();
- exec_with_args(program, args)
+ exec_with_args(program, &program.args)
}
#[cfg(feature = "async")]
pub async fn exec_with_args<C>(
program: &'static Program<C>,
- args: Vec<String>,
+ args: &[String],
) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C>,
{
// Run hooks
- program.run_hook_pre_dispatch(&args);
+ program.run_hook_pre_dispatch(args);
#[cfg(not(feature = "dispatch_tree"))]
- let mut current = dispatch_args_dynamic(program, &args)?;
+ let mut current = dispatch_args_dynamic(program, args)?;
#[cfg(feature = "dispatch_tree")]
- let mut current = C::dispatch_args_trie(&args)?;
+ let mut current = C::dispatch_args_trie(args)?;
// Run hook
program.run_hook_post_dispatch(&current.member_id);
@@ -125,19 +123,19 @@ where
#[cfg(not(feature = "async"))]
pub fn exec_with_args<C>(
program: &'static Program<C>,
- args: Vec<String>,
+ args: &[String],
) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C>,
{
// Run hooks
- program.run_hook_pre_dispatch(&args);
+ program.run_hook_pre_dispatch(args);
#[cfg(not(feature = "dispatch_tree"))]
- let mut current = dispatch_args_dynamic(program, &args)?;
+ let mut current = dispatch_args_dynamic(program, args)?;
#[cfg(feature = "dispatch_tree")]
- let mut current = C::dispatch_args_trie(&args)?;
+ let mut current = C::dispatch_args_trie(args)?;
// Run hook
program.run_hook_post_dispatch(&current.member_id);
@@ -221,7 +219,7 @@ where
/// Dynamically dispatch input arguments to registered entry types
pub(crate) fn dispatch_args_dynamic<C>(
program: &'static Program<C>,
- args: &Vec<String>,
+ args: &[String],
) -> Result<AnyOutput<C>, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C>,
@@ -236,7 +234,7 @@ where
}
Err(ProgramInternalExecuteError::DispatcherNotFound) => {
// No matching Dispatcher is found
- C::build_dispatcher_not_found(args.clone())
+ C::build_dispatcher_not_found(args.to_vec())
}
Err(e) => return Err(e),
};
@@ -245,10 +243,9 @@ where
/// Match user input against registered dispatchers and return the matched dispatcher and remaining arguments.
#[allow(clippy::type_complexity)]
-#[allow(clippy::ptr_arg)]
pub(crate) fn match_user_input<C>(
program: &'static Program<C>,
- args: &Vec<String>,
+ args: &[String],
) -> Result<(&'static (dyn Dispatcher<C> + Send + Sync), Vec<String>), ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C>,
@@ -260,7 +257,7 @@ where
let matching_nodes: Vec<&(String, &(dyn Dispatcher<C> + Send + Sync))> = nodes
.iter()
// Also add a space to the node string to ensure consistent matching logic
- .filter(|(node_str, _)| command.starts_with(&format!("{} ", node_str)))
+ .filter(|(node_str, _)| command.starts_with(&format!("{node_str} ")))
.collect();
match matching_nodes.len() {
@@ -289,7 +286,7 @@ where
}
}
-#[inline(always)]
+#[inline]
#[allow(unused_variables)]
fn render<C: ProgramCollect<Enum = C>>(program: &Program<C>, any: AnyOutput<C>) -> RenderResult {
#[cfg(not(feature = "general_renderer"))]
@@ -312,7 +309,7 @@ fn render<C: ProgramCollect<Enum = C>>(program: &Program<C>, any: AnyOutput<C>)
}
}
-#[inline(always)]
+#[inline]
#[allow(unused_variables)]
fn render_help<C: ProgramCollect<Enum = C>>(
program: &Program<C>,
diff --git a/mingling_core/src/program/exec/error.rs b/mingling_core/src/program/exec/error.rs
index 0f2d875..944e89a 100644
--- a/mingling_core/src/program/exec/error.rs
+++ b/mingling_core/src/program/exec/error.rs
@@ -26,10 +26,10 @@ impl fmt::Display for ProgramExecuteError {
match self {
ProgramExecuteError::DispatcherNotFound => write!(f, "No Dispatcher Found"),
ProgramExecuteError::RendererNotFound(s) => {
- write!(f, "No Renderer (`{}`) Found", s)
+ write!(f, "No Renderer (`{s}`) Found")
}
- ProgramExecuteError::Panic(p) => write!(f, "Panic: {:?}", p),
- ProgramExecuteError::Other(s) => write!(f, "Other error: {}", s),
+ ProgramExecuteError::Panic(p) => write!(f, "Panic: {p:?}"),
+ ProgramExecuteError::Other(s) => write!(f, "Other error: {s}"),
}
}
}
@@ -74,12 +74,12 @@ impl fmt::Display for ProgramInternalExecuteError {
write!(f, "No Dispatcher Found")
}
ProgramInternalExecuteError::RendererNotFound(s) => {
- write!(f, "No Renderer (`{}`) Found", s)
+ write!(f, "No Renderer (`{s}`) Found")
}
- ProgramInternalExecuteError::Other(s) => write!(f, "Other error: {}", s),
- ProgramInternalExecuteError::IO(e) => write!(f, "IO error: {}", e),
+ ProgramInternalExecuteError::Other(s) => write!(f, "Other error: {s}"),
+ ProgramInternalExecuteError::IO(e) => write!(f, "IO error: {e}"),
ProgramInternalExecuteError::REPLPanic(panic) => {
- write!(f, "A single REPL execution failed: {}", panic)
+ write!(f, "A single REPL execution failed: {panic}")
}
}
}
@@ -110,11 +110,10 @@ impl From<ProgramInternalExecuteError> for ProgramExecuteError {
ProgramExecuteError::RendererNotFound(s)
}
ProgramInternalExecuteError::Other(s) => ProgramExecuteError::Other(s),
- ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{}", e)),
- ProgramInternalExecuteError::REPLPanic(p) => ProgramExecuteError::Other(format!(
- "A single REPL execution failed: {}",
- p
- )),
+ ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{e}")),
+ ProgramInternalExecuteError::REPLPanic(p) => {
+ ProgramExecuteError::Other(format!("A single REPL execution failed: {p}"))
+ }
}
}
}
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 210f2d6..13f6ea9 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -51,7 +51,7 @@ impl From<&Flag> for Flag {
}
impl From<()> for Flag {
- fn from(_: ()) -> Self {
+ fn from((): ()) -> Self {
Flag { vec: vec![] }
}
}
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs
index 3520084..929eac2 100644
--- a/mingling_core/src/program/hook.rs
+++ b/mingling_core/src/program/hook.rs
@@ -16,7 +16,7 @@ where
pub begin: Option<fn()>,
/// Executes before the program dispatches
- pub pre_dispatch: Option<fn(args: &Vec<String>)>,
+ pub pre_dispatch: Option<fn(args: &[String])>,
/// Executes after the program dispatches
pub post_dispatch: Option<fn(entry: &C)>,
@@ -98,19 +98,19 @@ where
for hook in &self.hooks {
if let Some(begin) = hook.begin {
- begin()
+ begin();
}
}
}
- pub(crate) fn run_hook_pre_dispatch(&self, args: &Vec<String>) {
+ pub(crate) fn run_hook_pre_dispatch(&self, args: &[String]) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(pre_dispatch) = hook.pre_dispatch {
- pre_dispatch(args)
+ pre_dispatch(args);
}
}
}
@@ -122,7 +122,7 @@ where
for hook in &self.hooks {
if let Some(post_dispatch) = hook.post_dispatch {
- post_dispatch(entry)
+ post_dispatch(entry);
}
}
}
@@ -134,7 +134,7 @@ where
for hook in &self.hooks {
if let Some(pre_chain) = hook.pre_chain {
- pre_chain(input, raw)
+ pre_chain(input, raw);
}
}
}
@@ -146,7 +146,7 @@ where
for hook in &self.hooks {
if let Some(post_chain) = hook.post_chain {
- post_chain(output)
+ post_chain(output);
}
}
}
@@ -158,7 +158,7 @@ where
for hook in &self.hooks {
if let Some(pre_render) = hook.pre_render {
- pre_render(input, raw)
+ pre_render(input, raw);
}
}
}
@@ -170,7 +170,7 @@ where
for hook in &self.hooks {
if let Some(post_render) = hook.post_render {
- post_render(result)
+ post_render(result);
}
}
}
@@ -184,7 +184,7 @@ where
for hook in &self.hooks {
if let Some(exec_panic) = hook.exec_panic {
- exec_panic(panic_info)
+ exec_panic(panic_info);
}
}
}
@@ -354,6 +354,7 @@ where
C: ProgramCollect<Enum = C>,
{
/// Creates a new empty hook set with no handlers.
+ #[must_use]
pub fn empty() -> Self {
Self {
begin: None,
@@ -390,48 +391,56 @@ where
}
/// Sets the handler for the `begin` event.
+ #[must_use]
pub fn on_begin(mut self, handler: fn()) -> Self {
let _ = self.begin.insert(handler);
self
}
/// Sets the handler for the `pre_dispatch` event.
- pub fn on_pre_dispatch(mut self, handler: fn(args: &Vec<String>)) -> Self {
+ #[must_use]
+ pub fn on_pre_dispatch(mut self, handler: fn(args: &[String])) -> Self {
let _ = self.pre_dispatch.insert(handler);
self
}
/// Sets the handler for the `post_dispatch` event.
+ #[must_use]
pub fn on_post_dispatch(mut self, handler: fn(entry: &C)) -> Self {
let _ = self.post_dispatch.insert(handler);
self
}
/// Sets the handler for the `pre_chain` event.
+ #[must_use]
pub fn on_pre_chain(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self {
let _ = self.pre_chain.insert(handler);
self
}
/// Sets the handler for the `post_chain` event.
+ #[must_use]
pub fn on_post_chain(mut self, handler: fn(output: &AnyOutput<C>)) -> Self {
let _ = self.post_chain.insert(handler);
self
}
/// Sets the handler for the `pre_render` event.
+ #[must_use]
pub fn on_pre_render(mut self, handler: fn(input: &C, raw: &dyn Any)) -> Self {
let _ = self.pre_render.insert(handler);
self
}
/// Sets the handler for the `post_render` event.
+ #[must_use]
pub fn on_post_render(mut self, handler: fn(result: &RenderResult)) -> Self {
let _ = self.post_render.insert(handler);
self
}
/// Sets the handler for the `finish` event.
+ #[must_use]
pub fn on_finish(mut self, handler: fn() -> i32) -> Self {
let _ = self.finish.insert(handler);
self
@@ -439,6 +448,7 @@ where
/// Sets the handler for the `exec_panic` event.
#[cfg(not(feature = "async"))]
+ #[must_use]
pub fn on_exec_panic(mut self, handler: fn(&ProgramPanic)) -> Self {
let _ = self.exec_panic.insert(handler);
self
@@ -446,6 +456,7 @@ where
/// Sets the handler for the REPL begin event (only available with `repl` feature).
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_begin(mut self, handler: fn()) -> Self {
let _ = self.repl_on_begin.insert(handler);
self
@@ -454,6 +465,7 @@ where
/// Sets the handler for the REPL pre-readline event (only available with `repl` feature).
/// This hook runs after `on_repl_begin` but before reading the next input line.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_pre_readline(mut self, handler: fn()) -> Self {
let _ = self.repl_pre_readline.insert(handler);
self
@@ -463,6 +475,7 @@ where
/// If set, this function will be called to read a line instead of the default mechanism.
/// Returning `None` signals that there is no input (e.g., EOF).
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_readline(mut self, handler: fn() -> Option<String>) -> Self {
let _ = self.repl_readline.insert(handler);
self
@@ -471,6 +484,7 @@ where
/// Sets the handler for the REPL post-readline event (only available with `repl` feature).
/// This hook runs after reading a line of input and receives a mutable reference to the line.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_post_readline(mut self, handler: fn(line: &mut String)) -> Self {
let _ = self.repl_post_readline.insert(handler);
self
@@ -479,6 +493,7 @@ where
/// Sets the handler for the REPL pre-exec event (only available with `repl` feature).
/// This hook runs before executing a REPL command, receiving the parsed arguments.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_pre_exec(mut self, handler: fn(args: &[String])) -> Self {
let _ = self.repl_pre_exec.insert(handler);
self
@@ -487,6 +502,7 @@ where
/// Sets the handler for the REPL post-exec event (only available with `repl` feature).
/// This hook runs after executing a REPL command.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_post_exec(mut self, handler: fn()) -> Self {
let _ = self.repl_post_exec.insert(handler);
self
@@ -495,6 +511,7 @@ where
/// Sets the handler for the REPL receive result event (only available with `repl` feature).
/// This hook runs after a command is executed, receiving the render result on success.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_receive_result(mut self, handler: fn(result: &RenderResult)) -> Self {
let _ = self.repl_on_receive_result.insert(handler);
self
@@ -502,6 +519,7 @@ where
/// Sets the handler for the REPL panic event (only available with `repl` feature).
#[cfg(all(feature = "repl", not(feature = "async")))]
+ #[must_use]
pub fn on_repl_panic(mut self, handler: fn(panic: &ProgramPanic)) -> Self {
let _ = self.repl_on_panic.insert(handler);
self
@@ -510,6 +528,7 @@ where
/// Sets the handler for the REPL exit event (only available with `repl` feature).
/// This hook runs when the REPL is about to exit.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_exit(mut self, handler: fn()) -> Self {
let _ = self.repl_exit.insert(handler);
self
@@ -518,6 +537,7 @@ where
/// Sets the handler for the REPL loop_once event (only available with `repl` feature).
/// This hook runs after each REPL loop iteration.
#[cfg(feature = "repl")]
+ #[must_use]
pub fn on_repl_loop_once(mut self, handler: fn()) -> Self {
let _ = self.repl_loop_once.insert(handler);
self
diff --git a/mingling_core/src/program/once_exec.rs b/mingling_core/src/program/once_exec.rs
index e1c0956..f757893 100644
--- a/mingling_core/src/program/once_exec.rs
+++ b/mingling_core/src/program/once_exec.rs
@@ -29,6 +29,15 @@ where
}
/// Run the command line program
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(ProgramExecuteError)` if execution fails,
+ /// e.g., if no dispatcher is found or a chain error occurs.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the program encounters a non-recoverable internal error.
pub async fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError>
where
C: 'static + Send + Sync,
@@ -127,6 +136,15 @@ where
}
/// Run the command line program
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(ProgramExecuteError)` if execution fails,
+ /// e.g., if no dispatcher is found or a chain error occurs.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the program encounters a non-recoverable internal error.
pub fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError>
where
C: 'static + Send + Sync,
@@ -141,7 +159,7 @@ where
#[cfg(not(panic = "abort"))]
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
- self.exec_wrapper(|p| crate::exec::exec(p).map_err(|e| e.into()))
+ self.exec_wrapper(|p| crate::exec::exec(p).map_err(std::convert::Into::into))
})) {
Ok(result) => result,
Err(panic_info) => {
@@ -164,6 +182,7 @@ where
}
/// Run the command line program
+ #[must_use]
pub fn exec(self) -> i32
where
C: 'static + Send + Sync,
@@ -179,15 +198,15 @@ where
return 1;
}
ProgramExecuteError::RendererNotFound(renderer_name) => {
- eprintln!("Renderer `{}` not found", renderer_name);
+ eprintln!("Renderer `{renderer_name}` not found");
return 1;
}
ProgramExecuteError::Other(e) => {
- eprintln!("{}", e);
+ eprintln!("{e}");
return 1;
}
ProgramExecuteError::Panic(unwinded_error) => {
- eprintln!("{}", unwinded_error);
+ eprintln!("{unwinded_error}");
return 1;
}
},
@@ -196,12 +215,12 @@ where
// Render result
if stdout_setting.render_output && !result.is_empty() {
let exit_code = result.exit_code;
- print!("{}", result);
+ print!("{result}");
if let Err(e) = std::io::Write::flush(&mut std::io::stdout())
&& stdout_setting.error_output
{
- eprintln!("{}", e);
+ eprintln!("{e}");
1
} else {
exit_code
diff --git a/mingling_core/src/program/repl_exec.rs b/mingling_core/src/program/repl_exec.rs
index 3d82b74..d7ee8e8 100644
--- a/mingling_core/src/program/repl_exec.rs
+++ b/mingling_core/src/program/repl_exec.rs
@@ -115,12 +115,12 @@ where
C: ProgramCollect<Enum = C> + Send + Sync + 'static,
{
#[cfg(panic = "abort")]
- let exec_result = super::exec::exec_with_args(p, args);
+ let exec_result = super::exec::exec_with_args(p, &args);
#[cfg(not(panic = "abort"))]
let exec_result = {
let exec_unwind_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
- super::exec::exec_with_args(p, args)
+ super::exec::exec_with_args(p, &args)
}));
match exec_unwind_result {
@@ -153,5 +153,5 @@ async fn exec_once<C>(
where
C: ProgramCollect<Enum = C> + Send + Sync + 'static,
{
- super::exec::exec_with_args(p, args).await
+ super::exec::exec_with_args(p, &args).await
}
diff --git a/mingling_core/src/program/single_instance.rs b/mingling_core/src/program/single_instance.rs
index 45d4d33..70771d5 100644
--- a/mingling_core/src/program/single_instance.rs
+++ b/mingling_core/src/program/single_instance.rs
@@ -7,6 +7,11 @@ pub(crate) static THIS_PROGRAM: OnceLock<Option<Box<dyn std::any::Any + Send + S
OnceLock::new();
/// Returns a reference to the current program instance, panics if not set.
+///
+/// # Panics
+///
+/// Panics if the program has not been initialized yet.
+#[must_use]
pub fn this<C>() -> &'static Program<C>
where
C: ProgramCollect<Enum = C> + 'static,
diff --git a/mingling_core/src/renderer/general.rs b/mingling_core/src/renderer/general.rs
index 7d07bac..0ea82c1 100644
--- a/mingling_core/src/renderer/general.rs
+++ b/mingling_core/src/renderer/general.rs
@@ -14,7 +14,11 @@ pub mod error;
pub struct GeneralRenderer;
impl GeneralRenderer {
- // Renders data in the specified format to the given RenderResult.
+ /// Renders data in the specified format to the given `RenderResult`.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[allow(unused_variables)]
pub fn render<T: Serialize + Send>(
data: &T,
@@ -39,6 +43,10 @@ impl GeneralRenderer {
}
/// Serializes data to JSON format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "json_serde_fmt")]
pub fn render_to_json<T: Serialize + Send>(
data: &T,
@@ -46,11 +54,15 @@ impl GeneralRenderer {
) -> Result<(), GeneralRendererSerializeError> {
let json_string = serde_json::to_string(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(json_string.to_string().as_str());
+ r.print(json_string.clone().as_str());
Ok(())
}
/// Serializes data to pretty-printed JSON format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "json_serde_fmt")]
pub fn render_to_json_pretty<T: Serialize + Send>(
data: &T,
@@ -58,11 +70,15 @@ impl GeneralRenderer {
) -> Result<(), GeneralRendererSerializeError> {
let json_string = serde_json::to_string_pretty(data)
.map_err(|e| GeneralRendererSerializeError::new(e.to_string()))?;
- r.print(json_string.to_string().as_str());
+ r.print(json_string.clone().as_str());
Ok(())
}
/// Serializes data to RON format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "ron_serde_fmt")]
pub fn render_to_ron<T: Serialize + Send>(
data: &T,
@@ -75,6 +91,10 @@ impl GeneralRenderer {
}
/// Serializes data to pretty-printed RON format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "ron_serde_fmt")]
pub fn render_to_ron_pretty<T: Serialize + Send>(
data: &T,
@@ -91,6 +111,10 @@ impl GeneralRenderer {
}
/// Serializes data to TOML format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "toml_serde_fmt")]
pub fn render_to_toml<T: Serialize + Send>(
data: &T,
@@ -103,6 +127,10 @@ impl GeneralRenderer {
}
/// Serializes data to YAML format and writes it to the render result.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(GeneralRendererSerializeError)` if serialization fails.
#[cfg(feature = "yaml_serde_fmt")]
pub fn render_to_yaml<T: Serialize + Send>(
data: &T,
diff --git a/mingling_core/src/renderer/general/error.rs b/mingling_core/src/renderer/general/error.rs
index a61b19d..eb76a8b 100644
--- a/mingling_core/src/renderer/general/error.rs
+++ b/mingling_core/src/renderer/general/error.rs
@@ -9,6 +9,7 @@ pub struct GeneralRendererSerializeError {
}
impl GeneralRendererSerializeError {
+ #[must_use]
pub fn new(error: String) -> Self {
Self { error }
}
diff --git a/mingling_core/src/tester/chain_process_tester.rs b/mingling_core/src/tester/chain_process_tester.rs
index 8189c28..ca809e4 100644
--- a/mingling_core/src/tester/chain_process_tester.rs
+++ b/mingling_core/src/tester/chain_process_tester.rs
@@ -47,7 +47,7 @@ where
}
}
ChainProcess::Err(chain_process_error) => {
- panic!("Chain process error: {}", chain_process_error);
+ panic!("Chain process error: {chain_process_error}");
}
}
}
@@ -87,7 +87,7 @@ where
ChainProcess::Ok((any, _next)) => any
.downcast_ref::<Type>()
.expect("Type mismatch: expected type does not match actual output type"),
- ChainProcess::Err(chain_process_error) => panic!("{:?}", chain_process_error),
+ ChainProcess::Err(chain_process_error) => panic!("{chain_process_error:?}"),
}
}
@@ -187,7 +187,7 @@ macro_rules! assert_render_result {
};
}
-/// Asserts that the result's output type matches the expected member_id.
+/// Asserts that the result's output type matches the expected `member_id`.
///
/// This macro checks that the `ChainProcess` result is `Ok` and that its output type identifier
/// matches the expected type. It is a convenience wrapper around `assert_next_eq` with the `next`
diff --git a/mingling_macros/Cargo.toml b/mingling_macros/Cargo.toml
index 4fc5c75..a5fdabf 100644
--- a/mingling_macros/Cargo.toml
+++ b/mingling_macros/Cargo.toml
@@ -2,9 +2,13 @@
name = "mingling_macros"
version.workspace = true
edition.workspace = true
+authors = ["Weicao-CatilGrass"]
license.workspace = true
+readme = "README.md"
repository.workspace = true
description = "Macros of the mingling library"
+keywords = ["cli", "macros", "procedural", "command-line", "framework"]
+categories = ["command-line-interface", "development-tools::procedural-macros"]
[lib]
proc-macro = true
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs
index ac05480..b0ea8ae 100644
--- a/mingling_macros/src/chain.rs
+++ b/mingling_macros/src/chain.rs
@@ -229,8 +229,8 @@ fn generate_struct_and_impl(
group_name: &proc_macro2::TokenStream,
program_type: &proc_macro2::TokenStream,
use_crate_prefix: bool,
- proc_fn: proc_macro2::TokenStream,
- origin_proc_fn: proc_macro2::TokenStream,
+ proc_fn: &proc_macro2::TokenStream,
+ origin_proc_fn: &proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let chain_type = if use_crate_prefix {
program_type
@@ -389,8 +389,8 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
&group_name,
&program_type,
use_crate_prefix,
- proc_fn,
- origin_proc_fn,
+ &proc_fn,
+ &origin_proc_fn,
);
expanded.into()
diff --git a/mingling_macros/src/dispatch_tree_gen.rs b/mingling_macros/src/dispatch_tree_gen.rs
index a2cd52c..66fb6e7 100644
--- a/mingling_macros/src/dispatch_tree_gen.rs
+++ b/mingling_macros/src/dispatch_tree_gen.rs
@@ -43,7 +43,7 @@ pub fn gen_dispatch_args_trie(entries: &[(String, String, String)]) -> TokenStre
quote! {
fn dispatch_args_trie(
- raw: &Vec<String>,
+ raw: &[String],
) -> Result<::mingling::AnyOutput<Self::Enum>, ::mingling::error::ProgramInternalExecuteError>
{
let raw_string = format!("{} ", raw.join(" "));
@@ -63,7 +63,7 @@ pub fn gen_dispatch_args_trie(entries: &[(String, String, String)]) -> TokenStre
fn build_dispatch_body(nodes: &[(String, String)], depth: usize) -> TokenStream {
if nodes.is_empty() {
return quote! {
- return Ok(Self::build_dispatcher_not_found(raw.clone()));
+ return Ok(Self::build_dispatcher_not_found(raw.to_vec()));
};
}
@@ -121,7 +121,7 @@ fn build_dispatch_body(nodes: &[(String, String)], depth: usize) -> TokenStream
arms.push(quote! {
Some(#ch_char) => {
#arm
- return Ok(Self::build_dispatcher_not_found(raw.clone()));
+ return Ok(Self::build_dispatcher_not_found(raw.to_vec()));
}
});
} else {
@@ -150,7 +150,7 @@ fn build_dispatch_body(nodes: &[(String, String)], depth: usize) -> TokenStream
let match_body = quote! {
match raw_chars.nth(0) {
#(#arms)*
- _ => return Ok(Self::build_dispatcher_not_found(raw.clone())),
+ _ => return Ok(Self::build_dispatcher_not_found(raw.to_vec())),
}
};
quote! {
@@ -161,19 +161,19 @@ fn build_dispatch_body(nodes: &[(String, String)], depth: usize) -> TokenStream
// Only exact nodes, no deeper groups
quote! {
#(#exact_checks)*
- return Ok(Self::build_dispatcher_not_found(raw.clone()));
+ return Ok(Self::build_dispatcher_not_found(raw.to_vec()));
}
} else if arms.is_empty() {
// Only fallback (shouldn't happen if nodes is non-empty)
quote! {
- return Ok(Self::build_dispatcher_not_found(raw.clone()));
+ return Ok(Self::build_dispatcher_not_found(raw.to_vec()));
}
} else {
// Only group arms
quote! {
match raw_chars.nth(0) {
#(#arms)*
- _ => return Ok(Self::build_dispatcher_not_found(raw.clone())),
+ _ => return Ok(Self::build_dispatcher_not_found(raw.to_vec())),
}
}
}
diff --git a/mingling_macros/src/dispatcher.rs b/mingling_macros/src/dispatcher.rs
index b7952a1..7e973eb 100644
--- a/mingling_macros/src/dispatcher.rs
+++ b/mingling_macros/src/dispatcher.rs
@@ -104,6 +104,7 @@ impl Parse for DispatcherChainInput {
// Additionally, the token stream generation patterns are nearly identical between
// the two main functions and could benefit from refactoring.
+#[allow(clippy::too_many_lines)]
pub fn dispatcher(input: TokenStream) -> TokenStream {
// Parse the input
let dispatcher_input = syn::parse_macro_input!(input as DispatcherChainInput);
@@ -332,7 +333,7 @@ pub fn register_dispatcher(_input: TokenStream) -> TokenStream {
quote! {}.into()
}
-/// Converts a dotted command name (e.g. "remote.add") to PascalCase (e.g. "RemoteAdd").
+/// Converts a dotted command name (e.g. "remote.add") to `PascalCase` (e.g. "`RemoteAdd`").
///
/// Each segment is split by `.`, the first character of each segment is uppercased,
/// and the segments are joined. This is used by the abbreviated `dispatcher!` syntax
diff --git a/mingling_macros/src/entry.rs b/mingling_macros/src/entry.rs
index 6237e41..2ac5d6b 100644
--- a/mingling_macros/src/entry.rs
+++ b/mingling_macros/src/entry.rs
@@ -42,21 +42,16 @@ fn parse_strings(input: &syn::parse::ParseBuffer) -> syn::Result<Vec<String>> {
pub fn entry(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as EntryInput);
- let string_exprs = match &parsed {
- EntryInput::Typed { .. } | EntryInput::Untyped { .. } => {
- let strings = match &parsed {
- EntryInput::Typed { strings, .. } => strings,
- EntryInput::Untyped { strings } => strings,
- };
- strings
- .iter()
- .map(|s| {
- let lit = syn::LitStr::new(s, proc_macro2::Span::call_site());
- quote! { #lit.to_string() }
- })
- .collect::<Vec<_>>()
- }
+ let strings = match &parsed {
+ EntryInput::Typed { strings, .. } | EntryInput::Untyped { strings } => strings,
};
+ let string_exprs = strings
+ .iter()
+ .map(|s| {
+ let lit = syn::LitStr::new(s, proc_macro2::Span::call_site());
+ quote! { #lit.to_string() }
+ })
+ .collect::<Vec<_>>();
let expanded = match parsed {
EntryInput::Typed { ident, .. } => {
diff --git a/mingling_macros/src/enum_tag.rs b/mingling_macros/src/enum_tag.rs
index 8f0576a..6277b69 100644
--- a/mingling_macros/src/enum_tag.rs
+++ b/mingling_macros/src/enum_tag.rs
@@ -13,7 +13,7 @@ pub fn derive_enum_tag(input: TokenStream) -> TokenStream {
}
}
-/// Implementation of the EnumTag derive macro
+/// Implementation of the `EnumTag` derive macro
fn derive_enum_tag_impl(input: DeriveInput) -> Result<proc_macro2::TokenStream> {
let enum_name = &input.ident;
let generics = &input.generics;
@@ -42,7 +42,7 @@ fn derive_enum_tag_impl(input: DeriveInput) -> Result<proc_macro2::TokenStream>
for variant in data.variants {
process_variant(
- variant,
+ &variant,
enum_name,
&mut variant_info,
&mut match_arms,
@@ -82,7 +82,7 @@ fn derive_enum_tag_impl(input: DeriveInput) -> Result<proc_macro2::TokenStream>
/// Process a single enum variant
fn process_variant(
- variant: Variant,
+ variant: &Variant,
enum_name: &Ident,
variant_info: &mut Vec<proc_macro2::TokenStream>,
match_arms: &mut Vec<proc_macro2::TokenStream>,
@@ -97,10 +97,9 @@ fn process_variant(
}
Fields::Named(_) | Fields::Unnamed(_) => {
return Err(Error::new_spanned(
- &variant,
+ variant,
format!(
- "EnumTag cannot be derived for enum variant `{}` with fields. Only unit variants are supported.",
- variant_name
+ "EnumTag cannot be derived for enum variant `{variant_name}` with fields. Only unit variants are supported."
),
));
}
@@ -132,7 +131,7 @@ fn process_variant(
Ok(())
}
-/// Extract description from #[enum_desc] attribute
+/// Extract description from #[`enum_desc`] attribute
fn extract_description(attrs: &[Attribute]) -> Result<Option<String>> {
for attr in attrs {
if attr.path().is_ident("enum_desc") {
@@ -150,7 +149,7 @@ fn extract_description(attrs: &[Attribute]) -> Result<Option<String>> {
Ok(None)
}
-/// Extract rename from #[enum_rename] attribute
+/// Extract rename from #[`enum_rename`] attribute
fn extract_rename(attrs: &[Attribute]) -> Result<Option<String>> {
for attr in attrs {
if attr.path().is_ident("enum_rename") {
diff --git a/mingling_macros/src/groupped.rs b/mingling_macros/src/groupped.rs
index e385812..534e2a6 100644
--- a/mingling_macros/src/groupped.rs
+++ b/mingling_macros/src/groupped.rs
@@ -23,13 +23,10 @@ pub fn derive_groupped(input: TokenStream) -> TokenStream {
// Parse attributes to find #[group(...)]
let group_ident: proc_macro2::TokenStream = parse_group_attribute(&input.attrs)
- .map(|ident| quote! { #ident })
- .unwrap_or_else(crate::default_program_path);
+ .map_or_else(crate::default_program_path, |ident| quote! { #ident });
- let any_output_convert_impls = proc_macro2::TokenStream::from(build_any_output_convert_impls(
- struct_name.clone(),
- group_ident.clone(),
- ));
+ let any_output_convert_impls =
+ proc_macro2::TokenStream::from(build_any_output_convert_impls(&struct_name, &group_ident));
// Generate the Groupped trait implementation
let expanded = quote! {
@@ -55,13 +52,10 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream {
// Parse attributes to find #[group(...)]
let group_ident: proc_macro2::TokenStream = parse_group_attribute(&input_parsed.attrs)
- .map(|ident| quote! { #ident })
- .unwrap_or_else(crate::default_program_path);
+ .map_or_else(crate::default_program_path, |ident| quote! { #ident });
- let any_output_convert_impls = proc_macro2::TokenStream::from(build_any_output_convert_impls(
- struct_name.clone(),
- group_ident.clone(),
- ));
+ let any_output_convert_impls =
+ proc_macro2::TokenStream::from(build_any_output_convert_impls(&struct_name, &group_ident));
// Generate both Serialize and Groupped implementations
let expanded = quote! {
@@ -83,8 +77,8 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream {
}
fn build_any_output_convert_impls(
- struct_name: Ident,
- group_ident: proc_macro2::TokenStream,
+ struct_name: &Ident,
+ group_ident: &proc_macro2::TokenStream,
) -> TokenStream {
quote! {
impl ::std::convert::Into<::mingling::AnyOutput<#group_ident>> for #struct_name {
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 8cae29f..7a93895 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -82,7 +82,7 @@ pub(crate) static CHAINS_EXIST: Registry = OnceLock::new();
pub(crate) static RENDERERS_EXIST: Registry = OnceLock::new();
pub(crate) static HELP_REQUESTS: Registry = OnceLock::new();
-/// Checks that a TypePath is a simple single-segment identifier (no `::` in the path).
+/// Checks that a `TypePath` is a simple single-segment identifier (no `::` in the path).
///
/// This is used by `#[renderer]`, `#[help]`, `#[chain]`, and `#[completion]` attribute macros
/// to ensure that the type in the function signature is a bare identifier like `Empty`,
@@ -307,7 +307,7 @@ pub fn empty_result(_input: TokenStream) -> TokenStream {
///
/// When the `extra_macros` feature is enabled, the `CommandStruct => EntryStruct`
/// portion can be omitted. The struct names are auto-derived from the command path
-/// using PascalCase conversion:
+/// using `PascalCase` conversion:
///
/// ```rust,ignore
/// // Auto-derives: "remote.add" → CMDRemoteAdd ⇒ EntryRemoteAdd
@@ -1188,7 +1188,7 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream {
/// Registers a type into the global packed types registry for inclusion in
/// the program enum generated by `gen_program!`.
///
-/// This macro is called internally by `pack!` and `#[derive(Groupped)]`(macro.derive_groupped.html)
+/// This macro is called internally by `pack!` and `#[derive(Groupped)]`(`macro.derive_groupped.html`)
/// and is generally not needed in user code. However, it can be used for manual
/// registration if you are implementing custom type registration outside of
/// the standard macros.
@@ -1201,6 +1201,10 @@ pub fn program_comp_gen(input: TokenStream) -> TokenStream {
///
/// Each call inserts the type's name into the `PACKED_TYPES` global set, which
/// is later consumed by `program_final_gen!` to generate enum variants.
+///
+/// # Panics
+///
+/// Panics if the global `PACKED_TYPES` mutex is poisoned.
#[proc_macro]
pub fn register_type(input: TokenStream) -> TokenStream {
let type_ident = parse_macro_input!(input as syn::Ident);
@@ -1348,7 +1352,13 @@ pub fn program_fallback_gen(input: TokenStream) -> TokenStream {
/// pub fn new() -> Program<MyProgram> { Program::new() }
/// }
/// ```
+///
+/// # Panics
+///
+/// Panics if any of the global registries (`PACKED_TYPES`, `RENDERERS`, `CHAINS`, etc.)
+/// are poisoned.
#[proc_macro]
+#[allow(clippy::too_many_lines)]
pub fn program_final_gen(input: TokenStream) -> TokenStream {
let name = read_name(&input);
@@ -1479,11 +1489,11 @@ pub fn program_final_gen(input: TokenStream) -> TokenStream {
.collect();
let num_variants = packed_types.len();
- let repr_type = if num_variants <= u8::MAX as usize {
+ let repr_type = if u8::try_from(num_variants).is_ok() {
quote! { u8 }
- } else if num_variants <= u16::MAX as usize {
+ } else if u16::try_from(num_variants).is_ok() {
quote! { u16 }
- } else if num_variants <= u32::MAX as usize {
+ } else if u32::try_from(num_variants).is_ok() {
quote! { u32 }
} else {
quote! { u128 }
@@ -1612,7 +1622,7 @@ pub fn program_final_gen(input: TokenStream) -> TokenStream {
///
/// # Related
///
-/// - `suggest_enum!`(macro.suggest_enum.html) — Build suggestions from an `EnumTag` enum.
+/// - `suggest_enum!`(`macro.suggest_enum.html`) — Build suggestions from an `EnumTag` enum.
#[cfg(feature = "comp")]
#[proc_macro]
pub fn suggest(input: TokenStream) -> TokenStream {
diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs
index 954a052..5a1c388 100644
--- a/mingling_macros/src/pack.rs
+++ b/mingling_macros/src/pack.rs
@@ -59,6 +59,7 @@ impl Parse for PackInput {
}
}
+#[allow(clippy::too_many_lines)]
pub fn pack(input: TokenStream) -> TokenStream {
// Parse the input
let pack_input = syn::parse_macro_input!(input as PackInput);
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index ae75895..4cf9fc1 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -7,20 +7,21 @@ use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
/// Extracts and returns the return type from the function signature (or None for `()` / no return type).
-fn extract_return_type(sig: &Signature) -> syn::Result<Option<syn::Type>> {
+fn extract_return_type(sig: &Signature) -> Option<syn::Type> {
match &sig.output {
ReturnType::Type(_, ty) => {
match &**ty {
// `()` means no custom return type
- Type::Tuple(tuple) if tuple.elems.is_empty() => Ok(None),
+ Type::Tuple(tuple) if tuple.elems.is_empty() => None,
// Any other return type is allowed
- custom_ty => Ok(Some((*custom_ty).clone())),
+ custom_ty => Some((*custom_ty).clone()),
}
}
- ReturnType::Default => Ok(None),
+ ReturnType::Default => None,
}
}
+#[allow(clippy::too_many_lines)]
pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse attribute arguments for program path (e.g. #[renderer(my_crate::Program)])
let (program_path, _use_crate_prefix) = parse_renderer_attr_args(attr);
@@ -48,10 +49,7 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
}
// Validate return type – now returns Some(type) if custom type, None if ()
- let return_type = match extract_return_type(&input_fn.sig) {
- Ok(rt) => rt,
- Err(e) => return e.to_compile_error().into(),
- };
+ let return_type = extract_return_type(&input_fn.sig);
// Get function body statements
let fn_body_stmts: Vec<syn::Stmt> = input_fn.block.stmts.clone();
@@ -83,23 +81,20 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect();
// Determine public return type and the expression to return dummy_r
- let (public_return_type, result_return) = match &return_type {
+ let (public_return_type, result_return) = if let Some(custom_ty) = &return_type {
// User specified a custom return type (e.g. -> String)
- Some(custom_ty) => {
- let ret_ty = quote! { #custom_ty };
- let expr = quote! { dummy_r.into() };
- (ret_ty, expr)
- }
+ let ret_ty = quote! { #custom_ty };
+ let expr = quote! { dummy_r.into() };
+ (ret_ty, expr)
+ } else {
// Return type is () — no custom return type specified
- None => {
- let ret_ty = quote! { () };
- let expr = quote! {
- if !dummy_r.is_empty() {
- ::std::println!("{}", &*dummy_r);
- }
- };
- (ret_ty, expr)
- }
+ let ret_ty = quote! { () };
+ let expr = quote! {
+ if !dummy_r.is_empty() {
+ ::std::println!("{}", &*dummy_r);
+ }
+ };
+ (ret_ty, expr)
};
let inner_body_with_resources = if has_mut_resources {
diff --git a/mingling_macros/src/res_injection.rs b/mingling_macros/src/res_injection.rs
index bdb3b73..f2280e3 100644
--- a/mingling_macros/src/res_injection.rs
+++ b/mingling_macros/src/res_injection.rs
@@ -13,6 +13,7 @@ pub(crate) struct ResourceInjection {
/// Extracts the previous type and parameter name from function arguments,
/// and collects resource injection parameters from the 2nd argument onward.
+#[allow(clippy::too_many_lines)]
pub(crate) fn extract_args_info(
sig: &Signature,
) -> syn::Result<(Pat, TypePath, Vec<ResourceInjection>)> {
@@ -183,7 +184,7 @@ pub(crate) fn wrap_body_with_mut_resources(
#(#fn_body_stmts)*
};
- for res in mut_resources.iter() {
+ for res in mut_resources {
let var_name = &res.var_name;
let inner_type = &res.inner_type;
wrapped = quote! {
diff --git a/mling/Cargo.toml b/mling/Cargo.toml
index 2d01bdf..df9c675 100644
--- a/mling/Cargo.toml
+++ b/mling/Cargo.toml
@@ -2,9 +2,13 @@
name = "mingling-cli"
version.workspace = true
edition.workspace = true
+authors = ["Weicao-CatilGrass"]
license.workspace = true
+readme = "README.md"
repository.workspace = true
description = "Mingling's scaffolding tool"
+keywords = ["cli", "scaffolding", "command-line", "framework"]
+categories = ["command-line-interface"]
[[bin]]
name = "mling"
diff --git a/mling/src/cli.rs b/mling/src/cli.rs
index 705c6b4..b628021 100644
--- a/mling/src/cli.rs
+++ b/mling/src/cli.rs
@@ -17,6 +17,11 @@ pub use read::*;
pub mod install;
pub use install::*;
+/// Entry point for the CLI application.
+///
+/// # Panics
+///
+/// Panics on Windows if the virtual terminal processing cannot be enabled.
pub fn cli_entry() {
let mut program = ThisProgram::new();
@@ -62,7 +67,7 @@ pub fn cli_entry() {
#[cfg(windows)]
colored::control::set_virtual_terminal(true).unwrap();
- program.exec();
+ let _ = program.exec();
}
#[renderer]
diff --git a/mling/src/cli/list.rs b/mling/src/cli/list.rs
index ac9a65f..a2a9434 100644
--- a/mling/src/cli/list.rs
+++ b/mling/src/cli/list.rs
@@ -78,44 +78,44 @@ pub(crate) fn render_installed(prev: ResultInstalledNamespaces) {
match prev.option {
StateListInstalledOptions::All => {
print_list(
- "Trusted".bright_green().bold().to_string(),
- prev.trusted,
+ &"Trusted".bright_green().bold().to_string(),
+ &prev.trusted,
__renderer_inner_result,
);
print_list(
- "Untrusted".bright_red().bold().to_string(),
- prev.untrusted,
+ &"Untrusted".bright_red().bold().to_string(),
+ &prev.untrusted,
__renderer_inner_result,
);
print_list(
- "Untagged".bright_black().bold().to_string(),
- prev.untagged,
+ &"Untagged".bright_black().bold().to_string(),
+ &prev.untagged,
__renderer_inner_result,
);
}
StateListInstalledOptions::OnlyTrusted => {
print_list(
- "Trusted".bright_green().bold().to_string(),
- prev.trusted,
+ &"Trusted".bright_green().bold().to_string(),
+ &prev.trusted,
__renderer_inner_result,
);
}
StateListInstalledOptions::OnlyUntrusted => {
print_list(
- "Untrusted".bright_red().bold().to_string(),
- prev.untrusted,
+ &"Untrusted".bright_red().bold().to_string(),
+ &prev.untrusted,
__renderer_inner_result,
);
}
}
}
-fn print_list(title: String, list: Vec<String>, __renderer_inner_result: &mut RenderResult) {
+fn print_list(title: &str, list: &[String], __renderer_inner_result: &mut RenderResult) {
if list.is_empty() {
return;
}
- r_println!("{}", title);
+ r_println!("{title}");
for (i, namespace) in (1..).zip(list.iter()) {
r_println!(" {}. {}", i.to_string(), namespace.bold());
diff --git a/mling/src/cli/read.rs b/mling/src/cli/read.rs
index 82f8fd5..e51e78f 100644
--- a/mling/src/cli/read.rs
+++ b/mling/src/cli/read.rs
@@ -25,7 +25,8 @@ pub(crate) struct ResultBinaries {
}
#[chain]
-pub(crate) fn handle_target_dir_entry(_prev: ReadTargetDirEntry) -> Next {
+#[allow(unused_variables)]
+pub(crate) fn handle_target_dir_entry(entry: ReadTargetDirEntry) -> Next {
match solve_current_dir() {
Ok(solved) => {
let dir = solved.target_dir;
@@ -36,7 +37,8 @@ pub(crate) fn handle_target_dir_entry(_prev: ReadTargetDirEntry) -> Next {
}
#[chain]
-pub(crate) fn handle_workspace_root_entry(_prev: ReadWorkspaceRootEntry) -> Next {
+#[allow(unused_variables)]
+pub(crate) fn handle_workspace_root_entry(entry: ReadWorkspaceRootEntry) -> Next {
match solve_current_dir() {
Ok(solved) => {
let dir = solved.workspace_root;
@@ -47,7 +49,8 @@ pub(crate) fn handle_workspace_root_entry(_prev: ReadWorkspaceRootEntry) -> Next
}
#[chain]
-pub(crate) fn handle_binaries_entry(_prev: ReadBinariesEntry) -> Next {
+#[allow(unused_variables)]
+pub(crate) fn handle_binaries_entry(entry: ReadBinariesEntry) -> Next {
match solve_current_dir() {
Ok(solved) => {
let binaries = solved.binaries;
diff --git a/mling/src/display.rs b/mling/src/display.rs
index 9182692..3816d89 100644
--- a/mling/src/display.rs
+++ b/mling/src/display.rs
@@ -120,8 +120,7 @@ pub fn markdown(text: impl AsRef<str>) -> String {
// Format heading as white background, black text, bold
// ANSI codes: \x1b[1m for bold, \x1b[47m for white background, \x1b[30m for black text
- let formatted_heading =
- format!("\x1b[1m\x1b[47m\x1b[30m {} \x1b[0m", processed_content);
+ let formatted_heading = format!("\x1b[1m\x1b[47m\x1b[30m {processed_content} \x1b[0m");
// Add indentation to the heading line itself
// Heading indentation = level - 1
@@ -187,7 +186,7 @@ fn process_line_with_quote(line: &str) -> String {
let processed_rest = process_line(&rest_of_line);
// Combine the gray background space with the processed rest
- format!("{}{}", gray_bg_space, processed_rest)
+ format!("{gray_bg_space}{processed_rest}")
} else {
// No > at the beginning, process normally
process_line(line)
@@ -266,7 +265,7 @@ fn process_line(line: &str) -> String {
&& let Some(end) = find_matching(&chars, i + 1, "_")
{
let underline_text: String = chars[i + 1..end].iter().collect();
- let mut formatted_text = format!("\x1b[4m{}\x1b[0m", underline_text);
+ let mut formatted_text = format!("\x1b[4m{underline_text}\x1b[0m");
apply_color_stack(&mut formatted_text, &color_stack);
result.push_str(&formatted_text);
i = end + 1;
@@ -357,16 +356,18 @@ fn apply_color(text: impl AsRef<str>, color_name: impl AsRef<str>) -> String {
let text = text.as_ref();
let color_name = color_name.as_ref();
match color_name {
- // Normal colors
- "black" => text.black().to_string(),
- "red" => text.red().to_string(),
- "green" => text.green().to_string(),
- "yellow" => text.yellow().to_string(),
- "blue" => text.blue().to_string(),
- "magenta" => text.magenta().to_string(),
- "cyan" => text.cyan().to_string(),
- "white" => text.white().to_string(),
- "bright_black" => text.bright_black().to_string(),
+ // Normal colors and their bright short aliases
+ "black" | "b_black" => text.black().to_string(),
+ "red" | "b_red" => text.red().to_string(),
+ "green" | "b_green" => text.green().to_string(),
+ "yellow" | "b_yellow" => text.yellow().to_string(),
+ "blue" | "b_blue" => text.blue().to_string(),
+ "magenta" | "b_magenta" => text.magenta().to_string(),
+ "cyan" | "b_cyan" => text.cyan().to_string(),
+ "white" | "b_white" | "bright_gray" | "bright_grey" | "b_gray" | "b_grey" => {
+ text.white().to_string()
+ }
+ "bright_black" | "gray" | "grey" => text.bright_black().to_string(),
"bright_red" => text.bright_red().to_string(),
"bright_green" => text.bright_green().to_string(),
"bright_yellow" => text.bright_yellow().to_string(),
@@ -375,21 +376,6 @@ fn apply_color(text: impl AsRef<str>, color_name: impl AsRef<str>) -> String {
"bright_cyan" => text.bright_cyan().to_string(),
"bright_white" => text.bright_white().to_string(),
- // Short aliases for bright colors
- "b_black" => text.black().to_string(),
- "b_red" => text.red().to_string(),
- "b_green" => text.green().to_string(),
- "b_yellow" => text.yellow().to_string(),
- "b_blue" => text.blue().to_string(),
- "b_magenta" => text.magenta().to_string(),
- "b_cyan" => text.cyan().to_string(),
- "b_white" => text.white().to_string(),
-
- // Gray colors using truecolor
- "gray" | "grey" => text.bright_black().to_string(),
- "bright_gray" | "bright_grey" => text.white().to_string(),
- "b_gray" | "b_grey" => text.white().to_string(),
-
// Default to white if color not recognized
_ => text.to_string(),
}
diff --git a/mling/src/namespace_manager.rs b/mling/src/namespace_manager.rs
index 51181c3..d5176dd 100644
--- a/mling/src/namespace_manager.rs
+++ b/mling/src/namespace_manager.rs
@@ -2,6 +2,7 @@ use std::path::PathBuf;
use just_fmt::kebab_case;
+#[must_use]
pub fn list_namespaces(
show_trusted: bool,
show_untrusted: bool,
@@ -13,14 +14,12 @@ pub fn list_namespaces(
}
let mut namespaces = Vec::new();
- let entries = match std::fs::read_dir(&wdir) {
- Ok(entries) => entries,
- Err(_) => return Vec::new(),
+ let Ok(entries) = std::fs::read_dir(&wdir) else {
+ return Vec::new();
};
for entry in entries {
- let entry = match entry {
- Ok(e) => e,
- Err(_) => continue,
+ let Ok(entry) = entry else {
+ continue;
};
let path = entry.path();
if path.is_dir()
@@ -71,24 +70,34 @@ pub fn remove_namespace(namespace: String) {
}
}
+/// Returns the mingling data directory.
+///
+/// # Panics
+///
+/// Panics if the platform's data directory cannot be determined.
+#[must_use]
pub fn working_dir() -> PathBuf {
dirs::data_dir().unwrap().join("mingling")
}
+#[must_use]
pub fn namespace_dir(namespace: String) -> PathBuf {
working_dir().join(kebab_case!(namespace))
}
+#[must_use]
pub fn is_untrusted_namespace(namespace: String) -> bool {
let untrusted_file = namespace_dir(namespace).join("UNTRUSTED");
untrusted_file.exists()
}
+#[must_use]
pub fn is_trusted_namespace(namespace: String) -> bool {
let trusted = namespace_dir(namespace).join("TRUSTED");
trusted.exists()
}
+#[must_use]
pub fn is_untagged_namespace(namespace: String) -> bool {
let ndir = namespace_dir(namespace);
let trusted = ndir.join("TRUSTED");
@@ -96,14 +105,17 @@ pub fn is_untagged_namespace(namespace: String) -> bool {
!trusted.exists() && !untrusted.exists()
}
+#[must_use]
pub fn bin_dir(namespace: String) -> PathBuf {
namespace_dir(namespace).join("bin")
}
+#[must_use]
pub fn comp_dir(namespace: String) -> PathBuf {
namespace_dir(namespace).join("comp")
}
+#[must_use]
pub fn exe_path(namespace: String, bin_name_without_ext: String) -> PathBuf {
if cfg!(target_os = "windows") {
bin_dir(namespace).join(bin_name_without_ext + ".exe")
diff --git a/mling/src/project_installer.rs b/mling/src/project_installer.rs
index 2e9ca8d..983307f 100644
--- a/mling/src/project_installer.rs
+++ b/mling/src/project_installer.rs
@@ -1,5 +1,3 @@
-use std::path::PathBuf;
-
use mingling::{ShellFlag, build::build_comp_script_to};
use crate::{
@@ -21,15 +19,27 @@ struct Package {
name: String,
}
+/// Installs all projects and shell scripts.
+///
+/// # Errors
+///
+/// Returns an `io::Error` if the current directory cannot be determined, if the project
+/// installation fails, or if the shell scripts cannot be installed.
pub fn install_all(clean_before_build: bool) -> Result<(), std::io::Error> {
let current = std::env::current_dir()?;
- install_this_project(current, clean_before_build)?;
+ install_this_project(&current, clean_before_build)?;
install_shell_scripts()?;
Ok(())
}
+/// Installs a project from the given path.
+///
+/// # Errors
+///
+/// Returns an `io::Error` if the project installation fails, e.g., if `cargo build`
+/// fails, the Cargo.toml cannot be parsed, or file operations (copy, create dir) fail.
pub fn install_this_project(
- current: PathBuf,
+ current: &std::path::PathBuf,
clean_before_build: bool,
) -> Result<(), std::io::Error> {
// Obtain context data
@@ -95,6 +105,11 @@ pub fn install_this_project(
Ok(())
}
+/// Installs shell completion scripts for the `mling` command.
+///
+/// # Errors
+///
+/// Returns an `io::Error` if the shell scripts cannot be built or installed.
pub fn install_shell_scripts() -> Result<(), std::io::Error> {
// Get the working directory (mingling data dir)
let wdir = working_dir();
@@ -138,7 +153,7 @@ pub fn install_shell_scripts() -> Result<(), std::io::Error> {
.args(["+x", &dest.to_string_lossy()])
.status()?;
if !status.success() {
- eprintln!("Failed to chmod {}", filename);
+ eprintln!("Failed to chmod {filename}");
}
}
}
diff --git a/mling/src/project_solver.rs b/mling/src/project_solver.rs
index b6517b3..3aec2b4 100644
--- a/mling/src/project_solver.rs
+++ b/mling/src/project_solver.rs
@@ -17,13 +17,24 @@ pub struct BinaryItem {
pub path: PathBuf,
}
+/// Solves the current directory for project metadata.
+///
+/// # Errors
+///
+/// Returns an `io::Error` if the current directory cannot be determined
+/// or if `cargo metadata` fails.
pub fn solve_current_dir() -> Result<ProjectSolveResult, std::io::Error> {
let current = std::env::current_dir()?;
- solve(current)
+ solve(&current)
}
-pub fn solve(current: PathBuf) -> Result<ProjectSolveResult, std::io::Error> {
- let (target_dir, workspace_root, binaries) = solve_inner(&current)?;
+/// Solves the given directory path for project metadata.
+///
+/// # Errors
+///
+/// Returns an `io::Error` if `cargo metadata` fails for the given path.
+pub fn solve(current: &PathBuf) -> Result<ProjectSolveResult, std::io::Error> {
+ let (target_dir, workspace_root, binaries) = solve_inner(current)?;
Ok(ProjectSolveResult {
target_dir,
workspace_root,
@@ -40,9 +51,9 @@ fn solve_inner(current: &PathBuf) -> Result<(PathBuf, PathBuf, Vec<BinaryItem>),
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
- return Err(std::io::Error::other(
- format!("cargo metadata failed: {}", stderr),
- ));
+ return Err(std::io::Error::other(format!(
+ "cargo metadata failed: {stderr}"
+ )));
}
let metadata: serde_json::Value = serde_json::from_slice(&output.stdout)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
@@ -75,9 +86,7 @@ fn solve_inner(current: &PathBuf) -> Result<(PathBuf, PathBuf, Vec<BinaryItem>),
if let Some(targets) = pkg["targets"].as_array() {
for target in targets {
let kind = target["kind"].as_array();
- let is_bin = kind
- .map(|k| k.iter().any(|v| v.as_str() == Some("bin")))
- .unwrap_or(false);
+ let is_bin = kind.is_some_and(|k| k.iter().any(|v| v.as_str() == Some("bin")));
if is_bin {
let name = target["name"].as_str().ok_or_else(|| {
std::io::Error::new(