diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-06 17:02:36 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-07 16:57:24 +0800 |
| commit | d026b05624df4ab8a0be1c57adeb4947b566902d (patch) | |
| tree | d39876abe221949b56c8b751c7b075734dbae585 /mingling_cli/src/linter/cmd_explain.rs | |
| parent | f809e616b4ec51ea981f671507591e213e0f4192 (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/cmd_explain.rs')
| -rw-r--r-- | mingling_cli/src/linter/cmd_explain.rs | 123 |
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); + } +} |
