aboutsummaryrefslogtreecommitdiff
path: root/mingling_cli/src/linter
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-06 17:02:36 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-07 16:57:24 +0800
commitd026b05624df4ab8a0be1c57adeb4947b566902d (patch)
treed39876abe221949b56c8b751c7b075734dbae585 /mingling_cli/src/linter
parentf809e616b4ec51ea981f671507591e213e0f4192 (diff)
feat(cli): implement `explain` command and add JSON structural rendering
Add a new `explain <LINT>` command that queries the embedded lint registry (generated by `build.rs`) and displays detailed information about a specifoic lint, including its name, title, summary, active-on target, default setting, and author. Enable the `structural_renderer` feature and add `serde`/`serde_json` dependencies to support JSON output rendering for command results.
Diffstat (limited to 'mingling_cli/src/linter')
-rw-r--r--mingling_cli/src/linter/cmd_explain.rs123
1 files changed, 123 insertions, 0 deletions
diff --git a/mingling_cli/src/linter/cmd_explain.rs b/mingling_cli/src/linter/cmd_explain.rs
new file mode 100644
index 0000000..d76cf65
--- /dev/null
+++ b/mingling_cli/src/linter/cmd_explain.rs
@@ -0,0 +1,123 @@
+use mingling::{
+ Grouped, Routable, StructuralData,
+ macros::{
+ arg, buffer, chain, dispatcher, metadata, pack, pack_err_structural, r_println, renderer,
+ routeify,
+ },
+ metadata::Description,
+ picker::EntryPicker,
+};
+use serde::{Deserialize, Serialize};
+use std::sync::OnceLock;
+
+use crate::Next;
+
+dispatcher!("explain");
+
+#[metadata(EntryExplain)]
+pub fn desc_explain() -> Description {
+ "Explain the meaning of the specified Lint".into()
+}
+
+pack!(StateExplainLint = String);
+pack_err_structural!(ErrorNoExplainLintProvided);
+pack_err_structural!(ErrorNoSuchLint = String);
+
+#[derive(Debug, Default, Grouped, StructuralData, Serialize)]
+pub struct ResultExplainLint {
+ pub lint_name: String,
+ pub title: String,
+ pub summary: String,
+ pub active_on: String,
+ pub author: String,
+ pub default: String,
+}
+
+#[chain(routeify)]
+pub fn handle_explain(args: EntryExplain) -> Next {
+ let lint_name = args
+ .pick_or_route(&arg![String], || {
+ ErrorNoExplainLintProvided::default().to_chain()
+ })
+ .to_result()?;
+ StateExplainLint::new(lint_name).into()
+}
+
+/// Mirror of the lint registry JSON generated by `build.rs` (`registry.json`).
+#[derive(Debug, Deserialize)]
+struct LintRegistry {
+ lints: Vec<LintEntry>,
+}
+
+#[derive(Debug, Deserialize)]
+struct LintEntry {
+ name: String,
+ title: String,
+ summary: String,
+ metadata: LintMetadata,
+}
+
+#[derive(Debug, Deserialize)]
+struct LintMetadata {
+ active_on: String,
+ author: String,
+ default: String,
+}
+
+/// The lint registry, embedded at compile time via `include_str!`.
+///
+/// `registry.json` is regenerated by `build.rs` on every build, so the
+/// embedded copy always reflects the lints in `src/lints/`.
+fn lint_registry() -> &'static LintRegistry {
+ static REGISTRY: OnceLock<LintRegistry> = OnceLock::new();
+ REGISTRY.get_or_init(|| {
+ serde_json::from_str(include_str!("../../registry.json"))
+ .expect("failed to parse embedded registry.json")
+ })
+}
+
+#[chain]
+pub fn handle_state_explain_lint(p: StateExplainLint) -> Next {
+ let lint_name = p.inner;
+ let Some(entry) = lint_registry().lints.iter().find(|l| l.name == lint_name) else {
+ return ErrorNoSuchLint::new(lint_name).to_chain();
+ };
+ ResultExplainLint {
+ lint_name: entry.name.clone(),
+ title: entry.title.clone(),
+ summary: entry.summary.clone(),
+ active_on: entry.metadata.active_on.clone(),
+ author: entry.metadata.author.clone(),
+ default: entry.metadata.default.clone(),
+ }
+ .to_chain()
+}
+
+#[renderer(buffer)]
+pub fn render_explain_lint(r: ResultExplainLint) {
+ r_println!("{}", r.title);
+ r_println!("");
+ r_println!(" Name: {}", r.lint_name);
+ r_println!(" Active on: {}", r.active_on);
+ r_println!(" Default: {}", r.default);
+ r_println!(" Author: {}", r.author);
+ r_println!("");
+ r_println!("{}", r.summary);
+}
+
+#[renderer(buffer)]
+pub fn render_error_no_explain_lint_provided(_: ErrorNoExplainLintProvided) {
+ r_println!("No lint name provided");
+ r_println!("");
+ r_println!("Usage: mling explain <LINT>");
+}
+
+#[renderer(buffer)]
+pub fn render_error_no_such_lint(err: ErrorNoSuchLint) {
+ r_println!("No such lint: \"{}\"", err.info);
+ r_println!("");
+ r_println!("Available lints:");
+ for entry in &lint_registry().lints {
+ r_println!(" {}", entry.name);
+ }
+}