aboutsummaryrefslogtreecommitdiff
path: root/mingling_cli
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_cli')
-rw-r--r--mingling_cli/Cargo.lock3
-rw-r--r--mingling_cli/Cargo.toml3
-rw-r--r--mingling_cli/help/help.txt27
-rw-r--r--mingling_cli/src/diagnostic.rs2
-rw-r--r--mingling_cli/src/linter.rs47
-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
-rw-r--r--mingling_cli/src/lints/non_mingling_naming_style.rs16
-rw-r--r--mingling_cli/src/main.rs26
-rw-r--r--mingling_cli/src/metadata/cmd_metadata.rs10
-rw-r--r--mingling_cli/src/pkg_mgr.rs0
-rw-r--r--mingling_cli/src/proj_mgr.rs0
14 files changed, 281 insertions, 62 deletions
diff --git a/mingling_cli/Cargo.lock b/mingling_cli/Cargo.lock
index 5a0dcd0..60f8792 100644
--- a/mingling_cli/Cargo.lock
+++ b/mingling_cli/Cargo.lock
@@ -277,6 +277,7 @@ dependencies = [
"arg-picker",
"mingling_core",
"mingling_macros",
+ "serde",
]
[[package]]
@@ -304,6 +305,8 @@ dependencies = [
"just_template",
"might_be_async",
"mingling_pathf",
+ "serde",
+ "serde_json",
]
[[package]]
diff --git a/mingling_cli/Cargo.toml b/mingling_cli/Cargo.toml
index 4dfea20..a7a8538 100644
--- a/mingling_cli/Cargo.toml
+++ b/mingling_cli/Cargo.toml
@@ -27,7 +27,8 @@ features = [
"pathf",
"dispatch_tree",
"async",
- "comp"
+ "comp",
+ "structural_renderer"
]
[build-dependencies.mingling]
diff --git a/mingling_cli/help/help.txt b/mingling_cli/help/help.txt
index 85a4fb7..2089e2b 100644
--- a/mingling_cli/help/help.txt
+++ b/mingling_cli/help/help.txt
@@ -1,3 +1,30 @@
USAGE: mling [GLOBAL_FLAGS] [COMMAND] [ARGS...]
GLOBAL_FLAGS:
+ --all-features Enable all features
+ --features <FEATURES> List of features to enable
+ -h, --help Show help messages
+ --manifest-path <PATH> Custom path to Cargo.toml
+ --message-format <FORMAT> Output message format
+ --no-default-features Disable default features
+ --no-deps Do not include dependencies in metadata
+
+COMMANDS:
+ metadata Check your workspace metadata using 'cargo metadata'
+
+ LINTER:
+ lint Mingling Linter
+ ra-lint Run `mling lint` and output the results
+ ra-lint-check Run `mling lint` and `cargo check`, and output the combined results
+ ra-lint-clippy Run `mling lint` and `cargo clippy`, and output the combined results
+ explain <LINT> Explain the meaning of the specified Lint
+
+ PROJECT MANAGE:
+ proj-new <NAME> [NOT_IMPL] Create a project
+ proj-init [NOT_IMPL] Initialize a project
+
+ PACKAGE MANAGE:
+ install [NOT_IMPL] Install the project to the Mingling package list
+ package-show [NOT_IMPL] Show locally installed packages
+ package-enable <NAME> [NOT_IMPL] Enable the specified package
+ package-disable <NAME> [NOT_IMPL] Disable the specified package
diff --git a/mingling_cli/src/diagnostic.rs b/mingling_cli/src/diagnostic.rs
index 527e879..807cdf3 100644
--- a/mingling_cli/src/diagnostic.rs
+++ b/mingling_cli/src/diagnostic.rs
@@ -26,7 +26,7 @@ fn cargo_level_to_annotate(
}
}
-/// 把 1-based char offset 转成 0-based byte offset
+/// Convert 1-based char offset to 0-based byte offset
fn char_offset_to_byte_offset(s: &str, char_offset: usize) -> usize {
s.char_indices()
.nth(char_offset.saturating_sub(1))
diff --git a/mingling_cli/src/linter.rs b/mingling_cli/src/linter.rs
index 8be45c8..30d79de 100644
--- a/mingling_cli/src/linter.rs
+++ b/mingling_cli/src/linter.rs
@@ -1,45 +1,6 @@
-use mingling::macros::{chain, dispatcher, entry};
-
-use crate::{linter::cmd_mlint::EntryLint, metadata::setup::ResUsingJson};
-
-pub mod cmd_mlint;
+pub mod cmd_explain;
+pub mod cmd_lint;
+pub mod cmd_ra_lints;
pub mod mlint_attr;
pub mod mlint_report;
-
-// 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")
-}
+pub mod registry;
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
+ }));
+}
diff --git a/mingling_cli/src/lints/non_mingling_naming_style.rs b/mingling_cli/src/lints/non_mingling_naming_style.rs
index 83168b7..eee3098 100644
--- a/mingling_cli/src/lints/non_mingling_naming_style.rs
+++ b/mingling_cli/src/lints/non_mingling_naming_style.rs
@@ -4,14 +4,14 @@
//!
//! Checks that Mingling functions follow naming conventions:
//!
-//! | Prefix | 1st param must be |
-//! |--------|------------------|
-//! | `handle_` | `Entry*` |
-//! | `handle_state_` | `State*` |
-//! | `handle_error_` | `Error*` |
-//! | `help_` | `Entry*` |
-//! | `render_` | `Result*` |
-//! | `render_error_` | `Error*` |
+//! | Prefix | 1st param must be |
+//! |-----------------|-------------------|
+//! | `handle_` | `Entry*` |
+//! | `handle_state_` | `State*` |
+//! | `handle_error_` | `Error*` |
+//! | `help_` | `Entry*` |
+//! | `render_` | `Result*` |
+//! | `render_error_` | `Error*` |
//!
//! The name after prefix (snake_case) must match the type after prefix (PascalCase).
//!
diff --git a/mingling_cli/src/main.rs b/mingling_cli/src/main.rs
index b9164db..7c71bcd 100644
--- a/mingling_cli/src/main.rs
+++ b/mingling_cli/src/main.rs
@@ -1,15 +1,21 @@
-use crate::metadata::{
- MinglingMetadataSetup,
- setup::{
- ARG_ALL_FEATURES, ARG_FEATURES, ARG_MANIFEST_PATH, ARG_MESSAGE_FORMAT,
- ARG_NO_DEFAULT_FEATURES, ARG_NO_DEPS,
+use crate::{
+ linter::registry::LintRegistrySetup,
+ metadata::{
+ MinglingMetadataSetup,
+ setup::{
+ ARG_ALL_FEATURES, ARG_FEATURES, ARG_MANIFEST_PATH, ARG_MESSAGE_FORMAT,
+ ARG_NO_DEFAULT_FEATURES, ARG_NO_DEPS,
+ },
},
};
use mingling::{
ShellContext, Suggest,
- consts::HELP_FLAG,
+ consts::{HELP_FLAG, JSON_FLAG, JSON_PRETTY_FLAG},
macros::{completion, gen_program, help, suggest},
- setup::{ExitCodeSetup, picker::HelpFlagSetup},
+ setup::{
+ ExitCodeSetup,
+ picker::{HelpFlagSetup, StructuralRendererSetup},
+ },
};
pub mod diagnostic;
@@ -18,16 +24,20 @@ pub mod linter;
pub mod lints;
pub mod message;
pub mod metadata;
+pub mod pkg_mgr;
+pub mod proj_mgr;
#[tokio::main]
async fn main() {
let mut program = ThisProgram::new();
// Setups
+ program.with_setup(StructuralRendererSetup);
program.with_setup(HelpFlagSetup::default());
program.with_setup(ExitCodeSetup::default());
program.with_setup(MinglingMetadataSetup);
+ program.with_setup(LintRegistrySetup);
// Exec
program.exec_and_exit().await;
@@ -48,6 +58,8 @@ pub fn complete_global(_ctx: &ShellContext) -> Suggest {
ARG_ALL_FEATURES: "Enable all features",
ARG_NO_DEFAULT_FEATURES: "Disable default features",
ARG_NO_DEPS: "Do not include dependencies in metadata",
+ JSON_FLAG: "Render results in JSON format",
+ JSON_PRETTY_FLAG: "Render results in pretty JSON format"
}
}
diff --git a/mingling_cli/src/metadata/cmd_metadata.rs b/mingling_cli/src/metadata/cmd_metadata.rs
index 18241a2..3ee9f09 100644
--- a/mingling_cli/src/metadata/cmd_metadata.rs
+++ b/mingling_cli/src/metadata/cmd_metadata.rs
@@ -1,13 +1,21 @@
use cargo_metadata::Metadata;
use mingling::{
LazyRes,
- macros::{chain, dispatcher, pack},
+ macros::{chain, dispatcher, metadata, pack},
+ metadata::Description,
};
use crate::metadata::setup::ResMetadata;
dispatcher!("metadata");
+#[metadata(EntryMetadata)]
+pub fn desc_metadata() -> Description {
+ "Check your workspace metadata using 'cargo metadata'"
+ .to_string()
+ .into()
+}
+
pack!(ResultMetadata = ResMetadata);
#[chain]
diff --git a/mingling_cli/src/pkg_mgr.rs b/mingling_cli/src/pkg_mgr.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/src/pkg_mgr.rs
diff --git a/mingling_cli/src/proj_mgr.rs b/mingling_cli/src/proj_mgr.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mingling_cli/src/proj_mgr.rs