diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-07-21 06:40:46 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-07-21 06:40:46 +0800 |
| commit | 04ce3a18efe971c5b6893977f0ebe1ce1d9a6947 (patch) | |
| tree | 0f70ebbfae3e89404603721a1505dbed0cd6371d /mingling_cli/src | |
| parent | 8a230bd93b4801cc9001d0611ade16261e4f0cc4 (diff) | |
feat(mingling_cli): add metadata and linter commands with cargo analysis
Introduce `metadata` and `mlint` subcommands, wire up `cargo_metadata`
for manifest analysis, and enable procedural macro support in editors
Diffstat (limited to 'mingling_cli/src')
| -rw-r--r-- | mingling_cli/src/linter.rs | 15 | ||||
| -rw-r--r-- | mingling_cli/src/linter/cmd_mlint.rs | 6 | ||||
| -rw-r--r-- | mingling_cli/src/main.rs | 27 | ||||
| -rw-r--r-- | mingling_cli/src/metadata.rs | 12 | ||||
| -rw-r--r-- | mingling_cli/src/metadata/cmd_metadata.rs | 16 | ||||
| -rw-r--r-- | mingling_cli/src/metadata/setup.rs | 120 |
6 files changed, 195 insertions, 1 deletions
diff --git a/mingling_cli/src/linter.rs b/mingling_cli/src/linter.rs new file mode 100644 index 0000000..870efeb --- /dev/null +++ b/mingling_cli/src/linter.rs @@ -0,0 +1,15 @@ +use mingling::{Program, macros::program_setup}; + +use crate::linter::cmd_mlint::CMDMinglingLinter; + +pub mod cmd_mlint; + +#[program_setup] +pub fn mingling_linter_setup(program: &mut Program<crate::ThisProgram>) { + program.with_setup(MinglingLinterCommandSetup); +} + +#[program_setup] +pub fn mingling_linter_command_setup(program: &mut Program<crate::ThisProgram>) { + program.with_dispatcher(CMDMinglingLinter); +} diff --git a/mingling_cli/src/linter/cmd_mlint.rs b/mingling_cli/src/linter/cmd_mlint.rs new file mode 100644 index 0000000..1431c43 --- /dev/null +++ b/mingling_cli/src/linter/cmd_mlint.rs @@ -0,0 +1,6 @@ +use mingling::macros::{chain, dispatcher}; + +dispatcher!("mlint", CMDMinglingLinter => EntryMinglingLinter); + +#[chain] +pub fn handle_mlint(_args: EntryMinglingLinter) {} diff --git a/mingling_cli/src/main.rs b/mingling_cli/src/main.rs index e7a11a9..59fa92d 100644 --- a/mingling_cli/src/main.rs +++ b/mingling_cli/src/main.rs @@ -1,3 +1,28 @@ +use crate::{linter::MinglingLinterSetup, metadata::MinglingMetadataSetup}; +use mingling::{ + macros::gen_program, + setup::{ + ExitCodeSetup, + picker::{HelpFlagSetup, StructuralRendererSetup}, + }, +}; + +pub mod linter; +pub mod metadata; + fn main() { - println!("Hello, world!"); + let mut program = ThisProgram::new(); + + // Setups + program.with_setup(HelpFlagSetup::default()); + program.with_setup(ExitCodeSetup::default()); + program.with_setup(StructuralRendererSetup); + + program.with_setup(MinglingMetadataSetup); + program.with_setup(MinglingLinterSetup); + + // Exec + program.exec_and_exit(); } + +gen_program!(); diff --git a/mingling_cli/src/metadata.rs b/mingling_cli/src/metadata.rs new file mode 100644 index 0000000..0c0b0bf --- /dev/null +++ b/mingling_cli/src/metadata.rs @@ -0,0 +1,12 @@ +use mingling::{Program, macros::program_setup}; + +use crate::metadata::{cmd_metadata::CMDMetadata, setup::CargoMetadataSetup}; + +pub mod cmd_metadata; +pub mod setup; + +#[program_setup] +pub fn mingling_metadata_setup(program: &mut Program<crate::ThisProgram>) { + program.with_setup(CargoMetadataSetup); + program.with_dispatcher(CMDMetadata); +} diff --git a/mingling_cli/src/metadata/cmd_metadata.rs b/mingling_cli/src/metadata/cmd_metadata.rs new file mode 100644 index 0000000..867e606 --- /dev/null +++ b/mingling_cli/src/metadata/cmd_metadata.rs @@ -0,0 +1,16 @@ +use mingling::{ + LazyRes, + macros::{chain, dispatcher, pack_structural}, +}; + +use crate::metadata::setup::ResMetadata; + +dispatcher!("metadata"); + +pack_structural!(ResultMetadata = ResMetadata); + +#[chain] +pub fn handle_metadata(_: EntryMetadata, metadata: &mut LazyRes<ResMetadata>) -> ResultMetadata { + let metadata = metadata.get_ref().clone(); + ResultMetadata::new(metadata) +} diff --git a/mingling_cli/src/metadata/setup.rs b/mingling_cli/src/metadata/setup.rs new file mode 100644 index 0000000..4a891b6 --- /dev/null +++ b/mingling_cli/src/metadata/setup.rs @@ -0,0 +1,120 @@ +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; + +#[derive(Default, Clone, Serialize)] +pub struct ResMetadata { + data: Option<Metadata>, +} + +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() + } +} + +#[program_setup] +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(&arg![features: Vec<String>]) + .pick(&arg![manifest_path: Option<String>]) + .pick_or(&arg![message_format: String], || "disable".to_string()) + .pick(&arg![all_features: Flag]) + .pick(&arg![no_default_features: Flag]) + .pick(&arg![no_deps: Flag]) + .pick(&REMAINS) + .unwrap(); + program.replace_args(args.into()); + + // Bind format setting to StructuralRenderer + program.structural_renderer_name = message_format.into(); + + // Current Dir + let current_dir = current_dir().unwrap(); + + // Leak `feature_args` into a static slice so it can be captured by the metadata closure. + // Since the process does not loop, this memory leak is harmless. + 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 || { + // Paths + let metadata_path = match manifest_path_clone { + Some(ref path_str) => PathBuf::from(path_str), + None => find_manifest().unwrap(), + }; + + // Cargo Metadata - bind to a longer-lived variable + let mut cmd = MetadataCommand::new(); + cmd.manifest_path(metadata_path) + .current_dir(¤t_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()), + } + })); + + // Current Dir + 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; + } + } +} |
