aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--.run/Cargo.lock40
-rw-r--r--.run/Cargo.toml1
-rw-r--r--.run/src/bin/ci.rs29
-rwxr-xr-x[-rw-r--r--].run/src/bin/clippy.sh0
-rw-r--r--.run/src/bin/deploy-api-docs.rs23
-rw-r--r--.run/src/bin/doc-nightly.ps16
-rw-r--r--.run/src/bin/doc-nightly.sh8
-rw-r--r--.run/version-files.toml4
-rw-r--r--CHANGELOG.md59
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml6
-rw-r--r--GETTING_STARTED.md4
-rw-r--r--arg_picker/Cargo.toml2
-rw-r--r--arg_picker_macros/Cargo.toml2
-rw-r--r--build.rs13
-rw-r--r--docs/_zh_CN/pages/13-hook.md2
-rw-r--r--docs/_zh_CN/pages/advanced/1-completion.md4
-rw-r--r--docs/pages/13-hook.md2
-rw-r--r--docs/pages/advanced/1-completion.md4
-rw-r--r--examples/example-completion/Cargo.toml4
-rw-r--r--examples/example-completion/src/main.rs2
-rw-r--r--examples/example-hook/src/main.rs2
-rw-r--r--examples/example-pathfinder/Cargo.toml4
-rw-r--r--mingling/Cargo.toml6
-rw-r--r--mingling/src/example_docs.rs12
-rw-r--r--mingling/src/features.rs11
-rw-r--r--mingling/src/lib.rs1
-rw-r--r--mingling_cli/Cargo.lock69
-rw-r--r--mingling_cli/Cargo.toml13
-rw-r--r--mingling_cli/README.md2
-rw-r--r--mingling_cli/expand.rs1121
-rw-r--r--mingling_cli/help/help.txt0
-rw-r--r--mingling_cli/src/bin/wrapper.rs47
-rw-r--r--mingling_cli/src/errors.rs2
-rw-r--r--mingling_cli/src/errors/io_error.rs8
-rw-r--r--mingling_cli/src/linter.rs13
-rw-r--r--mingling_cli/src/linter/cmd_mlint.rs73
-rw-r--r--mingling_cli/src/linter/mlint_report.rs12
-rw-r--r--mingling_cli/src/lints.rs2
-rw-r--r--mingling_cli/src/lints/non_mingling_naming_style.rs4
-rw-r--r--mingling_cli/src/lints/template_linter.rs2
-rw-r--r--mingling_cli/src/lints/unnecessary_render_result_creation.rs221
-rw-r--r--mingling_cli/tmpls/lints.tmpl2
-rw-r--r--mingling_core/Cargo.toml2
-rw-r--r--mingling_core/src/any.rs128
-rw-r--r--mingling_core/src/any/group.rs12
-rw-r--r--mingling_core/src/asset.rs35
-rw-r--r--mingling_core/src/asset/global_resource.rs5
-rw-r--r--mingling_core/src/build.rs13
-rw-r--r--mingling_core/src/build/comp.rs (renamed from mingling_core/src/builds/comp.rs)0
-rw-r--r--mingling_core/src/build/pathf.rs (renamed from mingling_core/src/builds/pathf.rs)0
-rw-r--r--mingling_core/src/builds.rs7
-rw-r--r--mingling_core/src/lib.rs120
-rw-r--r--mingling_core/src/private.rs12
-rw-r--r--mingling_core/src/program/collection/mock.rs7
-rw-r--r--mingling_core/src/program/hook.rs12
-rw-r--r--mingling_core/src/renderer/render_result.rs2
-rw-r--r--mingling_core/tests/test-all/tests/integration.rs2
-rw-r--r--mingling_core/tests/test-comp/tests/integration.rs2
-rw-r--r--mingling_macros/src/derive/grouped.rs12
-rw-r--r--mingling_macros/src/func/gen_program.rs22
-rw-r--r--mingling_macros/src/func/group.rs6
-rw-r--r--mingling_macros/src/func/pack.rs6
-rw-r--r--mingling_macros/src/systems/structural_data.rs12
-rw-r--r--src/main.rs1
66 files changed, 930 insertions, 1344 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0a6a9a6..71b63ca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -82,8 +82,11 @@ jobs:
uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
+ - name: Install nightly toolchain
+ run: rustup toolchain install nightly
+
- name: Build API docs
- run: cargo run --manifest-path .run/Cargo.toml --bin deploy-api-docs
+ run: cargo +nightly run --manifest-path .run/Cargo.toml --bin deploy-api-docs -- --docsrs
- name: Install cargo-llvm-cov
run: cargo install cargo-llvm-cov
@@ -91,12 +94,17 @@ jobs:
- name: Run cov-test
run: cargo run --manifest-path .run/Cargo.toml --bin cov-test
+ - name: Delete .temp directory before deployment
+ run: rm -rf .temp
+
- name: Setup Pages
uses: actions/configure-pages@v5
+
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: "."
+
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
diff --git a/.run/Cargo.lock b/.run/Cargo.lock
index c255ad0..3117d0b 100644
--- a/.run/Cargo.lock
+++ b/.run/Cargo.lock
@@ -9,6 +9,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
+name = "arg-picker"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd62c395708a956e98e06b6b18200b2a65a5ae63448ed7d59cca32833aaf7265"
+dependencies = [
+ "arg-picker-macros",
+ "just_fmt 0.2.0",
+]
+
+[[package]]
+name = "arg-picker-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04fddbb5c1f26450cfc6aded0559b759b292d400dba1ff4e45128f3e0e1ffe6b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -175,12 +196,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e"
[[package]]
+name = "just_fmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6170dccbc3ea15dfb7f2da964097f814aba1dd8f746d4ffc56f33245c38e6d96"
+
+[[package]]
name = "just_template"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3edb658c34b10b69c4b3b58f7ba989cd09c82c0621dee1eef51843c2327225"
dependencies = [
- "just_fmt",
+ "just_fmt 0.1.2",
]
[[package]]
@@ -240,9 +267,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.45"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
@@ -332,9 +359,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
-version = "2.0.117"
+version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
@@ -418,10 +445,11 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
name = "tools"
version = "0.1.0"
dependencies = [
+ "arg-picker",
"colored",
"flate2",
"indicatif",
- "just_fmt",
+ "just_fmt 0.1.2",
"just_template",
"serde",
"serde_json",
diff --git a/.run/Cargo.toml b/.run/Cargo.toml
index caeb506..4935f2d 100644
--- a/.run/Cargo.toml
+++ b/.run/Cargo.toml
@@ -21,5 +21,6 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
indicatif = "0.18.4"
flate2 = "1"
tar = "0.4"
+arg-picker = "0.1.0"
[workspace]
diff --git a/.run/src/bin/ci.rs b/.run/src/bin/ci.rs
index f3ae9e8..4b6f973 100644
--- a/.run/src/bin/ci.rs
+++ b/.run/src/bin/ci.rs
@@ -1,6 +1,7 @@
use std::io::Write as _;
use std::process::exit;
+use arg_picker::{Picker, macros::arg};
use tools::{
cargo_tomls, crate_name_from, eprintln_cargo_style, println_cargo_style, run_cmd, run_parallel,
};
@@ -35,19 +36,20 @@ fn main() {
let _ = colored::control::set_virtual_terminal(true);
println!("{}", include_str!("../../../docs/res/ci_banner.txt"));
- let args: Vec<String> = std::env::args().collect();
+ let (auto_yes, dirty, test_docs, refresh_docs, test_codes, help) = Picker::from_args()
+ .pick_or_default(&arg![yes: bool, 'y'])
+ .pick_or_default(&arg![dirty: bool])
+ .pick_or_default(&arg![test_docs: bool])
+ .pick_or_default(&arg![refresh_docs: bool])
+ .pick_or_default(&arg![test_codes: bool])
+ .pick_or_default(&arg![help: bool, 'h'])
+ .unwrap();
- if args.iter().any(|a| a == "-h" || a == "--help") {
+ if help {
print_help();
return;
}
- let auto_yes = args.iter().any(|a| a == "-y");
- let dirty = args.iter().any(|a| a == "--dirty");
-
- let test_docs = args.iter().any(|a| a == "--test-docs");
- let refresh_docs = args.iter().any(|a| a == "--refresh-docs");
- let test_codes = args.iter().any(|a| a == "--test-codes");
let any_specified = test_docs || refresh_docs || test_codes;
let run_all = !any_specified;
@@ -140,6 +142,11 @@ fn ci(test_docs: bool, test_codes: bool, run_all: bool) -> Result<(), i32> {
exit_code = exit_code.max(code);
}
+ println_cargo_style!("Phase: Try Build API docs");
+ if let Err(code) = deploy_api_docs() {
+ exit_code = exit_code.max(code);
+ }
+
if exit_code != 0 {
return Err(exit_code);
}
@@ -223,6 +230,12 @@ fn test_all() -> Result<(), i32> {
run_parallel("Testing", tasks)
}
+fn deploy_api_docs() -> Result<(), i32> {
+ run_cmd!(
+ "cargo run --manifest-path .run/Cargo.toml --color always --bin deploy-api-docs -- --docsrs"
+ )
+}
+
fn docs_refresh() -> Result<(), i32> {
println_cargo_style!("Refresh: document at `./docs/`");
diff --git a/.run/src/bin/clippy.sh b/.run/src/bin/clippy.sh
index b393545..b393545 100644..100755
--- a/.run/src/bin/clippy.sh
+++ b/.run/src/bin/clippy.sh
diff --git a/.run/src/bin/deploy-api-docs.rs b/.run/src/bin/deploy-api-docs.rs
index 7aa093c..961eb04 100644
--- a/.run/src/bin/deploy-api-docs.rs
+++ b/.run/src/bin/deploy-api-docs.rs
@@ -1,10 +1,15 @@
use std::path::Path;
+use arg_picker::{Picker, macros::arg};
use tools::{println_cargo_style, run_cmd};
const OUTPUT_DIR: &str = "docs/api-docs";
fn main() {
+ let using_docsrs = Picker::from_args()
+ .pick_or_default(&arg![docsrs: bool])
+ .unwrap();
+
let repo_root = find_git_repo().expect("Failed to find git repository root");
// Read features from [package.metadata.docs.rs]
@@ -20,11 +25,19 @@ fn main() {
std::fs::create_dir_all(&output_path).expect("Failed to create output directory");
// Build cargo doc command
- let cmd = format!(
- "cargo doc --no-deps --features \"{}\" -p mingling --target-dir \"{}\" --color always",
- features_arg,
- output_path.join("target").to_string_lossy()
- );
+ let cmd = if using_docsrs {
+ format!(
+ "cargo +nightly rustdoc --features \"{}\" -p mingling --target-dir \"{}\" --color always -- --cfg docsrs",
+ features_arg,
+ output_path.join("target").to_string_lossy()
+ )
+ } else {
+ format!(
+ "cargo doc --no-deps --features \"{}\" -p mingling --target-dir \"{}\" --color always",
+ features_arg,
+ output_path.join("target").to_string_lossy()
+ )
+ };
println_cargo_style!("Features: {}", features_arg);
println_cargo_style!("Output: {}", output_path.display());
diff --git a/.run/src/bin/doc-nightly.ps1 b/.run/src/bin/doc-nightly.ps1
new file mode 100644
index 0000000..30d6aaf
--- /dev/null
+++ b/.run/src/bin/doc-nightly.ps1
@@ -0,0 +1,6 @@
+cargo +nightly rustdoc `
+ --manifest-path mingling/Cargo.toml `
+ --features docs_rs,core,macros,builds,structural_renderer,repl,comp,parser,picker,clap,extra_macros `
+ --open `
+ -- `
+ --cfg docsrs
diff --git a/.run/src/bin/doc-nightly.sh b/.run/src/bin/doc-nightly.sh
new file mode 100644
index 0000000..944f4b3
--- /dev/null
+++ b/.run/src/bin/doc-nightly.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+cargo rustdoc \
+ --manifest-path mingling/Cargo.toml \
+ --features docs_rs,core,macros,builds,structural_renderer,repl,comp,parser,picker,clap,extra_macros \
+ --open \
+ -- \
+ --cfg docsrs
diff --git a/.run/version-files.toml b/.run/version-files.toml
index ca104d2..b69a568 100644
--- a/.run/version-files.toml
+++ b/.run/version-files.toml
@@ -17,3 +17,7 @@ pattern = "version = \"{VER}\""
[[file]]
file = "./docs/res/guide.txt"
pattern = "mingling = \"{VER}\""
+
+[[file]]
+file = "./mingling_core/src/lib.rs"
+pattern = "mingling = \"{VER}\""
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ed528c..62ec971 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,16 @@ None
### Release 0.3.0 (Unreleased)
+> In detail, the changes in Mingling 0.3.0 are as follows:
+
+1. **Added `arg-picker`** — Mingling has never had a comfortable argument parsing solution. You either suffered with `parser` or went all-in on `clap`. So I wrote a smarter `arg-picker`. The API style is close to the original `parser`, but it's more type-safe, more robust, and more extensible. See the main text for details.
+
+2. **Made implicit behavior explicit**. For a long time, Mingling's attribute macros have been making **implicit** modifications to the original function — I have to admit, that's dirty. In the new version, I've removed **all** implicit modifications to the original function by attribute macros. In other words, `#[chain]`, `#[renderer]`, `#[help]`, and `#[completion]` will no longer modify your original function in any way unless you explicitly specify it. Use the Extension Attribute (`#[chain(/* ... */)]`) mechanism to explicitly inject implicit behavior into your functions.
+
+3. I was originally planning to remove `r_println!` because I couldn't stand that `__renderer_inner_result` thing implicitly injected by the `#[renderer]` macro. But now I've rewritten it: `#[buffer]` injects an implicit `__render_result_buffer` value into the function, and then `r_println!` calls it. It's an extra step, but it also means: the dirt is your choice, not something I'm forcing on you :)
+
+4. **Finally**, a philosophical point. Mingling will move forward with a preference for **"selectively dirty"** over **"invisibly, forcibly dirty"** — this is the biggest direction going forward, building a more comfortable API on this foundation.
+
#### Fixes:
1. **[`pathf:patterns`]** Fixed pattern detection logic in `mingling_pathf` for multiple patterns to correctly detect opening bracket forms (e.g., `[chain`, `[renderer`, `[help`, `[completion`) in addition to the previously supported closing bracket forms (e.g., `chain]`, `renderer]`, `help]`, `completion]`). This ensures that attribute macro usages like `#[chain]`, `#[renderer]`, `#[help]`, and `#[completion]` are properly detected regardless of which side of the attribute the pattern matcher examines.
@@ -154,6 +164,20 @@ None
_No behavioral changes — this is purely a type-system refactoring to enable `group_structural!` to work with external types. Since both traits are defined in `::mingling::__private`, this change has **no impact on the public API** — end users interact with `StructuralData` only through auto-generated derive macros and `pack_structural!`/`group_structural!` macros, which are automatically updated. Only users with manual `impl StructuralData` blocks (an advanced/rare case) need to update their code.
+7. **[`core`]** **`RenderResult` now derives `Clone` and `Eq` in addition to `Default`, `Debug`, and `PartialEq`.** Added `Clone` and `Eq` derive macros to the `RenderResult` struct in `mingling_core/src/renderer/render_result.rs`. These additions enable `RenderResult` values to be explicitly cloned and support equality comparisons that are both reflexive and transitive.
+
+ - **`Clone`** — Allows a `RenderResult` to be duplicated via `.clone()`, which is useful for scenarios where the same render output needs to be reused or stored in multiple locations.
+ - **`Eq`** — Enables `RenderResult` to be used in contexts that require full equivalence (e.g., `assert_eq!` with `Eq` bounds, `HashMap`/`HashSet` keys when combined with `Hash`).
+
+ _No migration is required — these are purely additive derives that expand the type's capabilities without affecting existing behavior._
+
+**[`core`]** Added the `build` feature (renamed from `builds`) to `mingling_core` and `mingling`. The old `builds` feature has been deprecated in favor of `build`, with a backward-compatibility alias retained in `mingling/Cargo.toml`:
+
+- **`mingling_core/Cargo.toml`**: Renamed the feature from `builds` to `build`.
+- **`mingling/Cargo.toml`**: Changed the feature dependency from `mingling_core/builds` to `mingling_core/build`. A deprecated `builds` feature alias is kept as `builds = ["mingling_core/build"]` with a note indicating it will be removed in a future breaking change.
+
+ _No behavioral changes — the `build` feature provides identical functionality to the old `builds` feature. Downstream code using `builds` continues to work via the alias, but should migrate to `build`._
+
#### Features:
1. **[`core`]** Added `RenderResult::new()` method for creating a new `RenderResult` with default values (empty text and exit code 0). This provides a more explicit and discoverable constructor compared to `RenderResult::default()`, making it clearer when a fresh result is being created for use with `write!`/`writeln!`.
@@ -556,6 +580,41 @@ None
All examples and internal usages have been updated across the codebase to reflect these changes (e.g., `repl_basic_setup` now calls `println!("{}", r.result)` instead of `println!("{}", r.result.trim())`, since `Display` no longer adds a trailing newline).
+7. **[`any`]** **[`macros`]** Made `AnyOutput`'s `type_id` and `member_id` fields private (`pub(crate)`) and added public accessor methods `type_id()` and `member_id()`. Added the `unsafe fn new_bare<T>(value: T, member_id: G) -> Self` constructor that bypasses the `Grouped` trait, allowing manual specification of `member_id` without requiring the concrete type to implement `Grouped`.
+
+ - **`type_id`** field changed from `pub` to `pub(crate)` — accessible via `type_id()` accessor.
+ - **`member_id`** field changed from `pub` to `pub(crate)` — accessible via `member_id()` accessor (requires `G: Copy`).
+ - **`new_bare`** — Unsafe constructor that takes a raw `member_id` value without invoking `Grouped::member_id()`. The caller must ensure the provided `member_id` correctly corresponds to the concrete type `T`.
+ - Updated all internal `match any.member_id { ... }` patterns in `gen_program.rs` to use `match any.member_id() { ... }` instead.
+ - Updated the panic message in `do_chain` (both sync and async) from `any.type_id` to `any.type_id()`.
+ - Updated the example-hook `main.rs` to call `info.output.member_id()` instead of accessing `info.output.member_id` directly.
+ - Added `Copy` derive to the generated enum to enable `member_id()`'s `Copy` requirement on the enum type.
+
+ _No behavioral changes for existing code — the accessor methods provide the same values as the previously-public fields._
+
+8. **[`any`]** **[`macros`]** **[BREAKING]** Marked `Grouped` trait as `unsafe trait`. The `Grouped` trait has always been inherently unsafe — the `member_id()` return value must exactly correspond to the variant registered by `register_type!` for the concrete type, otherwise dispatching on that type will result in **undefined behavior**. This unsoundness has existed since the trait's inception but was previously unenforced at the type system level.
+
+ By making `Grouped` an `unsafe trait`, implementors must now explicitly acknowledge this safety contract with `unsafe impl Grouped<...> for ...`. This change makes the existing safety invariant visible to developers and enables soundness warnings at compile time.
+
+ **Changes made:**
+
+ - **`Grouped` trait** in `mingling_core/src/any/group.rs` changed from `pub trait Grouped<Group>` to `pub unsafe trait Grouped<Group>`, with a safety doc comment explaining that manually implementing the trait with an incorrect `member_id` leads to undefined behavior.
+
+ - **Derive macros** (`#[derive(Grouped)]`, `#[derive(GroupedSerialize)]`) now generate `unsafe impl` instead of `impl`, with a SAFETY comment stating that the derive macro guarantees correctness because the `Ident` used in `register_type!` matches the `Ident` returned by `member_id()`.
+
+ - **`pack!`, `pack_structural!`, `group!`, `group_structural!`** macros now generate `unsafe impl` instead of `impl`, with analogous SAFETY comments.
+
+ - **All manual test implementations** of `Grouped` across the codebase (in `any.rs` tests, `hook.rs` tests, `mock.rs`) updated to `unsafe impl` with SAFETY comments explaining why they are safe in their test contexts.
+
+ - **`MockProgramCollect::member_id()`** changed from `MockProgramCollect::Foo` to `panic!("Attempting to read an unsafe enum type")` to prevent accidental execution in production paths.
+
+ **Migration guide:**
+
+ - Existing code that uses `Grouped` only through the derive macro or `pack!`/`group!` macros is automatically migrated — no changes needed.
+ - Code with **manual** `impl Grouped<...> for ...` blocks must add `unsafe` before `impl` and verify that the `member_id()` return value correctly corresponds to the type's registered variant. Only proceed if the correspondence is guaranteed.
+
+ _This is a breaking change only for code with manual `Grouped` implementations._
+
---
## Release 0.2.2 (2026-07-10)
diff --git a/Cargo.lock b/Cargo.lock
index 5db81a8..77fcf77 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -270,6 +270,10 @@ dependencies = [
]
[[package]]
+name = "mingling-workspace"
+version = "0.3.0"
+
+[[package]]
name = "mingling_core"
version = "0.3.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index 8baadbb..558a4b8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,3 +77,9 @@ lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
+
+[package]
+name = "mingling-workspace"
+version.workspace = true
+edition.workspace = true
+publish = false
diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md
index 1aad8d5..516122e 100644
--- a/GETTING_STARTED.md
+++ b/GETTING_STARTED.md
@@ -239,7 +239,7 @@ In your `build.rs`, generate the shell scripts:
```rust
// BUILD TIME
-// Features: ["comp", "builds"]
+// Features: ["comp", "build"]
mingling::build::build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap();
```
@@ -551,7 +551,7 @@ fn main() {
.on_pre_chain(|info| {
println!("[DEBUG] Pre chain: {}", info.input);
})
- .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id))
+ .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id()))
.on_finish(|_| {
println!("[DEBUG] Loop end");
ProgramControlUnit::OverrideExitCode(0) // Override exit code
diff --git a/arg_picker/Cargo.toml b/arg_picker/Cargo.toml
index d87a844..ac4041d 100644
--- a/arg_picker/Cargo.toml
+++ b/arg_picker/Cargo.toml
@@ -3,7 +3,7 @@ name = "arg-picker"
version = "0.1.0"
edition = "2024"
license = "MIT OR Apache-2.0"
-repository = "https://github.com/mingling-rs/mingling/tree/main/arg-picker"
+repository = "https://github.com/mingling-rs/mingling/tree/main/arg_picker"
authors = ["Weicao-CatilGrass"]
readme = "README.md"
description = "A lightweight, type-safe CLI argument parser"
diff --git a/arg_picker_macros/Cargo.toml b/arg_picker_macros/Cargo.toml
index c83cb9e..c329274 100644
--- a/arg_picker_macros/Cargo.toml
+++ b/arg_picker_macros/Cargo.toml
@@ -3,7 +3,7 @@ name = "arg-picker-macros"
version = "0.1.0"
edition = "2024"
license = "MIT OR Apache-2.0"
-repository = "https://github.com/mingling-rs/mingling/tree/main/arg-picker"
+repository = "https://github.com/mingling-rs/mingling/tree/main/arg_picker"
authors = ["Weicao-CatilGrass"]
readme = "README.md"
description = "Mingling's lightweight argument parser macros"
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..951ecf8
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,13 @@
+use std::{env::current_dir, fs};
+
+fn main() {
+ gen_fake_cargo_toml_in_temp_dir();
+}
+
+fn gen_fake_cargo_toml_in_temp_dir() {
+ fs::write(
+ current_dir().unwrap().join(".temp").join("Cargo.toml"),
+ "[workspace]",
+ )
+ .unwrap();
+}
diff --git a/docs/_zh_CN/pages/13-hook.md b/docs/_zh_CN/pages/13-hook.md
index 6d6018a..ad3008a 100644
--- a/docs/_zh_CN/pages/13-hook.md
+++ b/docs/_zh_CN/pages/13-hook.md
@@ -71,7 +71,7 @@ fn main() {
eprintln!("[hook] executing chain for: {}", info.input);
})
.on_post_chain(|info| {
- eprintln!("[hook] chain output: {}", info.output.member_id);
+ eprintln!("[hook] chain output: {}", info.output.member_id());
}),
);
diff --git a/docs/_zh_CN/pages/advanced/1-completion.md b/docs/_zh_CN/pages/advanced/1-completion.md
index 3941404..a6500db 100644
--- a/docs/_zh_CN/pages/advanced/1-completion.md
+++ b/docs/_zh_CN/pages/advanced/1-completion.md
@@ -15,8 +15,8 @@ features = ["comp"]
[build-dependencies.mingling]
features = [
"comp",
- # 启用 `builds` 特性以提供构建期支持
- "builds"
+ # 启用 `build` 特性以提供构建期支持
+ "build"
]
```
diff --git a/docs/pages/13-hook.md b/docs/pages/13-hook.md
index 26f8712..d927a9c 100644
--- a/docs/pages/13-hook.md
+++ b/docs/pages/13-hook.md
@@ -71,7 +71,7 @@ fn main() {
eprintln!("[hook] executing chain for: {}", info.input);
})
.on_post_chain(|info| {
- eprintln!("[hook] chain output: {}", info.output.member_id);
+ eprintln!("[hook] chain output: {}", info.output.member_id());
}),
);
diff --git a/docs/pages/advanced/1-completion.md b/docs/pages/advanced/1-completion.md
index a90c3ce..52350db 100644
--- a/docs/pages/advanced/1-completion.md
+++ b/docs/pages/advanced/1-completion.md
@@ -15,8 +15,8 @@ features = ["comp"]
[build-dependencies.mingling]
features = [
"comp",
- # Enable `builds` for build-time support
- "builds"
+ # Enable `build` for build-time support
+ "build"
]
```
diff --git a/examples/example-completion/Cargo.toml b/examples/example-completion/Cargo.toml
index 1c6b327..00c7f8c 100644
--- a/examples/example-completion/Cargo.toml
+++ b/examples/example-completion/Cargo.toml
@@ -20,8 +20,8 @@ features = [
"comp",
# If you want to build completion scripts,
- # enable `builds` features
- "builds",
+ # enable `build` features
+ "build",
]
[workspace]
diff --git a/examples/example-completion/src/main.rs b/examples/example-completion/src/main.rs
index 45cc8ef..d65be49 100644
--- a/examples/example-completion/src/main.rs
+++ b/examples/example-completion/src/main.rs
@@ -7,7 +7,7 @@
//! To make your completions work, you need to generate a completion script using Mingling's tools
//!
//! 1. Enable features
-//! You need to enable the `builds` and `comp` features for `mingling` in `[build-dependencies]`
+//! You need to enable the `build` and `comp` features for `mingling` in `[build-dependencies]`
//!
//! 2. Write `build.rs`
//! Write the following in `build.rs`
diff --git a/examples/example-hook/src/main.rs b/examples/example-hook/src/main.rs
index 23b87c7..da92045 100644
--- a/examples/example-hook/src/main.rs
+++ b/examples/example-hook/src/main.rs
@@ -40,7 +40,7 @@ fn main() {
.on_pre_chain(|info| {
println!("[DEBUG] Pre chain: {}", info.input);
})
- .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id))
+ .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id()))
.on_finish(|_| {
println!("[DEBUG] Loop end");
ProgramControlUnit::OverrideExitCode(0) // Override exit code
diff --git a/examples/example-pathfinder/Cargo.toml b/examples/example-pathfinder/Cargo.toml
index bd362f2..bc41bd2 100644
--- a/examples/example-pathfinder/Cargo.toml
+++ b/examples/example-pathfinder/Cargo.toml
@@ -18,8 +18,8 @@ features = [
# Enable `pathf` features
"pathf",
- # Enable the `builds` feature for build-time support
- "builds",
+ # Enable the `build` feature for build-time support
+ "build",
]
[workspace]
diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml
index 351384f..9bf82a7 100644
--- a/mingling/Cargo.toml
+++ b/mingling/Cargo.toml
@@ -43,7 +43,8 @@ macros = ["dep:mingling_macros", "mingling_macros/default"]
nightly = ["mingling_core/nightly", "mingling_macros/nightly"]
debug = ["mingling_core/debug"]
async = ["mingling_core/async", "mingling_macros/async"]
-builds = ["mingling_core/builds"]
+
+build = ["mingling_core/build"]
default = ["core", "macros"]
@@ -92,6 +93,9 @@ extra_macros = ["mingling_macros/extra_macros"]
# Section only shown in docs.rs
docs_rs = []
+# This is an old name, will be Breaking Change in the future
+builds = ["mingling_core/build"]
+
[dependencies]
mingling_core = { workspace = true, optional = true }
mingling_macros = { workspace = true, optional = true }
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
index 3b36f00..35cf024 100644
--- a/mingling/src/example_docs.rs
+++ b/mingling/src/example_docs.rs
@@ -777,7 +777,7 @@ pub mod example_combine_pathf_dispatch_tree {}
/// To make your completions work, you need to generate a completion script using Mingling's tools
///
/// 1. Enable features
-/// You need to enable the `builds` and `comp` features for `mingling` in `[build-dependencies]`
+/// You need to enable the `build` and `comp` features for `mingling` in `[build-dependencies]`
///
/// 2. Write `build.rs`
/// Write the following in `build.rs`
@@ -838,8 +838,8 @@ pub mod example_combine_pathf_dispatch_tree {}
/// "comp",
///
/// # If you want to build completion scripts,
-/// # enable `builds` features
-/// "builds",
+/// # enable `build` features
+/// "build",
/// ]
///
/// [workspace]
@@ -1649,7 +1649,7 @@ pub mod example_help {}
/// .on_pre_chain(|info| {
/// println!("[DEBUG] Pre chain: {}", info.input);
/// })
-/// .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id))
+/// .on_post_chain(|info| println!("[DEBUG] Post chain: {}", info.output.member_id()))
/// .on_finish(|_| {
/// println!("[DEBUG] Loop end");
/// ProgramControlUnit::OverrideExitCode(0) // Override exit code
@@ -2268,8 +2268,8 @@ pub mod example_panic_unwind {}
/// # Enable `pathf` features
/// "pathf",
///
-/// # Enable the `builds` feature for build-time support
-/// "builds",
+/// # Enable the `build` feature for build-time support
+/// "build",
/// ]
///
/// [workspace]
diff --git a/mingling/src/features.rs b/mingling/src/features.rs
index 78d6226..0dde333 100644
--- a/mingling/src/features.rs
+++ b/mingling/src/features.rs
@@ -20,6 +20,17 @@ pub const MINGLING_ASYNC: bool = false;
#[cfg(feature = "async")]
#[allow(unused)]
pub const MINGLING_ASYNC: bool = true;
+/// Whether the `build` feature is enabled
+/// Current: `disabled`
+#[cfg(not(feature = "build"))]
+#[allow(unused)]
+pub const MINGLING_BUILD: bool = false;
+
+/// Whether the `build` feature is enabled
+/// Current: `enabled`
+#[cfg(feature = "build")]
+#[allow(unused)]
+pub const MINGLING_BUILD: bool = true;
/// Whether the `builds` feature is enabled
/// Current: `disabled`
#[cfg(not(feature = "builds"))]
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index fd274aa..19f2188 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -4,6 +4,7 @@
)]
#![deny(missing_docs)]
#![doc = include_str!("lib.md")]
+#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "core")]
mod example_docs;
diff --git a/mingling_cli/Cargo.lock b/mingling_cli/Cargo.lock
index e617d0a..ae28d9a 100644
--- a/mingling_cli/Cargo.lock
+++ b/mingling_cli/Cargo.lock
@@ -155,6 +155,12 @@ dependencies = [
]
[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -171,12 +177,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -259,6 +281,7 @@ dependencies = [
"serde_json",
"syn 3.0.2",
"tokio",
+ "toml_edit",
]
[[package]]
@@ -517,6 +540,43 @@ dependencies = [
]
[[package]]
+name = "toml_datetime"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.13+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
+
+[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -550,6 +610,15 @@ dependencies = [
]
[[package]]
+name = "winnow"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/mingling_cli/Cargo.toml b/mingling_cli/Cargo.toml
index 5b36c6b..330ef5a 100644
--- a/mingling_cli/Cargo.toml
+++ b/mingling_cli/Cargo.toml
@@ -13,6 +13,10 @@ categories = ["command-line-interface"]
[[bin]]
name = "mling"
+path = "src/bin/wrapper.rs"
+
+[[bin]]
+name = "mingling-cli"
path = "src/main.rs"
[dependencies.mingling]
@@ -27,7 +31,7 @@ features = [
[build-dependencies.mingling]
path = "../mingling"
features = [
- "builds",
+ "build",
"pathf",
]
@@ -48,6 +52,7 @@ serde_json = "1.0.151"
# Parallelism
tokio = { version = "1.53.1", features = ["full"] }
+toml_edit = "0.25.13"
[build-dependencies]
# Configure & Serialization
@@ -57,4 +62,10 @@ serde_json = "1.0.151"
# Code gen
just_template = "0.2.0"
+[profile.release]
+opt-level = "z"
+lto = true
+codegen-units = 1
+strip = true
+
[workspace]
diff --git a/mingling_cli/README.md b/mingling_cli/README.md
new file mode 100644
index 0000000..419534b
--- /dev/null
+++ b/mingling_cli/README.md
@@ -0,0 +1,2 @@
+> [!WARNING]
+> `mling`, as the scaffolding tool for Mingling, is currently under development. For completed features, please refer to the Roadmap section in the Mingling README.md.
diff --git a/mingling_cli/expand.rs b/mingling_cli/expand.rs
deleted file mode 100644
index 2e8bc4f..0000000
--- a/mingling_cli/expand.rs
+++ /dev/null
@@ -1,1121 +0,0 @@
-#![feature(prelude_import)]
-extern crate std;
-#[prelude_import]
-use std::prelude::rust_2024::*;
-use crate::{linter::MinglingLinterSetup, metadata::MinglingMetadataSetup};
-use mingling::{
- macros::gen_program,
- setup::{ExitCodeSetup, picker::{HelpFlagSetup, StructuralRendererSetup}},
-};
-pub mod errors {
- pub mod serde_json {
- use mingling::macros::{buffer, group, r_println, renderer};
- pub type ErrorSerdeJson = serde_json::Error;
- #[allow(non_camel_case_types)]
- mod internal_group_serde_json_error {
- use crate::ThisProgram as __MinglingProgram;
- #[allow(unused_imports)]
- use serde_json::Error;
- use super::ErrorSerdeJson;
- impl ::mingling::Grouped<__MinglingProgram> for ErrorSerdeJson {
- fn member_id() -> __MinglingProgram {
- __MinglingProgram::ErrorSerdeJson
- }
- }
- }
- #[doc(hidden)]
- #[allow(non_camel_case_types)]
- pub struct __internal_renderer_render_error_serde_json;
- impl ::mingling::Renderer for __internal_renderer_render_error_serde_json {
- type Previous = ErrorSerdeJson;
- fn render(prev: Self::Previous) -> ::mingling::RenderResult {
- let __renderer_result = { render_error_serde_json(prev) };
- ::std::convert::Into::into(__renderer_result)
- }
- }
- pub fn render_error_serde_json(
- _err: ErrorSerdeJson,
- ) -> ::mingling::RenderResult {
- let mut __render_result_buffer = ::mingling::RenderResult::new();
- {
- __render_result_buffer
- .println(
- ::alloc::__export::must_use({
- ::alloc::fmt::format(format_args!("serde"))
- }),
- );
- }
- __render_result_buffer
- }
- }
-}
-pub mod linter {
- use mingling::{Program, macros::program_setup};
- use crate::linter::cmd_mlint::CMDMinglingLinter;
- pub mod cmd_mlint {
- use mingling::macros::{chain, dispatcher};
- pub struct CMDMinglingLinter;
- #[automatically_derived]
- impl ::core::fmt::Debug for CMDMinglingLinter {
- #[inline]
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- ::core::fmt::Formatter::write_str(f, "CMDMinglingLinter")
- }
- }
- #[automatically_derived]
- impl ::core::default::Default for CMDMinglingLinter {
- #[inline]
- fn default() -> CMDMinglingLinter {
- CMDMinglingLinter {}
- }
- }
- pub struct EntryMinglingLinter {
- pub(crate) inner: Vec<String>,
- }
- impl EntryMinglingLinter {
- /// Creates a new instance of the wrapper type
- pub fn new(inner: Vec<String>) -> Self {
- Self { inner }
- }
- }
- impl From<Vec<String>> for EntryMinglingLinter {
- fn from(inner: Vec<String>) -> Self {
- Self::new(inner)
- }
- }
- impl From<EntryMinglingLinter> for Vec<String> {
- fn from(wrapper: EntryMinglingLinter) -> Vec<String> {
- wrapper.inner
- }
- }
- impl ::std::convert::AsRef<Vec<String>> for EntryMinglingLinter {
- fn as_ref(&self) -> &Vec<String> {
- &self.inner
- }
- }
- impl ::std::convert::AsMut<Vec<String>> for EntryMinglingLinter {
- fn as_mut(&mut self) -> &mut Vec<String> {
- &mut self.inner
- }
- }
- impl ::std::ops::Deref for EntryMinglingLinter {
- type Target = Vec<String>;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
- }
- impl ::std::ops::DerefMut for EntryMinglingLinter {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
- }
- impl ::std::default::Default for EntryMinglingLinter
- where
- Vec<String>: ::std::default::Default,
- {
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
- }
- impl Into<mingling::AnyOutput<crate::ThisProgram>> for EntryMinglingLinter {
- fn into(self) -> mingling::AnyOutput<crate::ThisProgram> {
- mingling::AnyOutput::new(self)
- }
- }
- impl Into<mingling::ChainProcess<crate::ThisProgram>> for EntryMinglingLinter {
- fn into(self) -> mingling::ChainProcess<crate::ThisProgram> {
- mingling::AnyOutput::new(self).route_chain()
- }
- }
- impl ::mingling::Grouped<crate::ThisProgram> for EntryMinglingLinter {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::EntryMinglingLinter
- }
- }
- impl ::mingling::Dispatcher<crate::ThisProgram> for CMDMinglingLinter {
- fn node(&self) -> ::mingling::Node {
- mingling::Node::default().join("mlint")
- }
- fn begin(
- &self,
- args: Vec<String>,
- ) -> ::mingling::ChainProcess<crate::ThisProgram> {
- use ::mingling::Grouped;
- ::mingling::Routable::to_chain(EntryMinglingLinter::new(args))
- }
- fn clone_dispatcher(
- &self,
- ) -> Box<dyn ::mingling::Dispatcher<crate::ThisProgram>> {
- Box::new(CMDMinglingLinter)
- }
- }
- #[doc(hidden)]
- #[allow(non_camel_case_types)]
- pub struct __internal_chain_handle_mlint;
- impl ::mingling::Chain<crate::ThisProgram> for __internal_chain_handle_mlint {
- type Previous = EntryMinglingLinter;
- fn proc(
- prev: EntryMinglingLinter,
- ) -> ::mingling::ChainProcess<crate::ThisProgram> {
- handle_mlint(prev);
- <crate::ResultEmpty as ::mingling::Routable<
- crate::ThisProgram,
- >>::to_chain(crate::ResultEmpty)
- }
- }
- pub fn handle_mlint(_args: EntryMinglingLinter) {}
- }
- #[doc(hidden)]
- pub struct MinglingLinterSetup;
- impl ::mingling::setup::ProgramSetup<crate::ThisProgram> for MinglingLinterSetup {
- fn setup(self, program: &mut ::mingling::Program<crate::ThisProgram>) {
- mingling_linter_setup(program);
- }
- }
- pub fn mingling_linter_setup(program: &mut Program<crate::ThisProgram>) {
- {
- program.with_setup(MinglingLinterCommandSetup);
- }
- }
- #[doc(hidden)]
- pub struct MinglingLinterCommandSetup;
- impl ::mingling::setup::ProgramSetup<crate::ThisProgram>
- for MinglingLinterCommandSetup {
- fn setup(self, program: &mut ::mingling::Program<crate::ThisProgram>) {
- mingling_linter_command_setup(program);
- }
- }
- pub fn mingling_linter_command_setup(program: &mut Program<crate::ThisProgram>) {
- {
- program.with_dispatcher(CMDMinglingLinter);
- }
- }
-}
-pub mod metadata {
- use cargo_metadata::Metadata;
- use mingling::{
- AnyOutput, Program, ProgramCollect, RenderResult,
- macros::{buffer, group_structural, program_setup, r_println, renderer, routeify},
- };
- use crate::metadata::{cmd_metadata::CMDMetadata, setup::CargoMetadataSetup};
- pub mod cmd_metadata {
- use cargo_metadata::Metadata;
- use mingling::{LazyRes, macros::{chain, dispatcher, pack_structural}};
- use crate::metadata::setup::ResMetadata;
- pub struct CMDMetadata;
- #[automatically_derived]
- impl ::core::fmt::Debug for CMDMetadata {
- #[inline]
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- ::core::fmt::Formatter::write_str(f, "CMDMetadata")
- }
- }
- #[automatically_derived]
- impl ::core::default::Default for CMDMetadata {
- #[inline]
- fn default() -> CMDMetadata {
- CMDMetadata {}
- }
- }
- pub struct EntryMetadata {
- pub(crate) inner: Vec<String>,
- }
- impl EntryMetadata {
- /// Creates a new instance of the wrapper type
- pub fn new(inner: Vec<String>) -> Self {
- Self { inner }
- }
- }
- impl From<Vec<String>> for EntryMetadata {
- fn from(inner: Vec<String>) -> Self {
- Self::new(inner)
- }
- }
- impl From<EntryMetadata> for Vec<String> {
- fn from(wrapper: EntryMetadata) -> Vec<String> {
- wrapper.inner
- }
- }
- impl ::std::convert::AsRef<Vec<String>> for EntryMetadata {
- fn as_ref(&self) -> &Vec<String> {
- &self.inner
- }
- }
- impl ::std::convert::AsMut<Vec<String>> for EntryMetadata {
- fn as_mut(&mut self) -> &mut Vec<String> {
- &mut self.inner
- }
- }
- impl ::std::ops::Deref for EntryMetadata {
- type Target = Vec<String>;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
- }
- impl ::std::ops::DerefMut for EntryMetadata {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
- }
- impl ::std::default::Default for EntryMetadata
- where
- Vec<String>: ::std::default::Default,
- {
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
- }
- impl Into<mingling::AnyOutput<crate::ThisProgram>> for EntryMetadata {
- fn into(self) -> mingling::AnyOutput<crate::ThisProgram> {
- mingling::AnyOutput::new(self)
- }
- }
- impl Into<mingling::ChainProcess<crate::ThisProgram>> for EntryMetadata {
- fn into(self) -> mingling::ChainProcess<crate::ThisProgram> {
- mingling::AnyOutput::new(self).route_chain()
- }
- }
- impl ::mingling::Grouped<crate::ThisProgram> for EntryMetadata {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::EntryMetadata
- }
- }
- impl ::mingling::Dispatcher<crate::ThisProgram> for CMDMetadata {
- fn node(&self) -> ::mingling::Node {
- mingling::Node::default().join("metadata")
- }
- fn begin(
- &self,
- args: Vec<String>,
- ) -> ::mingling::ChainProcess<crate::ThisProgram> {
- use ::mingling::Grouped;
- ::mingling::Routable::to_chain(EntryMetadata::new(args))
- }
- fn clone_dispatcher(
- &self,
- ) -> Box<dyn ::mingling::Dispatcher<crate::ThisProgram>> {
- Box::new(CMDMetadata)
- }
- }
- pub struct ResultMetadata {
- pub inner: ResMetadata,
- }
- #[doc(hidden)]
- #[allow(
- non_upper_case_globals,
- unused_attributes,
- unused_qualifications,
- clippy::absolute_paths,
- )]
- const _: () = {
- #[allow(unused_extern_crates, clippy::useless_attribute)]
- extern crate serde as _serde;
- #[automatically_derived]
- impl _serde::Serialize for ResultMetadata {
- fn serialize<__S>(
- &self,
- __serializer: __S,
- ) -> _serde::__private229::Result<__S::Ok, __S::Error>
- where
- __S: _serde::Serializer,
- {
- let mut __serde_state = _serde::Serializer::serialize_struct(
- __serializer,
- "ResultMetadata",
- false as usize + 1,
- )?;
- _serde::ser::SerializeStruct::serialize_field(
- &mut __serde_state,
- "inner",
- &self.inner,
- )?;
- _serde::ser::SerializeStruct::end(__serde_state)
- }
- }
- };
- impl ResultMetadata {
- pub fn new(inner: ResMetadata) -> Self {
- Self { inner }
- }
- }
- impl From<ResMetadata> for ResultMetadata {
- fn from(inner: ResMetadata) -> Self {
- Self::new(inner)
- }
- }
- impl From<ResultMetadata> for ResMetadata {
- fn from(wrapper: ResultMetadata) -> ResMetadata {
- wrapper.inner
- }
- }
- impl ::std::convert::AsRef<ResMetadata> for ResultMetadata {
- fn as_ref(&self) -> &ResMetadata {
- &self.inner
- }
- }
- impl ::std::convert::AsMut<ResMetadata> for ResultMetadata {
- fn as_mut(&mut self) -> &mut ResMetadata {
- &mut self.inner
- }
- }
- impl ::std::ops::Deref for ResultMetadata {
- type Target = ResMetadata;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
- }
- impl ::std::ops::DerefMut for ResultMetadata {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
- }
- impl ::std::default::Default for ResultMetadata
- where
- ResMetadata: ::std::default::Default,
- {
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
- }
- impl ::mingling::__private::StructuralDataSealed<crate::ThisProgram>
- for ResultMetadata {}
- impl ::mingling::__private::StructuralData<crate::ThisProgram>
- for ResultMetadata {}
- impl Into<::mingling::AnyOutput<crate::ThisProgram>> for ResultMetadata {
- fn into(self) -> ::mingling::AnyOutput<crate::ThisProgram> {
- ::mingling::AnyOutput::new(self)
- }
- }
- impl Into<::mingling::ChainProcess<crate::ThisProgram>> for ResultMetadata {
- fn into(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- ::mingling::AnyOutput::new(self).route_chain()
- }
- }
- impl ::mingling::Grouped<crate::ThisProgram> for ResultMetadata {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::ResultMetadata
- }
- }
- #[doc(hidden)]
- #[allow(non_camel_case_types)]
- pub struct __internal_chain_handle_metadata;
- impl ::mingling::Chain<crate::ThisProgram> for __internal_chain_handle_metadata {
- type Previous = EntryMetadata;
- fn proc(
- prev: EntryMetadata,
- ) -> ::mingling::ChainProcess<crate::ThisProgram> {
- let __chain_result = {
- ::mingling::this::<crate::ThisProgram>()
- .__modify_res_and_return_route(|
- metadata: &mut LazyRes<ResMetadata>|
- {
- ::mingling::Routable::<
- crate::ThisProgram,
- >::to_chain(handle_metadata(prev, metadata))
- })
- };
- ::mingling::Routable::<crate::ThisProgram>::to_chain(__chain_result)
- }
- }
- pub fn handle_metadata(
- _: EntryMetadata,
- metadata: &mut LazyRes<ResMetadata>,
- ) -> Metadata {
- let metadata = metadata.get_ref().clone();
- metadata.data().clone()
- }
- }
- pub mod setup {
- use std::{env::current_dir, path::PathBuf};
- use cargo_metadata::{CargoOpt, MetadataCommand};
- use mingling::{
- LazyInit, Program, consts::REMAINS, macros::program_setup,
- picker::{IntoPicker, value::Flag},
- prelude::*, res::ResCurrentDir,
- };
- /// Name of Cargo manifest file
- pub const CARGO_TOML: &str = "Cargo.toml";
- use cargo_metadata::Metadata;
- use serde::Serialize;
- pub struct ResMetadata {
- data: Option<Metadata>,
- }
- #[automatically_derived]
- impl ::core::default::Default for ResMetadata {
- #[inline]
- fn default() -> ResMetadata {
- ResMetadata {
- data: ::core::default::Default::default(),
- }
- }
- }
- #[automatically_derived]
- impl ::core::clone::Clone for ResMetadata {
- #[inline]
- fn clone(&self) -> ResMetadata {
- ResMetadata {
- data: ::core::clone::Clone::clone(&self.data),
- }
- }
- }
- #[doc(hidden)]
- #[allow(
- non_upper_case_globals,
- unused_attributes,
- unused_qualifications,
- clippy::absolute_paths,
- )]
- const _: () = {
- #[allow(unused_extern_crates, clippy::useless_attribute)]
- extern crate serde as _serde;
- #[automatically_derived]
- impl _serde::Serialize for ResMetadata {
- fn serialize<__S>(
- &self,
- __serializer: __S,
- ) -> _serde::__private229::Result<__S::Ok, __S::Error>
- where
- __S: _serde::Serializer,
- {
- let mut __serde_state = _serde::Serializer::serialize_struct(
- __serializer,
- "ResMetadata",
- false as usize + 1,
- )?;
- _serde::ser::SerializeStruct::serialize_field(
- &mut __serde_state,
- "data",
- &self.data,
- )?;
- _serde::ser::SerializeStruct::end(__serde_state)
- }
- }
- };
- impl ResMetadata {
- /// Returns a reference to the parsed `cargo metadata`.
- ///
- /// # Panics
- ///
- /// This function does **not** panic in practice, because `ResMetadata` is
- /// always initialized via [`LazyInit`] inside the program setup, and the
- /// initialization either succeeds (setting `data` to `Some(...)`) or fails
- /// by propagating the error from `cmd.exec().unwrap()`. Therefore, by the
- /// time this getter is called, `self.data` is guaranteed to be `Some`.
- pub fn data(&self) -> &Metadata {
- self.data.as_ref().unwrap()
- }
- }
- #[doc(hidden)]
- pub struct CargoMetadataSetup;
- impl ::mingling::setup::ProgramSetup<crate::ThisProgram> for CargoMetadataSetup {
- fn setup(self, program: &mut ::mingling::Program<crate::ThisProgram>) {
- cargo_metadata_setup(program);
- }
- }
- pub fn cargo_metadata_setup(program: &mut Program<crate::ThisProgram>) {
- {
- let args = program.take_args();
- let (
- feature_args,
- manifest_path,
- message_format,
- enable_all_features,
- no_default_features,
- no_deps,
- args,
- ) = args
- .pick(
- &::mingling::picker::PickerArg::<Vec<String>> {
- full: &["features"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- )
- .pick(
- &::mingling::picker::PickerArg::<Option<String>> {
- full: &["manifest_path"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- )
- .pick_or(
- &::mingling::picker::PickerArg::<String> {
- full: &["message_format"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- || "disable".to_string(),
- )
- .pick(
- &::mingling::picker::PickerArg::<Flag> {
- full: &["all_features"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- )
- .pick(
- &::mingling::picker::PickerArg::<Flag> {
- full: &["no_default_features"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- )
- .pick(
- &::mingling::picker::PickerArg::<Flag> {
- full: &["no_deps"],
- short: ::std::option::Option::None,
- positional: false,
- internal_type: ::std::marker::PhantomData,
- },
- )
- .pick(&REMAINS)
- .unwrap();
- program.replace_args(args.into());
- program.structural_renderer_name = message_format.into();
- let current_dir = current_dir().unwrap();
- let feature_args_leaked: &[String] = Box::leak(
- feature_args.into_boxed_slice(),
- );
- let manifest_path_clone = manifest_path.clone();
- let current_dir_clone = current_dir.clone();
- program
- .with_resource(
- ResMetadata::lazy_init(move || {
- let metadata_path = match manifest_path_clone {
- Some(ref path_str) => PathBuf::from(path_str),
- None => find_manifest().unwrap(),
- };
- let mut cmd = MetadataCommand::new();
- cmd.manifest_path(metadata_path)
- .current_dir(&current_dir_clone);
- if *enable_all_features {
- cmd.features(CargoOpt::AllFeatures);
- }
- if *no_default_features {
- cmd.features(CargoOpt::NoDefaultFeatures);
- }
- if *no_deps {
- cmd.no_deps();
- }
- cmd.features(
- CargoOpt::SomeFeatures(feature_args_leaked.to_vec()),
- );
- ResMetadata {
- data: Some(cmd.exec().unwrap()),
- }
- }),
- );
- program.with_resource(ResCurrentDir::from(current_dir));
- }
- }
- /// Find `Cargo.toml` by searching upward from `current_dir`.
- fn find_manifest() -> Option<PathBuf> {
- let mut dir = current_dir().ok()?;
- loop {
- let candidate = dir.join(CARGO_TOML);
- if candidate.is_file() {
- return Some(candidate);
- }
- if !dir.pop() {
- return None;
- }
- }
- }
- }
- #[doc(hidden)]
- pub struct MinglingMetadataSetup;
- impl ::mingling::setup::ProgramSetup<crate::ThisProgram> for MinglingMetadataSetup {
- fn setup(self, program: &mut ::mingling::Program<crate::ThisProgram>) {
- mingling_metadata_setup(program);
- }
- }
- pub fn mingling_metadata_setup(program: &mut Program<crate::ThisProgram>) {
- {
- program.with_setup(CargoMetadataSetup);
- program.with_dispatcher(CMDMetadata);
- }
- }
- #[allow(non_camel_case_types)]
- mod internal_group_metadata {
- use crate::ThisProgram as __MinglingProgram;
- #[allow(unused_imports)]
- use super::Metadata;
- impl ::mingling::Grouped<__MinglingProgram> for Metadata {
- fn member_id() -> __MinglingProgram {
- __MinglingProgram::Metadata
- }
- }
- impl ::mingling::__private::StructuralDataSealed<crate::ThisProgram>
- for Metadata {}
- impl ::mingling::__private::StructuralData<crate::ThisProgram> for Metadata {}
- }
- #[doc(hidden)]
- #[allow(non_camel_case_types)]
- pub struct __internal_renderer_render_metadata;
- impl ::mingling::Renderer for __internal_renderer_render_metadata {
- type Previous = Metadata;
- fn render(prev: Self::Previous) -> ::mingling::RenderResult {
- let __renderer_result = { render_metadata(prev) };
- ::std::convert::Into::into(__renderer_result)
- }
- }
- pub fn render_metadata(metadata: Metadata) -> RenderResult {
- let mut r = RenderResult::new();
- r.println(
- ::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!("{0}", serde_json::to_string(&metadata)?),
- )
- }),
- );
- r
- }
-}
-fn main() {
- let mut program = ThisProgram::new();
- program.with_setup(HelpFlagSetup::default());
- program.with_setup(ExitCodeSetup::default());
- program.with_setup(StructuralRendererSetup);
- program.with_setup(MinglingMetadataSetup);
- program.with_setup(MinglingLinterSetup);
- program.exec_and_exit();
-}
-/// Alias for the current program type `crate::ThisProgram`
-pub type Next = ::mingling::ChainProcess<crate::ThisProgram>;
-impl ::mingling::Routable<crate::ThisProgram>
-for ::mingling::ChainProcess<crate::ThisProgram> {
- fn to_chain(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- match self {
- ::mingling::ChainProcess::Ok((any, _)) => {
- ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Chain))
- }
- other => other,
- }
- }
- fn to_render(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- match self {
- ::mingling::ChainProcess::Ok((any, _)) => {
- ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Renderer))
- }
- other => other,
- }
- }
-}
-pub struct ErrorRendererNotFound {
- pub(crate) inner: String,
-}
-impl ErrorRendererNotFound {
- /// Creates a new instance of the wrapper type
- pub fn new(inner: String) -> Self {
- Self { inner }
- }
-}
-impl From<String> for ErrorRendererNotFound {
- fn from(inner: String) -> Self {
- Self::new(inner)
- }
-}
-impl From<ErrorRendererNotFound> for String {
- fn from(wrapper: ErrorRendererNotFound) -> String {
- wrapper.inner
- }
-}
-impl ::std::convert::AsRef<String> for ErrorRendererNotFound {
- fn as_ref(&self) -> &String {
- &self.inner
- }
-}
-impl ::std::convert::AsMut<String> for ErrorRendererNotFound {
- fn as_mut(&mut self) -> &mut String {
- &mut self.inner
- }
-}
-impl ::std::ops::Deref for ErrorRendererNotFound {
- type Target = String;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
-}
-impl ::std::ops::DerefMut for ErrorRendererNotFound {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
-}
-impl ::std::default::Default for ErrorRendererNotFound
-where
- String: ::std::default::Default,
-{
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
-}
-impl Into<mingling::AnyOutput<crate::ThisProgram>> for ErrorRendererNotFound {
- fn into(self) -> mingling::AnyOutput<crate::ThisProgram> {
- mingling::AnyOutput::new(self)
- }
-}
-impl Into<mingling::ChainProcess<crate::ThisProgram>> for ErrorRendererNotFound {
- fn into(self) -> mingling::ChainProcess<crate::ThisProgram> {
- mingling::AnyOutput::new(self).route_chain()
- }
-}
-impl ::mingling::Grouped<crate::ThisProgram> for ErrorRendererNotFound {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::ErrorRendererNotFound
- }
-}
-pub struct ErrorDispatcherNotFound {
- pub(crate) inner: Vec<String>,
-}
-impl ErrorDispatcherNotFound {
- /// Creates a new instance of the wrapper type
- pub fn new(inner: Vec<String>) -> Self {
- Self { inner }
- }
-}
-impl From<Vec<String>> for ErrorDispatcherNotFound {
- fn from(inner: Vec<String>) -> Self {
- Self::new(inner)
- }
-}
-impl From<ErrorDispatcherNotFound> for Vec<String> {
- fn from(wrapper: ErrorDispatcherNotFound) -> Vec<String> {
- wrapper.inner
- }
-}
-impl ::std::convert::AsRef<Vec<String>> for ErrorDispatcherNotFound {
- fn as_ref(&self) -> &Vec<String> {
- &self.inner
- }
-}
-impl ::std::convert::AsMut<Vec<String>> for ErrorDispatcherNotFound {
- fn as_mut(&mut self) -> &mut Vec<String> {
- &mut self.inner
- }
-}
-impl ::std::ops::Deref for ErrorDispatcherNotFound {
- type Target = Vec<String>;
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
-}
-impl ::std::ops::DerefMut for ErrorDispatcherNotFound {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.inner
- }
-}
-impl ::std::default::Default for ErrorDispatcherNotFound
-where
- Vec<String>: ::std::default::Default,
-{
- fn default() -> Self {
- Self::new(::std::default::Default::default())
- }
-}
-impl Into<mingling::AnyOutput<crate::ThisProgram>> for ErrorDispatcherNotFound {
- fn into(self) -> mingling::AnyOutput<crate::ThisProgram> {
- mingling::AnyOutput::new(self)
- }
-}
-impl Into<mingling::ChainProcess<crate::ThisProgram>> for ErrorDispatcherNotFound {
- fn into(self) -> mingling::ChainProcess<crate::ThisProgram> {
- mingling::AnyOutput::new(self).route_chain()
- }
-}
-impl ::mingling::Grouped<crate::ThisProgram> for ErrorDispatcherNotFound {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::ErrorDispatcherNotFound
- }
-}
-pub struct ResultEmpty;
-#[doc(hidden)]
-#[allow(
- non_upper_case_globals,
- unused_attributes,
- unused_qualifications,
- clippy::absolute_paths,
-)]
-const _: () = {
- #[allow(unused_extern_crates, clippy::useless_attribute)]
- extern crate serde as _serde;
- #[automatically_derived]
- impl _serde::Serialize for ResultEmpty {
- fn serialize<__S>(
- &self,
- __serializer: __S,
- ) -> _serde::__private229::Result<__S::Ok, __S::Error>
- where
- __S: _serde::Serializer,
- {
- _serde::Serializer::serialize_unit_struct(__serializer, "ResultEmpty")
- }
- }
-};
-impl ::mingling::__private::StructuralDataSealed<crate::ThisProgram> for ResultEmpty {}
-impl ::mingling::__private::StructuralData<crate::ThisProgram> for ResultEmpty {}
-impl ::mingling::Grouped<crate::ThisProgram> for ResultEmpty {
- fn member_id() -> crate::ThisProgram {
- crate::ThisProgram::ResultEmpty
- }
-}
-impl ::std::convert::Into<::mingling::AnyOutput<crate::ThisProgram>> for ResultEmpty {
- fn into(self) -> ::mingling::AnyOutput<crate::ThisProgram> {
- ::mingling::AnyOutput::new(self)
- }
-}
-impl ::std::convert::Into<::mingling::ChainProcess<crate::ThisProgram>> for ResultEmpty {
- fn into(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- ::mingling::AnyOutput::new(self).route_chain()
- }
-}
-#[automatically_derived]
-impl ::core::default::Default for ResultEmpty {
- #[inline]
- fn default() -> ResultEmpty {
- ResultEmpty {}
- }
-}
-#[repr(u8)]
-#[allow(nonstandard_style)]
-pub enum ThisProgram {
- EntryMetadata,
- EntryMinglingLinter,
- ErrorDispatcherNotFound,
- ErrorRendererNotFound,
- ErrorSerdeJson,
- Metadata,
- ResultEmpty,
- ResultMetadata,
-}
-#[automatically_derived]
-#[allow(nonstandard_style)]
-impl ::core::fmt::Debug for ThisProgram {
- #[inline]
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- ::core::fmt::Formatter::write_str(
- f,
- match self {
- ThisProgram::EntryMetadata => "EntryMetadata",
- ThisProgram::EntryMinglingLinter => "EntryMinglingLinter",
- ThisProgram::ErrorDispatcherNotFound => "ErrorDispatcherNotFound",
- ThisProgram::ErrorRendererNotFound => "ErrorRendererNotFound",
- ThisProgram::ErrorSerdeJson => "ErrorSerdeJson",
- ThisProgram::Metadata => "Metadata",
- ThisProgram::ResultEmpty => "ResultEmpty",
- ThisProgram::ResultMetadata => "ResultMetadata",
- },
- )
- }
-}
-#[automatically_derived]
-#[allow(nonstandard_style)]
-impl ::core::marker::StructuralPartialEq for ThisProgram {}
-#[automatically_derived]
-#[allow(nonstandard_style)]
-impl ::core::cmp::PartialEq for ThisProgram {
- #[inline]
- fn eq(&self, other: &ThisProgram) -> bool {
- let __self_discr = ::core::intrinsics::discriminant_value(self);
- let __arg1_discr = ::core::intrinsics::discriminant_value(other);
- __self_discr == __arg1_discr
- }
-}
-#[automatically_derived]
-#[allow(nonstandard_style)]
-impl ::core::cmp::Eq for ThisProgram {
- #[doc(hidden)]
- #[coverage(off)]
- fn assert_fields_are_eq(&self) {}
-}
-#[automatically_derived]
-#[allow(nonstandard_style)]
-impl ::core::clone::Clone for ThisProgram {
- #[inline]
- fn clone(&self) -> ThisProgram {
- match self {
- ThisProgram::EntryMetadata => ThisProgram::EntryMetadata,
- ThisProgram::EntryMinglingLinter => ThisProgram::EntryMinglingLinter,
- ThisProgram::ErrorDispatcherNotFound => ThisProgram::ErrorDispatcherNotFound,
- ThisProgram::ErrorRendererNotFound => ThisProgram::ErrorRendererNotFound,
- ThisProgram::ErrorSerdeJson => ThisProgram::ErrorSerdeJson,
- ThisProgram::Metadata => ThisProgram::Metadata,
- ThisProgram::ResultEmpty => ThisProgram::ResultEmpty,
- ThisProgram::ResultMetadata => ThisProgram::ResultMetadata,
- }
- }
-}
-impl ::std::fmt::Display for ThisProgram {
- fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- match self {
- ThisProgram::EntryMetadata => f.write_fmt(format_args!("EntryMetadata")),
- ThisProgram::EntryMinglingLinter => {
- f.write_fmt(format_args!("EntryMinglingLinter"))
- }
- ThisProgram::ErrorDispatcherNotFound => {
- f.write_fmt(format_args!("ErrorDispatcherNotFound"))
- }
- ThisProgram::ErrorRendererNotFound => {
- f.write_fmt(format_args!("ErrorRendererNotFound"))
- }
- ThisProgram::ErrorSerdeJson => f.write_fmt(format_args!("ErrorSerdeJson")),
- ThisProgram::Metadata => f.write_fmt(format_args!("Metadata")),
- ThisProgram::ResultEmpty => f.write_fmt(format_args!("ResultEmpty")),
- ThisProgram::ResultMetadata => f.write_fmt(format_args!("ResultMetadata")),
- }
- }
-}
-impl ::mingling::ProgramCollect for ThisProgram {
- type Enum = ThisProgram;
- type ErrorDispatcherNotFound = ErrorDispatcherNotFound;
- type ErrorRendererNotFound = ErrorRendererNotFound;
- type ResultEmpty = ResultEmpty;
- fn build_renderer_not_found(
- member_id: Self::Enum,
- ) -> ::mingling::AnyOutput<Self::Enum> {
- ::mingling::AnyOutput::new(ErrorRendererNotFound::new(member_id.to_string()))
- }
- fn build_dispatcher_not_found(
- args: Vec<String>,
- ) -> ::mingling::AnyOutput<Self::Enum> {
- ::mingling::AnyOutput::new(ErrorDispatcherNotFound::new(args))
- }
- fn build_empty_result() -> ::mingling::AnyOutput<Self::Enum> {
- ::mingling::AnyOutput::new(ResultEmpty)
- }
- fn render(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
- match any.member_id {
- Self::ErrorSerdeJson => {
- let value = unsafe {
- any.downcast::<crate::errors::serde_json::ErrorSerdeJson>()
- .unwrap_unchecked()
- };
- <crate::errors::serde_json::__internal_renderer_render_error_serde_json as ::mingling::Renderer>::render(
- value,
- )
- }
- Self::Metadata => {
- let value = unsafe {
- any.downcast::<cargo_metadata::Metadata>().unwrap_unchecked()
- };
- <crate::metadata::__internal_renderer_render_metadata as ::mingling::Renderer>::render(
- value,
- )
- }
- _ => ::mingling::RenderResult::default(),
- }
- }
- fn do_chain(
- any: ::mingling::AnyOutput<Self::Enum>,
- ) -> ::mingling::ChainProcess<Self::Enum> {
- match any.member_id {
- Self::EntryMetadata => {
- let value = unsafe {
- any.downcast::<crate::metadata::cmd_metadata::EntryMetadata>()
- .unwrap_unchecked()
- };
- <crate::metadata::cmd_metadata::__internal_chain_handle_metadata as ::mingling::Chain<
- Self::Enum,
- >>::proc(value)
- }
- Self::EntryMinglingLinter => {
- let value = unsafe {
- any.downcast::<crate::linter::cmd_mlint::EntryMinglingLinter>()
- .unwrap_unchecked()
- };
- <crate::linter::cmd_mlint::__internal_chain_handle_mlint as ::mingling::Chain<
- Self::Enum,
- >>::proc(value)
- }
- _ => {
- ::core::panicking::panic_fmt(
- format_args!("No chain found for type id: {0:?}", any.type_id),
- );
- }
- }
- }
- fn render_help(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
- #[allow(unused_imports)]
- use crate::metadata::cmd_metadata::__internal_chain_handle_metadata;
- use crate::metadata::cmd_metadata::ResultMetadata;
- use crate::linter::cmd_mlint::__internal_chain_handle_mlint;
- use crate::metadata::__internal_renderer_render_metadata;
- use crate::metadata::cmd_metadata::CMDMetadata;
- use crate::metadata::setup::ResMetadata;
- use crate::metadata::cmd_metadata::EntryMetadata;
- use crate::linter::cmd_mlint::CMDMinglingLinter;
- use crate::errors::serde_json::ErrorSerdeJson;
- use cargo_metadata::Metadata;
- use crate::errors::serde_json::__internal_renderer_render_error_serde_json;
- use crate::linter::cmd_mlint::EntryMinglingLinter;
- match any.member_id {
- _ => ::mingling::RenderResult::default(),
- }
- }
- fn has_renderer(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
- Self::ErrorSerdeJson => true,
- Self::Metadata => true,
- _ => false,
- }
- }
- fn has_chain(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
- Self::EntryMetadata => true,
- Self::EntryMinglingLinter => true,
- _ => false,
- }
- }
- fn structural_render(
- any: ::mingling::AnyOutput<Self::Enum>,
- setting: &::mingling::StructuralRendererSetting,
- ) -> Result<
- ::mingling::RenderResult,
- ::mingling::error::StructuralRendererSerializeError,
- > {
- #[allow(unused_imports)]
- use crate::metadata::cmd_metadata::__internal_chain_handle_metadata;
- use crate::metadata::cmd_metadata::ResultMetadata;
- use crate::linter::cmd_mlint::__internal_chain_handle_mlint;
- use crate::metadata::__internal_renderer_render_metadata;
- use crate::metadata::cmd_metadata::CMDMetadata;
- use crate::metadata::setup::ResMetadata;
- use crate::metadata::cmd_metadata::EntryMetadata;
- use crate::linter::cmd_mlint::CMDMinglingLinter;
- use crate::errors::serde_json::ErrorSerdeJson;
- use cargo_metadata::Metadata;
- use crate::errors::serde_json::__internal_renderer_render_error_serde_json;
- use crate::linter::cmd_mlint::EntryMinglingLinter;
- match any.member_id {
- Self::Metadata => {
- let raw = unsafe { any.restore::<Metadata>().unwrap_unchecked() };
- let mut __renderer_inner_result = ::mingling::RenderResult::default();
- ::mingling::StructuralRenderer::render(
- &raw,
- setting,
- &mut __renderer_inner_result,
- )?;
- Ok(__renderer_inner_result)
- }
- _ => {
- let mut r = ::mingling::RenderResult::default();
- ::mingling::StructuralRenderer::render(&ResultEmpty, setting, &mut r)?;
- Ok(r)
- }
- }
- }
-}
-impl ThisProgram {
- /// Creates a new `Program<#name>` instance with default configuration.
- pub fn new() -> ::mingling::Program<ThisProgram> {
- ::mingling::Program::new()
- }
- /// Returns a static reference to the global `Program<#name>` singleton.
- pub fn this() -> &'static ::mingling::Program<ThisProgram> {
- &::mingling::this::<ThisProgram>()
- }
-}
diff --git a/mingling_cli/help/help.txt b/mingling_cli/help/help.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/help/help.txt
diff --git a/mingling_cli/src/bin/wrapper.rs b/mingling_cli/src/bin/wrapper.rs
new file mode 100644
index 0000000..b1aecf6
--- /dev/null
+++ b/mingling_cli/src/bin/wrapper.rs
@@ -0,0 +1,47 @@
+use std::env;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::process::{self, Command};
+
+fn main() {
+ exec();
+}
+
+fn exec() {
+ fn target_exe_path() -> PathBuf {
+ let mut path = env::current_exe().expect("Fail to read current_exe");
+ path.pop();
+
+ let target_name = if cfg!(target_os = "windows") {
+ "mingling-cli.exe"
+ } else {
+ "mingling-cli"
+ };
+
+ path.push(target_name);
+ path
+ }
+
+ let mut args: Vec<OsString> = env::args_os().collect();
+ let pass_args = if args.len() > 1 {
+ args.split_off(1)
+ } else {
+ vec![]
+ };
+
+ let target = target_exe_path();
+
+ let status = Command::new(&target)
+ .args(&pass_args)
+ .status()
+ .unwrap_or_else(|_| {
+ process::exit(1);
+ });
+
+ match status.code() {
+ Some(code) => process::exit(code),
+ None => {
+ process::exit(1);
+ }
+ }
+}
diff --git a/mingling_cli/src/errors.rs b/mingling_cli/src/errors.rs
index cef9616..5cdc43f 100644
--- a/mingling_cli/src/errors.rs
+++ b/mingling_cli/src/errors.rs
@@ -1 +1,3 @@
+pub mod io_error;
pub mod serde_json;
+
diff --git a/mingling_cli/src/errors/io_error.rs b/mingling_cli/src/errors/io_error.rs
new file mode 100644
index 0000000..97509c9
--- /dev/null
+++ b/mingling_cli/src/errors/io_error.rs
@@ -0,0 +1,8 @@
+use mingling::macros::{group, renderer};
+
+group!(ErrorIo = std::io::Error);
+
+#[renderer]
+pub fn handle_error_io(err: ErrorIo) {
+ panic!("{}", err.to_string())
+}
diff --git a/mingling_cli/src/linter.rs b/mingling_cli/src/linter.rs
index c85afc1..859ffc5 100644
--- a/mingling_cli/src/linter.rs
+++ b/mingling_cli/src/linter.rs
@@ -4,7 +4,7 @@ use mingling::{
};
use crate::{
- linter::cmd_mlint::{CMDMinglingLinter, EntryMinglingLinter},
+ linter::cmd_mlint::{CMDLint, EntryLint},
metadata::setup::ResUsingJson,
};
@@ -19,7 +19,7 @@ pub fn mingling_linter_setup(program: &mut Program<crate::ThisProgram>) {
#[program_setup]
pub fn mingling_linter_command_setup(program: &mut Program<crate::ThisProgram>) {
- program.with_dispatcher(CMDMinglingLinter);
+ program.with_dispatcher(CMDLint);
program.with_dispatcher(CMDLinterSupportRustAnalyzer);
program.with_dispatcher(CMDLinterSupportRustAnalyzerWithClippy);
program.with_dispatcher(CMDLinterSupportRustAnalyzerWithCheck);
@@ -40,10 +40,7 @@ dispatcher!("ra-lint",
);
#[chain]
-pub fn handle_ra_lint(
- _: EntryLinterSupportRustAnalyzer,
- use_json: &mut ResUsingJson,
-) -> EntryMinglingLinter {
+pub fn handle_ra_lint(_: EntryLinterSupportRustAnalyzer, use_json: &mut ResUsingJson) -> EntryLint {
use_json.using = true;
entry!("--message-format=json")
}
@@ -52,7 +49,7 @@ pub fn handle_ra_lint(
pub fn handle_ra_lint_check(
_: EntryLinterSupportRustAnalyzerWithCheck,
use_json: &mut ResUsingJson,
-) -> EntryMinglingLinter {
+) -> EntryLint {
use_json.using = true;
entry!("--message-format=json", "--with-checker=cargo,check")
}
@@ -61,7 +58,7 @@ pub fn handle_ra_lint_check(
pub fn handle_ra_lint_clippy(
_: EntryLinterSupportRustAnalyzerWithClippy,
use_json: &mut ResUsingJson,
-) -> EntryMinglingLinter {
+) -> EntryLint {
use_json.using = true;
entry!("--message-format=json", "--with-checker=cargo,clippy")
}
diff --git a/mingling_cli/src/linter/cmd_mlint.rs b/mingling_cli/src/linter/cmd_mlint.rs
index 90871af..1dfd02b 100644
--- a/mingling_cli/src/linter/cmd_mlint.rs
+++ b/mingling_cli/src/linter/cmd_mlint.rs
@@ -4,8 +4,9 @@ use mingling::LazyRes;
use mingling::consts::REMAINS;
use mingling::macros::{arg, chain, dispatcher, pack};
use mingling::picker::EntryPicker;
+use tokio::task::JoinSet;
-dispatcher!("lint", CMDMinglingLinter => EntryMinglingLinter);
+dispatcher!("lint", CMDLint => EntryLint);
/// Main linting function that processes all packages in the metadata.
///
@@ -13,42 +14,54 @@ dispatcher!("lint", CMDMinglingLinter => EntryMinglingLinter);
/// reads Rust source files (`.rs`), parses them into ASTs, runs lint checks,
/// and enriches each report with metadata information.
async fn linter_main(metadata: &Metadata) -> Vec<MlintReport> {
- // Vector to accumulate all lint reports
- let mut all_reports = Vec::new();
+ let mut join_set = JoinSet::new();
- // Iterate over all packages in the metadata
for package in &metadata.packages {
- // Iterate over all targets within a package (e.g., bin, lib, test, etc.)
for target in &package.targets {
let path = &target.src_path;
// Only process Rust source files (with `.rs` extension)
if !path.as_str().ends_with(".rs") {
continue;
}
- // Read the source file content
- let source = match std::fs::read_to_string(path.as_str()) {
- Ok(s) => s,
- Err(_) => continue,
- };
- // Parse the source file into an AST (Abstract Syntax Tree)
- let ast = match syn::parse_file(&source) {
- Ok(f) => f,
- Err(_) => continue,
- };
-
- // Run all lint checks and collect reports
- let reports = crate::lints::run_all_lints(&ast, &source);
- //
- for mut r in reports {
- // Enrich report with metadata information
- r.file_name = path.as_str().to_string();
- r.source_code = source.clone();
- r.package_id = Some(package.id.to_string());
- r.target_name = Some(target.name.clone());
- r.target_kind = target.kind.first().map(|k| k.to_string());
- r.target_src_path = Some(path.as_str().to_string());
- all_reports.push(r);
- }
+
+ // Clone/move all data needed inside the blocking closure
+ let path_str = path.as_str().to_string();
+ let package_id = package.id.to_string();
+ let target_name = target.name.clone();
+ let target_kind = target.kind.first().map(|k| k.to_string());
+
+ join_set.spawn_blocking(move || {
+ // Read the source file content
+ let source = std::fs::read_to_string(&path_str).ok()?;
+ // Parse the source file into an AST
+ let ast = syn::parse_file(&source).ok()?;
+ // Run all lint checks and collect reports
+ let reports = crate::lints::run_all_lints(&ast, &source);
+
+ // Enrich each report with metadata information
+ let enriched: Vec<MlintReport> = reports
+ .into_iter()
+ .map(|mut r| {
+ r.file_name = path_str.clone();
+ r.source_code = source.clone();
+ r.package_id = Some(package_id.clone());
+ r.target_name = Some(target_name.clone());
+ r.target_kind = target_kind.clone();
+ r.target_src_path = Some(path_str.clone());
+ r
+ })
+ .collect();
+
+ Some(enriched)
+ });
+ }
+ }
+
+ let mut all_reports = Vec::new();
+ while let Some(res) = join_set.join_next().await {
+ // `spawn_blocking` panics are propagated, `None` means task skipped (read/parse failure)
+ if let Ok(Some(reports)) = res {
+ all_reports.extend(reports);
}
}
@@ -58,7 +71,7 @@ async fn linter_main(metadata: &Metadata) -> Vec<MlintReport> {
pack!(StateBeginLinter = ());
#[chain]
-pub fn handle_lint(args: EntryMinglingLinter) -> StateBeginLinter {
+pub fn handle_lint(args: EntryLint) -> StateBeginLinter {
let (with_checker, checker_args) = args
.pick_or(&arg![with_checker: Option<String>], || {
Some("cargo,check".to_string())
diff --git a/mingling_cli/src/linter/mlint_report.rs b/mingling_cli/src/linter/mlint_report.rs
index 8195f77..0472056 100644
--- a/mingling_cli/src/linter/mlint_report.rs
+++ b/mingling_cli/src/linter/mlint_report.rs
@@ -51,8 +51,8 @@ pub struct MlintReport {
/// Compilation target source file path that this report belongs to
pub target_src_path: Option<String>,
- /// A suggestion for automatic fix (shown as diff in annotated output)
- pub suggestion: Option<LintSuggestion>,
+ /// Suggestions for automatic fix (shown as diff in annotated output)
+ pub suggestions: Vec<LintSuggestion>,
}
/// Report severity level, indicating the seriousness of the Lint result.
@@ -248,8 +248,8 @@ impl MlintReport {
group = group.element(msg);
}
- // Render suggestion as a diff (Element::Suggestion)
- if let Some(sugg) = &self.suggestion {
+ // Render suggestions as diffs
+ for sugg in &self.suggestions {
let patch_snippet = Snippet::source(sugg.source.clone())
.line_start(sugg.line_start)
.path(&self.file_name)
@@ -404,8 +404,8 @@ impl MlintReport {
.collect::<Vec<_>>(),
)
.label(span.label.clone())
- .suggested_replacement(self.suggestion.as_ref().map(|s| s.replacement.clone()))
- .suggestion_applicability(if self.suggestion.is_some() {
+ .suggested_replacement(self.suggestions.first().map(|s| s.replacement.clone()))
+ .suggestion_applicability(if !self.suggestions.is_empty() {
Some(cargo_metadata::diagnostic::Applicability::MachineApplicable)
} else {
None
diff --git a/mingling_cli/src/lints.rs b/mingling_cli/src/lints.rs
index 89f5ca3..8cc1e6e 100644
--- a/mingling_cli/src/lints.rs
+++ b/mingling_cli/src/lints.rs
@@ -1,3 +1,5 @@
+// This file is auto-generated by pre/lint_registry.rs
+
#![allow(unused)]
use crate::linter::mlint_report::{MlintLevel, MlintReport};
diff --git a/mingling_cli/src/lints/non_mingling_naming_style.rs b/mingling_cli/src/lints/non_mingling_naming_style.rs
index 6481926..83168b7 100644
--- a/mingling_cli/src/lints/non_mingling_naming_style.rs
+++ b/mingling_cli/src/lints/non_mingling_naming_style.rs
@@ -153,12 +153,12 @@ fn check_fn_name(func: &syn::ItemFn, source: &str) -> Vec<MlintReport> {
lint_code: "non_mingling_naming_style".into(),
message: msg,
spans: vec![span],
- suggestion: Some(LintSuggestion {
+ suggestions: vec![LintSuggestion {
source: source_line.to_string(),
line_start: func.sig.span().start().line,
byte_range,
replacement: suggestion_target,
- }),
+ }],
attached_reports: vec![MlintReport {
level: MlintLevel::Help,
message: format!("expected `{expected_fn}` ↔ `{expected_type}`"),
diff --git a/mingling_cli/src/lints/template_linter.rs b/mingling_cli/src/lints/template_linter.rs
index 271aa0c..4836e24 100644
--- a/mingling_cli/src/lints/template_linter.rs
+++ b/mingling_cli/src/lints/template_linter.rs
@@ -13,7 +13,7 @@
//! > This section is the **Metadata** section, which needs to be filled in correctly.
//! > These contents will eventually be compiled as the Linter's behavior.
//!
-//! Author: `Weicao-CatilGrass`
+//! Author: `Your-Name`
//! Default: `allow`
// ^^^^^ Supported parameters: `warn`, `allow`, `deny`
diff --git a/mingling_cli/src/lints/unnecessary_render_result_creation.rs b/mingling_cli/src/lints/unnecessary_render_result_creation.rs
index 03f81d2..1450c1a 100644
--- a/mingling_cli/src/lints/unnecessary_render_result_creation.rs
+++ b/mingling_cli/src/lints/unnecessary_render_result_creation.rs
@@ -14,8 +14,9 @@
//! Author: `Weicao-CatilGrass`
//! Default: `warn`
-use crate::linter::mlint_report::{MlintLevel, MlintReport};
+use crate::linter::mlint_report::{LintSuggestion, MlintLevel, MlintReport};
use quote::ToTokens;
+use syn::spanned::Spanned;
pub fn linter(ast: syn::ItemFn, source: &str) -> Vec<MlintReport> {
if !has_renderer_attr(&ast) {
@@ -45,6 +46,31 @@ pub fn linter(ast: syn::ItemFn, source: &str) -> Vec<MlintReport> {
if only_print_and_return && print_count > 0 {
let span = MlintReport::span_from_syn(&ast.sig, source);
+ let mut suggestions = Vec::new();
+
+ // 1. Attribute change: #[renderer] → #[renderer(buffer)]
+ if let Some(sugg) = make_attr_suggestion(&ast, source) {
+ suggestions.push(sugg);
+ }
+
+ // 2. Remove -> RenderResult from function signature
+ if let Some(sugg) = make_return_type_suggestion(&ast, source) {
+ suggestions.push(sugg);
+ }
+
+ // 3. Remove let mut r = RenderResult::...
+ if let Some(sugg) = make_let_removal_suggestion(stmts, let_idx, source) {
+ suggestions.push(sugg);
+ }
+
+ // 4. Fix r_println!(r, ...) → r_println!(...) for all r_xxx macros
+ suggestions.extend(make_macro_arg_suggestions(stmts, &r_name, source));
+
+ // 5. Remove return 'r' expression
+ if let Some(sugg) = make_return_removal_suggestion(stmts, &r_name, source) {
+ suggestions.push(sugg);
+ }
+
vec![MlintReport {
source_code: source.to_string(),
level: MlintLevel::Warning,
@@ -54,6 +80,7 @@ pub fn linter(ast: syn::ItemFn, source: &str) -> Vec<MlintReport> {
ast.sig.ident,
),
spans: vec![span],
+ suggestions,
attached_reports: vec![MlintReport {
level: MlintLevel::Help,
message: format!(
@@ -185,6 +212,198 @@ fn check_stmt_usage(stmt: &syn::Stmt, r_name: &str, print_count: &mut usize) ->
true
}
+/// Build suggestion: `#[renderer]` → `#[renderer(buffer)]`
+fn make_attr_suggestion(ast: &syn::ItemFn, source: &str) -> Option<LintSuggestion> {
+ let attr = ast.attrs.iter().find(|a| {
+ let name = a.path().to_token_stream().to_string();
+ name.ends_with("renderer")
+ })?;
+
+ let line_idx = attr.span().start().line.saturating_sub(1);
+ let line = source.lines().nth(line_idx)?;
+
+ // Replace `renderer]` with `renderer(buffer)]`
+ // This handles both `#[renderer]` and `#[::mingling::macros::renderer]`
+ let line_str = line;
+ let replacement = line_str.replacen("renderer]", "renderer(buffer)]", 1);
+
+ if replacement == line_str {
+ return None;
+ }
+
+ Some(LintSuggestion {
+ source: line_str.to_string(),
+ line_start: line_idx + 1,
+ byte_range: 0..line_str.len(),
+ replacement,
+ })
+}
+
+/// Build suggestion: remove ` -> RenderResult` from function signature
+fn make_return_type_suggestion(ast: &syn::ItemFn, source: &str) -> Option<LintSuggestion> {
+ let syn::ReturnType::Type(arrow, ret_type) = &ast.sig.output else {
+ return None;
+ };
+
+ let sig_line_idx = ast.sig.span().start().line.saturating_sub(1);
+ let line = source.lines().nth(sig_line_idx)?;
+
+ // proc-macro2 column is 0-based byte offset from line start
+ let arrow_byte_col = arrow.span().start().column;
+ let ret_end_byte_col = ret_type.span().end().column;
+
+ // Include the space before `->`
+ let range_start = if arrow_byte_col > 0 {
+ arrow_byte_col - 1
+ } else {
+ arrow_byte_col
+ };
+
+ Some(LintSuggestion {
+ source: line.to_string(),
+ line_start: sig_line_idx + 1,
+ byte_range: range_start..ret_end_byte_col,
+ replacement: String::new(),
+ })
+}
+
+/// Build suggestion: remove `let mut r = RenderResult::...` line
+fn make_let_removal_suggestion(
+ stmts: &[syn::Stmt],
+ let_idx: usize,
+ source: &str,
+) -> Option<LintSuggestion> {
+ let stmt = &stmts[let_idx];
+ let line_idx = stmt.span().start().line.saturating_sub(1);
+ let line = source.lines().nth(line_idx)?;
+
+ Some(LintSuggestion {
+ source: line.to_string(),
+ line_start: line_idx + 1,
+ byte_range: 0..line.len(),
+ replacement: String::new(),
+ })
+}
+
+/// Build suggestions: fix `r_println!(r, ...)` → `r_println!(...)` for all r_xxx macros
+fn make_macro_arg_suggestions(
+ stmts: &[syn::Stmt],
+ r_name: &str,
+ source: &str,
+) -> Vec<LintSuggestion> {
+ let r_macros = ["r_println", "r_eprintln", "r_print", "r_eprint"];
+
+ stmts
+ .iter()
+ .filter_map(|stmt| {
+ let syn::Stmt::Macro(stmt_mac) = stmt else {
+ return None;
+ };
+
+ let macro_name = stmt_mac
+ .mac
+ .path
+ .segments
+ .last()
+ .map(|s| s.ident.to_string())
+ .unwrap_or_default();
+
+ if !r_macros.contains(&macro_name.as_str()) {
+ return None;
+ }
+
+ // Check that the first token is the r_name
+ let first_token = stmt_mac.mac.tokens.clone().into_iter().next()?;
+ if first_token.to_string() != *r_name {
+ return None;
+ }
+
+ let line_idx = stmt.span().start().line.saturating_sub(1);
+ let line = source.lines().nth(line_idx)?;
+
+ // Find pattern: macro_name!(r_name, ...
+ let macro_str = format!("{}!(", macro_name);
+ let macro_pos = line.find(&macro_str)?;
+ let after_open = macro_pos + macro_str.len();
+
+ // The first argument is `r_name` followed by `,` and possibly a space
+ // We need to find and remove `r_name, ` or `r_name,`
+ let first_arg = r_name;
+ if line[after_open..].starts_with(first_arg) {
+ // Find the end of the first argument (including `,` and any whitespace)
+ let arg_end = after_open + first_arg.len();
+ if arg_end < line.len() {
+ let rest = &line[arg_end..];
+ // Skip `,` and optional whitespace
+ let skip = if rest.starts_with(", ") {
+ 2
+ } else if rest.starts_with(',') {
+ 1
+ } else {
+ // Not followed by comma — not our pattern
+ return None;
+ };
+ let range_end = arg_end + skip;
+
+ Some(LintSuggestion {
+ source: line.to_string(),
+ line_start: line_idx + 1,
+ byte_range: after_open..range_end,
+ replacement: String::new(),
+ })
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+ .collect()
+}
+
+/// Build suggestion: remove the return `r` expression
+fn make_return_removal_suggestion(
+ stmts: &[syn::Stmt],
+ r_name: &str,
+ source: &str,
+) -> Option<LintSuggestion> {
+ let last = stmts.last()?;
+
+ let is_r_return = match last {
+ // `r` (bare expression, no semicolon) or `r;` (with semicolon)
+ syn::Stmt::Expr(expr, _) => {
+ if let syn::Expr::Path(p) = expr {
+ p.path.is_ident(r_name)
+ } else if let syn::Expr::Return(ret) = expr {
+ ret.expr.as_ref().is_some_and(|e| {
+ if let syn::Expr::Path(p) = e.as_ref() {
+ p.path.is_ident(r_name)
+ } else {
+ false
+ }
+ })
+ } else {
+ false
+ }
+ }
+ _ => false,
+ };
+
+ if !is_r_return {
+ return None;
+ }
+
+ let line_idx = last.span().start().line.saturating_sub(1);
+ let line = source.lines().nth(line_idx)?;
+
+ Some(LintSuggestion {
+ source: line.to_string(),
+ line_start: line_idx + 1,
+ byte_range: 0..line.len(),
+ replacement: String::new(),
+ })
+}
+
#[cfg(test)]
mod lint_test {
use crate::{assert_detected, assert_not_detected};
diff --git a/mingling_cli/tmpls/lints.tmpl b/mingling_cli/tmpls/lints.tmpl
index 590828a..e177079 100644
--- a/mingling_cli/tmpls/lints.tmpl
+++ b/mingling_cli/tmpls/lints.tmpl
@@ -1,3 +1,5 @@
+// This file is auto-generated by pre/lint_registry.rs
+
#![allow(unused)]
use crate::linter::mlint_report::{MlintLevel, MlintReport};
diff --git a/mingling_core/Cargo.toml b/mingling_core/Cargo.toml
index 14140c6..f525609 100644
--- a/mingling_core/Cargo.toml
+++ b/mingling_core/Cargo.toml
@@ -14,7 +14,7 @@ categories = ["command-line-interface"]
nightly = []
default = []
async = []
-builds = []
+build = []
picker = []
dispatch_tree = []
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index ec29a1b..e922e2e 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -1,8 +1,8 @@
+use crate::ProgramCollect;
use crate::error::ChainProcessError;
-use crate::{Grouped, ProgramCollect};
-#[doc(hidden)]
-pub mod group;
+mod group;
+pub use group::*;
/// Any type output
///
@@ -22,14 +22,14 @@ pub struct AnyOutput<G> {
///
/// This is set during construction and used for type-checking
/// in downcast, restore, and is methods.
- pub type_id: std::any::TypeId,
+ pub(crate) type_id: std::any::TypeId,
/// The variant identifier returned by [`Grouped::member_id`] for the
/// concrete type stored in `inner`.
///
/// This is used by the scheduler to dispatch on the correct enum
/// variant when routing the output.
- pub member_id: G,
+ pub(crate) member_id: G,
}
impl<G> AnyOutput<G> {
@@ -45,6 +45,52 @@ impl<G> AnyOutput<G> {
}
}
+ /// Create an `AnyOutput` from a raw value with a manually specified member_id.
+ ///
+ /// This function bypasses the [`Grouped`] trait, meaning the `member_id` you provide
+ /// does **not** have to match the actual concrete type `T`. The scheduler uses
+ /// `member_id` to determine which enum variant the output belongs to, and later
+ /// attempts to restore the value to the concrete type `T` based on that variant.
+ ///
+ /// # Safety
+ ///
+ /// - The caller must ensure that `member_id` correctly corresponds to the concrete
+ /// type `T` according to the scheduling logic. If `member_id` does not match,
+ /// calling [`restore`](Self::restore) or [`downcast`](Self::downcast) with the
+ /// type associated with `member_id` will cause **undefined behavior**.
+ /// - This safety contract is the caller's responsibility; the compiler cannot
+ /// enforce the correspondence between `member_id` and the stored type.
+ pub unsafe fn new_bare<T>(value: T, member_id: G) -> Self
+ where
+ T: Send + 'static,
+ {
+ Self {
+ inner: Box::new(value),
+ type_id: std::any::TypeId::of::<T>(),
+ member_id,
+ }
+ }
+
+ /// Get the [`TypeId`] of the concrete type stored in `inner`.
+ ///
+ /// The `TypeId` is set during construction (via [`AnyOutput::new`] or [`AnyOutput::new_bare`])
+ /// and is used for subsequent downcasting and type checking.
+ pub fn type_id(&self) -> std::any::TypeId {
+ self.type_id
+ }
+
+ /// Get the [`member_id`] of the concrete type stored in `inner`.
+ ///
+ /// [`member_id`] is set during construction (via [`AnyOutput::new`] or [`AnyOutput::new_bare`])
+ /// and identifies which variant of the output enum this value corresponds to.
+ /// The scheduler uses this value to dispatch the output to the correct next step.
+ pub fn member_id(&self) -> G
+ where
+ G: Copy,
+ {
+ self.member_id
+ }
+
/// Attempt to downcast the `AnyOutput` to a concrete type.
///
/// # Errors
@@ -190,7 +236,17 @@ mod tests {
value: i32,
}
- impl Grouped<MockGroup> for AlphaData {
+ /// # Safety
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for AlphaData {
fn member_id() -> MockGroup {
MockGroup::Alpha
}
@@ -202,7 +258,17 @@ mod tests {
name: String,
}
- impl Grouped<MockGroup> for BetaData {
+ /// # Safety
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for BetaData {
fn member_id() -> MockGroup {
MockGroup::Beta
}
@@ -213,7 +279,17 @@ mod tests {
#[cfg_attr(feature = "structural_renderer", derive(serde::Serialize))]
struct GammaData;
- impl Grouped<MockGroup> for GammaData {
+ /// # Safety
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for GammaData {
fn member_id() -> MockGroup {
MockGroup::Gamma
}
@@ -369,7 +445,17 @@ mod tests {
x: i32,
}
- impl Grouped<MockGroup> for SerData {
+ /// SAFETY:
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for SerData {
fn member_id() -> MockGroup {
MockGroup::Gamma
}
@@ -396,13 +482,33 @@ mod tests {
b: String,
}
- impl Grouped<MockGroup> for SerA {
+ /// SAFETY:
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for SerA {
fn member_id() -> MockGroup {
MockGroup::Alpha
}
}
- impl Grouped<MockGroup> for SerB {
+ /// SAFETY:
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockGroup> for SerB {
fn member_id() -> MockGroup {
MockGroup::Beta
}
diff --git a/mingling_core/src/any/group.rs b/mingling_core/src/any/group.rs
index 2813ad5..5e5e347 100644
--- a/mingling_core/src/any/group.rs
+++ b/mingling_core/src/any/group.rs
@@ -2,10 +2,14 @@ use crate::{AnyOutput, ChainProcess, ProgramCollect, Routable};
/// Used to mark a type with a unique enum ID, assisting dynamic dispatch
///
-/// **Note:** Unlike earlier versions, `Grouped` no longer requires `Serialize`
-/// even when the `structural_renderer` feature is enabled. Structured output is
-/// controlled separately via the \[`StructuralData`\] trait.
-pub trait Grouped<Group>
+/// # Safety
+///
+/// The returned `Group` value is an enum variant created by `register_type!` when
+/// registering the type's ID. Whether the variant matches correctly is guaranteed
+/// by `Grouped derive` or macros like `pack!`. If implemented manually, and the
+/// type name written in `member_id()` does not match the actually registered type,
+/// dispatching to that type will result in **100% undefined behavior**.
+pub unsafe trait Grouped<Group>
where
Self: Sized + 'static,
{
diff --git a/mingling_core/src/asset.rs b/mingling_core/src/asset.rs
index 9b9a5d4..f26952b 100644
--- a/mingling_core/src/asset.rs
+++ b/mingling_core/src/asset.rs
@@ -1,26 +1,9 @@
-#[doc(hidden)]
-pub mod chain;
-
-#[doc(hidden)]
-pub mod dispatcher;
-
-#[doc(hidden)]
-pub mod enum_tag;
-
-#[doc(hidden)]
-pub mod global_resource;
-
-#[doc(hidden)]
-pub mod help;
-
-#[doc(hidden)]
-pub mod lazy_resource;
-
-#[doc(hidden)]
-pub mod node;
-
-#[doc(hidden)]
-pub mod renderer;
-
-#[doc(hidden)]
-pub mod routable;
+pub(crate) mod chain;
+pub(crate) mod dispatcher;
+pub(crate) mod enum_tag;
+pub(crate) mod global_resource;
+pub(crate) mod help;
+pub(crate) mod lazy_resource;
+pub(crate) mod node;
+pub(crate) mod renderer;
+pub(crate) mod routable;
diff --git a/mingling_core/src/asset/global_resource.rs b/mingling_core/src/asset/global_resource.rs
index a610378..19374e7 100644
--- a/mingling_core/src/asset/global_resource.rs
+++ b/mingling_core/src/asset/global_resource.rs
@@ -184,7 +184,10 @@ pub trait ResourceMarker {
C: ProgramCollect<Enum = C> + 'static;
}
-impl<T: Default + Clone + Send + Sync + 'static> ResourceMarker for T {
+impl<T> ResourceMarker for T
+where
+ T: Default + Clone + Send + Sync + 'static,
+{
fn res_clone(&self) -> Self {
Clone::clone(self)
}
diff --git a/mingling_core/src/build.rs b/mingling_core/src/build.rs
new file mode 100644
index 0000000..7918f3a
--- /dev/null
+++ b/mingling_core/src/build.rs
@@ -0,0 +1,13 @@
+#[doc(hidden)]
+#[cfg(feature = "comp")]
+mod comp;
+
+#[cfg(feature = "comp")]
+pub use comp::*;
+
+#[doc(hidden)]
+#[cfg(feature = "pathf")]
+mod pathf;
+
+#[cfg(feature = "pathf")]
+pub use pathf::*;
diff --git a/mingling_core/src/builds/comp.rs b/mingling_core/src/build/comp.rs
index 2abb1e1..2abb1e1 100644
--- a/mingling_core/src/builds/comp.rs
+++ b/mingling_core/src/build/comp.rs
diff --git a/mingling_core/src/builds/pathf.rs b/mingling_core/src/build/pathf.rs
index d8d4698..d8d4698 100644
--- a/mingling_core/src/builds/pathf.rs
+++ b/mingling_core/src/build/pathf.rs
diff --git a/mingling_core/src/builds.rs b/mingling_core/src/builds.rs
deleted file mode 100644
index 3c52907..0000000
--- a/mingling_core/src/builds.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#[doc(hidden)]
-#[cfg(feature = "comp")]
-pub mod comp;
-
-#[doc(hidden)]
-#[cfg(feature = "pathf")]
-pub mod pathf;
diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs
index 31476c9..b9dbd06 100644
--- a/mingling_core/src/lib.rs
+++ b/mingling_core/src/lib.rs
@@ -1,3 +1,4 @@
+// #![deny(missing_docs)]
//! Mingling Core
//!
//! # Intro
@@ -8,7 +9,7 @@
//!
//! Recommended to import [mingling](https://crates.io/crates/mingling) to use its features.
-#![deny(missing_docs)]
+// Private Modules
mod any;
mod asset;
@@ -16,20 +17,65 @@ mod program;
mod renderer;
mod tester;
+/// Module for setting up a `Mingling` program.
+///
+/// This module provides the [`ProgramSetup`] type, which allows users to configure
+/// and initialize the program's execution environment.
+pub mod setup {
+ pub use crate::program::setup::ProgramSetup;
+}
+
+/// This module provides result types for Mingling components.
+///
+/// These are re-exported at the top level via `mingling::res`.
+#[doc(hidden)]
+pub mod core_res {
+ #[cfg(feature = "repl")]
+ pub use crate::program::repl_exec::res::*;
+}
+
+/// Provides the runtime logic for Mingling's dynamic completion system.
+///
+/// This module contains the core functionality for the "comp" (completion) feature,
+/// which enables dynamic tab-completion and input suggestion capabilities within
+/// Mingling applications.
+#[cfg(feature = "comp")]
+pub(crate) mod comp;
+
+/// Provides Mingling's build script module, used in `build.rs` to provide build-time behavior for certain features.
+///
+/// To use it, add the following to your `Cargo.toml` under `[build-dependencies]`, and enable the features
+/// that require build-time behavior from the crate:
+///
+/// ```toml
+/// [build-dependencies.mingling]
+/// version = "0.3.0"
+/// features = [
+/// "build", // Enable it
+/// "comp", // If you need completion-related build-time behavior, enable this as well
+/// ]
+/// ```
+#[cfg(feature = "build")]
+#[doc(hidden)]
+pub mod build;
+
+// Public Modules
+
/// Provides a toolkit for `Mingling` testing capabilities.
pub mod test {
pub use crate::tester::*;
}
-#[cfg(feature = "structural_renderer")]
-pub use crate::renderer::structural::StructuralRenderer;
+/// Provided for framework developers
+pub mod debug;
// NOT re-exported at top level: the `StructuralData` trait is sealed and only
// accessible through the derive macro. Users who need the trait can access it
// via `mingling::renderer::structural::StructuralData` (through the inner alias).
-pub use crate::any::group::*;
-pub use crate::any::*;
+#[cfg(feature = "structural_renderer")]
+pub use crate::renderer::structural::StructuralRenderer;
+pub use crate::any::*;
pub use crate::asset::chain::*;
pub use crate::asset::dispatcher::*;
pub use crate::asset::enum_tag::*;
@@ -39,74 +85,32 @@ pub use crate::asset::lazy_resource::*;
pub use crate::asset::node::*;
pub use crate::asset::renderer::*;
pub use crate::asset::routable::*;
+#[cfg(feature = "comp")]
+pub use crate::comp::*;
+pub use crate::program::*;
+pub use crate::renderer::render_result::*;
/// All error types of `Mingling`
pub mod error {
pub use crate::asset::chain::error::*;
pub use crate::exec::error::*;
pub use crate::program::error::*;
+
#[cfg(feature = "structural_renderer")]
pub use crate::renderer::structural::error::*;
- #[cfg(feature = "pathf")]
- pub use mingling_pathf::error::*;
-}
-
-pub use crate::program::*;
-
-pub use crate::renderer::render_result::*;
-
-#[cfg(feature = "builds")]
-#[doc(hidden)]
-pub mod builds;
-
-/// Provides build scripts for users
-#[cfg(feature = "builds")]
-pub mod build {
- #[cfg(feature = "comp")]
- pub use crate::builds::comp::*;
#[cfg(feature = "pathf")]
- pub use crate::builds::pathf::*;
+ pub use mingling_pathf::error::*;
}
-/// Provided for framework developers
-pub mod debug;
-
-#[cfg(feature = "comp")]
#[doc(hidden)]
-pub mod comp;
-
-#[cfg(feature = "comp")]
-pub use crate::comp::*;
+mod private;
-/// Module for setting up a `Mingling` program.
+/// Internal API provided by Mingling Core
///
-/// This module provides the [`ProgramSetup`] type, which allows users to configure
-/// and initialize the program's execution environment.
-pub mod setup {
- pub use crate::program::setup::ProgramSetup;
-}
-
-/// Private API — not intended for direct use.
+/// These are used by macros and are not exposed to users, but are still accessible externally.
#[doc(hidden)]
+#[allow(unused_imports)]
pub mod __private {
- use crate::ProgramCollect;
-
- /// Sealed trait for `StructuralData` — only implementable via derive macro.
- pub trait StructuralDataSealed<C>
- where
- C: ProgramCollect<Enum = C>,
- {
- }
-
- /// Re-export so the derive macro can reference the trait without
- /// conflicting with the derive macro name at `::mingling::StructuralData`.
- #[cfg(feature = "structural_renderer")]
- pub use crate::renderer::structural::structural_data::StructuralData;
-}
-
-#[doc(hidden)]
-pub mod core_res {
- #[cfg(feature = "repl")]
- pub use crate::program::repl_exec::res::ResREPL;
+ pub use crate::private::*;
}
diff --git a/mingling_core/src/private.rs b/mingling_core/src/private.rs
new file mode 100644
index 0000000..371ada2
--- /dev/null
+++ b/mingling_core/src/private.rs
@@ -0,0 +1,12 @@
+/// Sealed trait for `StructuralData` — only implementable via derive macro.
+#[cfg(feature = "structural_renderer")]
+pub trait StructuralDataSealed<C>
+where
+ C: crate::ProgramCollect<Enum = C>,
+{
+}
+
+/// Re-export so the derive macro can reference the trait without
+/// conflicting with the derive macro name at `::mingling::StructuralData`.
+#[cfg(feature = "structural_renderer")]
+pub use crate::renderer::structural::structural_data::StructuralData;
diff --git a/mingling_core/src/program/collection/mock.rs b/mingling_core/src/program/collection/mock.rs
index 5847f10..dbe4789 100644
--- a/mingling_core/src/program/collection/mock.rs
+++ b/mingling_core/src/program/collection/mock.rs
@@ -23,9 +23,12 @@ pub enum MockProgramCollect {
Bar,
}
-impl Grouped<MockProgramCollect> for MockProgramCollect {
+/// SAFETY: This is a mock type used only for temporary testing.
+/// It will never actually enter the macro system.
+/// The internal `panic!` ensures that `member_id` will never be executed.
+unsafe impl Grouped<MockProgramCollect> for MockProgramCollect {
fn member_id() -> MockProgramCollect {
- MockProgramCollect::Foo
+ panic!("Attempting to read an unsafe enum type");
}
}
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs
index 48f632f..7d94a21 100644
--- a/mingling_core/src/program/hook.rs
+++ b/mingling_core/src/program/hook.rs
@@ -695,7 +695,17 @@ mod tests {
}
}
- impl Grouped<MockHookEnum> for MockHookEnum {
+ /// SAFETY:
+ ///
+ /// This implementation is only for testing purposes to satisfy trait bounds.
+ /// Since this code only constructs `AnyOutput` and calls methods like
+ /// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
+ /// none of which involve `ProgramCollect::do_chain` or
+ /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// never exploited in an unsafe way here.
+ /// The caller must ensure that the associated `member_id` correctly
+ /// corresponds to the type's role in the group.
+ unsafe impl Grouped<MockHookEnum> for MockHookEnum {
fn member_id() -> MockHookEnum {
MockHookEnum::A
}
diff --git a/mingling_core/src/renderer/render_result.rs b/mingling_core/src/renderer/render_result.rs
index c38522a..8757376 100644
--- a/mingling_core/src/renderer/render_result.rs
+++ b/mingling_core/src/renderer/render_result.rs
@@ -6,7 +6,7 @@ use std::{
use crate::RenderResultMode::{Stderr, Stdout};
/// Render result, containing the rendered text content.
-#[derive(Default, Debug, PartialEq)]
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RenderResult {
/// Whether the output should be written immediately.
///
diff --git a/mingling_core/tests/test-all/tests/integration.rs b/mingling_core/tests/test-all/tests/integration.rs
index d36b9df..acb12db 100644
--- a/mingling_core/tests/test-all/tests/integration.rs
+++ b/mingling_core/tests/test-all/tests/integration.rs
@@ -7,9 +7,9 @@ use mingling::StringVec;
use mingling::StructuralData;
use mingling::StructuralRenderer;
use mingling::StructuralRendererSetting;
-use mingling::comp::{ShellContext, ShellFlag, Suggest};
use mingling::core_res::ResREPL;
use mingling::hook::ProgramHook;
+use mingling::{ShellContext, ShellFlag, Suggest};
use serde::Serialize;
use std::sync::atomic::{AtomicBool, Ordering};
diff --git a/mingling_core/tests/test-comp/tests/integration.rs b/mingling_core/tests/test-comp/tests/integration.rs
index 37aa716..5c5a9e4 100644
--- a/mingling_core/tests/test-comp/tests/integration.rs
+++ b/mingling_core/tests/test-comp/tests/integration.rs
@@ -1,6 +1,6 @@
use mingling::MockProgramCollect;
use mingling::Program;
-use mingling::comp::{ShellContext, ShellFlag, Suggest, SuggestItem};
+use mingling::{ShellContext, ShellFlag, Suggest, SuggestItem};
#[test]
fn test_shell_context_parsing_full() {
diff --git a/mingling_macros/src/derive/grouped.rs b/mingling_macros/src/derive/grouped.rs
index 307aab6..a00eea1 100644
--- a/mingling_macros/src/derive/grouped.rs
+++ b/mingling_macros/src/derive/grouped.rs
@@ -16,7 +16,11 @@ pub(crate) fn derive_grouped(input: TokenStream) -> TokenStream {
let expanded = quote! {
::mingling::macros::register_type!(#struct_name);
- impl ::mingling::Grouped<#group_ident> for #struct_name {
+ /// SAFETY: This is an internal implementation of the `Grouped` derive macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<#group_ident> for #struct_name {
fn member_id() -> #group_ident {
#group_ident::#struct_name
}
@@ -46,7 +50,11 @@ pub fn derive_grouped_serialize(input: TokenStream) -> TokenStream {
::mingling::macros::register_type!(#struct_name);
- impl ::mingling::Grouped<#group_ident> for #struct_name {
+ /// SAFETY: This is an internal implementation of the `Grouped` derive macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<#group_ident> for #struct_name {
fn member_id() -> #group_ident {
#group_ident::#struct_name
}
diff --git a/mingling_macros/src/func/gen_program.rs b/mingling_macros/src/func/gen_program.rs
index de4da06..db82504 100644
--- a/mingling_macros/src/func/gen_program.rs
+++ b/mingling_macros/src/func/gen_program.rs
@@ -342,7 +342,7 @@ fn main() {
) -> Result<::mingling::RenderResult, ::mingling::error::StructuralRendererSerializeError> {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#structural_renderer_tokens)*
_ => {
// Non-structural types: render ResultEmpty (which implements
@@ -408,7 +408,7 @@ fn main() {
fn do_comp(any: &::mingling::AnyOutput<Self::Enum>, ctx: &::mingling::ShellContext) -> ::mingling::Suggest {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#completion_tokens)*
_ => ::mingling::Suggest::FileCompletion,
}
@@ -442,7 +442,7 @@ fn main() {
}).collect();
quote! {
fn render(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
- match any.member_id {
+ match any.member_id() {
#(#render_arms)*
_ => ::mingling::RenderResult::default(),
}
@@ -494,9 +494,9 @@ fn main() {
fn do_chain(
any: ::mingling::AnyOutput<Self::Enum>,
) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::mingling::ChainProcess<Self::Enum>> + ::std::marker::Send>> {
- match any.member_id {
+ match any.member_id() {
#(#chain_arms_async)*
- _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id()),
}
}
}
@@ -505,9 +505,9 @@ fn main() {
fn do_chain(
any: ::mingling::AnyOutput<Self::Enum>,
) -> ::mingling::ChainProcess<Self::Enum> {
- match any.member_id {
+ match any.member_id() {
#(#chain_arms_sync)*
- _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id()),
}
}
}
@@ -535,7 +535,7 @@ fn main() {
let expanded = quote! {
#pathf_hint
- #[derive(Debug, PartialEq, Eq, Clone)]
+ #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(#repr_type)]
#[allow(nonstandard_style)]
pub enum #name {
@@ -569,19 +569,19 @@ fn main() {
fn render_help(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#help_tokens)*
_ => ::mingling::RenderResult::default(),
}
}
fn has_renderer(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
+ match any.member_id() {
#(#renderer_exist_tokens)*
_ => false
}
}
fn has_chain(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
+ match any.member_id() {
#(#chain_exist_tokens)*
_ => false
}
diff --git a/mingling_macros/src/func/group.rs b/mingling_macros/src/func/group.rs
index b865913..edb1fe1 100644
--- a/mingling_macros/src/func/group.rs
+++ b/mingling_macros/src/func/group.rs
@@ -133,7 +133,11 @@ pub(crate) fn group_macro(input: TokenStream) -> TokenStream {
#type_use
#alias_use
- impl ::mingling::Grouped<__MinglingProgram> for #type_name {
+ /// SAFETY: This is an internal implementation of the `group!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<__MinglingProgram> for #type_name {
fn member_id() -> __MinglingProgram {
__MinglingProgram::#type_name
}
diff --git a/mingling_macros/src/func/pack.rs b/mingling_macros/src/func/pack.rs
index a1a7e6b..7206b8e 100644
--- a/mingling_macros/src/func/pack.rs
+++ b/mingling_macros/src/func/pack.rs
@@ -138,7 +138,11 @@ pub(crate) fn pack(input: TokenStream) -> TokenStream {
}
}
- impl ::mingling::Grouped<#group_name> for #type_name {
+ /// SAFETY: This is an internal implementation of the `pack!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<#group_name> for #type_name {
fn member_id() -> #group_name {
#group_name::#type_name
}
diff --git a/mingling_macros/src/systems/structural_data.rs b/mingling_macros/src/systems/structural_data.rs
index 0a783ec..46d7cf8 100644
--- a/mingling_macros/src/systems/structural_data.rs
+++ b/mingling_macros/src/systems/structural_data.rs
@@ -176,7 +176,11 @@ pub(crate) fn pack_structural(input: TokenStream) -> TokenStream {
}
}
- impl ::mingling::Grouped<#program_path> for #type_name {
+ /// SAFETY: This is an internal implementation of the `pack_structural!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<#program_path> for #type_name {
fn member_id() -> #program_path {
#program_path::#type_name
}
@@ -297,7 +301,11 @@ pub(crate) fn group_structural(input: TokenStream) -> TokenStream {
#type_use
#alias_use
- impl ::mingling::Grouped<__MinglingProgram> for #type_name {
+ /// SAFETY: This is an internal implementation of the `pack_structural!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<__MinglingProgram> for #type_name {
fn member_id() -> __MinglingProgram {
__MinglingProgram::#type_name
}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..f328e4d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1 @@
+fn main() {}