aboutsummaryrefslogtreecommitdiff
path: root/mingling_cli/src/linter
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_cli/src/linter')
-rw-r--r--mingling_cli/src/linter/cmd_explain.rs105
-rw-r--r--mingling_cli/src/linter/cmd_lint.rs (renamed from mingling_cli/src/linter/cmd_mlint.rs)8
-rw-r--r--mingling_cli/src/linter/cmd_ra_lints.rs63
-rw-r--r--mingling_cli/src/linter/registry.rs33
4 files changed, 208 insertions, 1 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..0bc969a
--- /dev/null
+++ b/mingling_cli/src/linter/cmd_explain.rs
@@ -0,0 +1,105 @@
+use crate::{Next, linter::registry::ResLintRegistry};
+use mingling::{
+ Grouped, LazyRes, Routable, ShellContext, StructuralData, Suggest, SuggestItem,
+ macros::{
+ arg, buffer, chain, completion, dispatcher, metadata, pack, pack_err_structural, r_println,
+ renderer, routeify,
+ },
+ metadata::Description,
+ picker::EntryPicker,
+};
+use serde::Serialize;
+
+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()
+}
+
+#[chain]
+pub fn handle_state_explain_lint(
+ p: StateExplainLint,
+ registry: &mut LazyRes<ResLintRegistry>,
+) -> Next {
+ let registry = registry.get_ref();
+ let lint_name = p.inner;
+ let Some(entry) = 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 : #[mlint[{}({})]", r.default, r.lint_name);
+ 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, registry: &mut LazyRes<ResLintRegistry>) {
+ let registry = registry.get_ref();
+ r_println!("No such lint: \"{}\"", err.info);
+ r_println!("");
+ r_println!("Available lints:");
+ for entry in registry.lints.iter() {
+ r_println!(" {}", entry.name);
+ }
+}
+
+#[completion(EntryExplain)]
+pub fn complete_explain(ctx: &ShellContext, registry: &mut LazyRes<ResLintRegistry>) -> Suggest {
+ let registry = registry.get_ref();
+ if ctx.previous_word != "explain" {
+ return Suggest::FileCompletion;
+ }
+ let lints: Vec<String> = registry.lints.iter().map(|l| l.name.clone()).collect();
+ let mut suggest = Suggest::new();
+ for lint in lints {
+ suggest.insert(SuggestItem::Simple(lint));
+ }
+ suggest
+}
diff --git a/mingling_cli/src/linter/cmd_mlint.rs b/mingling_cli/src/linter/cmd_lint.rs
index 1dfd02b..57a78ad 100644
--- a/mingling_cli/src/linter/cmd_mlint.rs
+++ b/mingling_cli/src/linter/cmd_lint.rs
@@ -2,12 +2,18 @@ use crate::linter::mlint_report::{MlintReport, StateLintReports};
use cargo_metadata::Metadata;
use mingling::LazyRes;
use mingling::consts::REMAINS;
-use mingling::macros::{arg, chain, dispatcher, pack};
+use mingling::macros::{arg, chain, dispatcher, metadata, pack};
+use mingling::metadata::Description;
use mingling::picker::EntryPicker;
use tokio::task::JoinSet;
dispatcher!("lint", CMDLint => EntryLint);
+#[metadata(EntryLint)]
+pub fn desc_lint() -> Description {
+ "Mingling Linter".to_string().into()
+}
+
/// Main linting function that processes all packages in the metadata.
///
/// Iterates through all packages and their targets (e.g., binaries, libraries, tests),
diff --git a/mingling_cli/src/linter/cmd_ra_lints.rs b/mingling_cli/src/linter/cmd_ra_lints.rs
new file mode 100644
index 0000000..733d611
--- /dev/null
+++ b/mingling_cli/src/linter/cmd_ra_lints.rs
@@ -0,0 +1,63 @@
+use mingling::{
+ macros::{chain, dispatcher, entry, metadata},
+ metadata::Description,
+};
+
+use crate::{linter::cmd_lint::EntryLint, metadata::setup::ResUsingJson};
+
+// Aliases
+
+dispatcher!("ra-lint-clippy",
+ CMDLinterSupportRustAnalyzerWithClippy => EntryLinterSupportRustAnalyzerWithClippy
+);
+
+dispatcher!("ra-lint-check",
+ CMDLinterSupportRustAnalyzerWithCheck => EntryLinterSupportRustAnalyzerWithCheck
+);
+
+dispatcher!("ra-lint",
+ CMDLinterSupportRustAnalyzer => EntryLinterSupportRustAnalyzer
+);
+
+#[chain]
+pub fn handle_ra_lint(_: EntryLinterSupportRustAnalyzer, use_json: &mut ResUsingJson) -> EntryLint {
+ use_json.using = true;
+ entry!("--message-format=json")
+}
+
+#[chain]
+pub fn handle_ra_lint_check(
+ _: EntryLinterSupportRustAnalyzerWithCheck,
+ use_json: &mut ResUsingJson,
+) -> EntryLint {
+ use_json.using = true;
+ entry!("--message-format=json", "--with-checker=cargo,check")
+}
+
+#[chain]
+pub fn handle_ra_lint_clippy(
+ _: EntryLinterSupportRustAnalyzerWithClippy,
+ use_json: &mut ResUsingJson,
+) -> EntryLint {
+ use_json.using = true;
+ entry!("--message-format=json", "--with-checker=cargo,clippy")
+}
+
+#[metadata(EntryLinterSupportRustAnalyzer)]
+pub fn desc_ra_lint() -> Description {
+ "Run `mling lint` and output the results".to_string().into()
+}
+
+#[metadata(EntryLinterSupportRustAnalyzerWithCheck)]
+pub fn desc_ra_lint_check() -> Description {
+ "Run `mling lint` and `cargo check`, and output the combined results"
+ .to_string()
+ .into()
+}
+
+#[metadata(EntryLinterSupportRustAnalyzerWithClippy)]
+pub fn desc_ra_lint_clippy() -> Description {
+ "Run `mling lint` and `cargo clippy`, and output the combined results"
+ .to_string()
+ .into()
+}
diff --git a/mingling_cli/src/linter/registry.rs b/mingling_cli/src/linter/registry.rs
new file mode 100644
index 0000000..79f3225
--- /dev/null
+++ b/mingling_cli/src/linter/registry.rs
@@ -0,0 +1,33 @@
+use mingling::{LazyInit, Program, macros::program_setup};
+use serde::Deserialize;
+
+use crate::ThisProgram;
+
+#[derive(Debug, Default, Clone, Deserialize)]
+pub struct ResLintRegistry {
+ pub lints: Vec<LintEntry>,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct LintEntry {
+ pub name: String,
+ pub title: String,
+ pub summary: String,
+ pub metadata: LintMetadata,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct LintMetadata {
+ pub active_on: String,
+ pub author: String,
+ pub default: String,
+}
+
+#[program_setup]
+pub fn lint_registry_setup(p: &mut Program<ThisProgram>) {
+ p.with_resource(ResLintRegistry::lazy_init(|| {
+ let registry: ResLintRegistry = serde_json::from_str(include_str!("../../registry.json"))
+ .expect("failed to parse embedded registry.json");
+ registry
+ }));
+}