From 0f84a88d0b2c9205ec1b3cbfa18ffe05478e5a64 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sun, 28 Jun 2026 06:21:56 +0800 Subject: feat(mingling_pathf): add pattern analyzer module for struct detection Add a `PatternAnalyzer` with an `AnalyzePattern` trait to detect and extract struct declarations from Rust source files, supporting nested inline modules. --- mingling_pathf/src/lib.rs | 1 + mingling_pathf/src/pattern_analyzer.rs | 140 ++++++++++++++++++++++++++++ mingling_pathf/src/patterns.rs | 5 +- mingling_pathf/src/patterns/basic_struct.rs | 51 ++++++++++ 4 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 mingling_pathf/src/pattern_analyzer.rs create mode 100644 mingling_pathf/src/patterns/basic_struct.rs (limited to 'mingling_pathf/src') diff --git a/mingling_pathf/src/lib.rs b/mingling_pathf/src/lib.rs index 5e4921b..d8d81db 100644 --- a/mingling_pathf/src/lib.rs +++ b/mingling_pathf/src/lib.rs @@ -1,3 +1,4 @@ pub mod module_pathf; +pub mod pattern_analyzer; pub mod error; pub mod patterns; diff --git a/mingling_pathf/src/pattern_analyzer.rs b/mingling_pathf/src/pattern_analyzer.rs new file mode 100644 index 0000000..cb98a5f --- /dev/null +++ b/mingling_pathf/src/pattern_analyzer.rs @@ -0,0 +1,140 @@ +use std::collections::HashSet; +use std::path::Path; + +use crate::error::MinglingPathfinderError; + +/// Top-level convenience function: creates a default PatternAnalyzer with all built-in patterns pre-registered +pub fn init() -> PatternAnalyzer { + let mut analyzer = PatternAnalyzer::new(); + macro_rules! __register { + ( $($pattern:ident),* $(,)? ) => { + $( + analyzer.add_pattern(crate::patterns::$pattern); + )* + }; + } + + __register![ + BasicStructPattern, + ]; + + analyzer +} + +/// A single analysis item representing a parseable importable/referenceable item from code +#[derive(Debug, Clone)] +pub struct AnalyzeItem { + /// The module path to which the item belongs, e.g. `"std::collections"`; empty string `""` if the item is in the root module + pub module: String, + /// The name of the item itself, e.g. `"HashMap"`, `"AnalyzeResult"`, etc. + pub item_name: String, +} + +/// Collection of analysis results +#[derive(Debug)] +pub struct AnalyzeResult { + items: Vec, +} + +impl AnalyzeResult { + /// Creates an empty `AnalyzeResult` instance + pub fn new() -> Self { + Self { items: Vec::new() } + } + + /// Formats all items into a set of strings in the format `"::{module_path}::{item_name}"` + pub fn into_formatted(self) -> HashSet { + self.items + .into_iter() + .map(|item| { + if item.module.is_empty() { + format!("::{}", item.item_name) + } else { + format!("::{}::{}", item.module, item.item_name) + } + }) + .collect() + } +} + +impl Default for AnalyzeResult { + fn default() -> Self { + Self::new() + } +} + +/// Extension point trait — one independent implementation per syntax kind +pub trait AnalyzePattern { + /// Quickly determine whether the file content contains an analyzable item + fn contains(&self, content: &str) -> bool; + + /// Analyze the content and return all found AnalyzeItems + fn analyze(&self, content: &str) -> Vec; +} + +/// A pattern analyzer that registers and runs multiple `AnalyzePattern` instances to parse +/// referenceable items from code. +/// +/// Internally maintains a `Vec>` for dynamic dispatch, supporting +/// runtime registration of custom patterns. The `Default` derive provides an empty analyzer +/// instance. +/// +/// # Fields +/// - `patterns`: A list of registered analysis patterns, each responsible for detecting and +/// extracting a specific type of analyzable item (e.g., structs, functions). +#[derive(Default)] +pub struct PatternAnalyzer { + /// A list of registered analysis patterns, each responsible for detecting and extracting + /// a specific type of analyzable item. + patterns: Vec>, +} + +impl PatternAnalyzer { + /// Creates a new `PatternAnalyzer` instance. + /// + /// Internally calls `Default::default()` directly, initially containing no registered patterns. + pub fn new() -> Self { + Self::default() + } + + /// Registers an analysis pattern with the current analyzer. + /// + /// The pattern is wrapped in a `Box` and stored in the internal pattern list, + /// and will be used to scan and analyze file content in subsequent calls to `analyze_file`. + /// + /// # Parameters + /// - `pattern` —— A type instance implementing the `AnalyzePattern` trait, + /// responsible for detecting and extracting a specific syntactic structure (e.g., structs, functions). + pub fn add_pattern(&mut self, pattern: impl AnalyzePattern + 'static) { + self.patterns.push(Box::new(pattern)); + } + + /// Analyzes a single file and returns a formatted set of strings. + /// + /// This method reads the content of the file at the specified path, then uses each registered + /// pattern to analyze the content in turn. Only patterns whose `contains` method returns `true` + /// will trigger the subsequent `analyze` call. All extracted `AnalyzeItem`s are merged and + /// converted into a formatted `HashSet`. + /// + /// # Parameters + /// - `path` —— The path to the file to analyze, supporting any type that implements `AsRef`. + /// + /// # Returns + /// - `Ok(HashSet)` —— On success, returns a formatted set of strings, each in the form `"::module_path::item_name"`. + /// - `Err(MinglingPathfinderError)` —— If the file cannot be read, returns the corresponding I/O error wrapper. + pub fn analyze_file(&self, path: impl AsRef) -> Result, MinglingPathfinderError> { + let path = path.as_ref(); + let content = std::fs::read_to_string(path)?; + + let mut all_items = Vec::new(); + for pattern in &self.patterns { + if pattern.contains(&content) { + let items = pattern.analyze(&content); + all_items.extend(items); + } + } + + let result = AnalyzeResult { items: all_items }; + Ok(result.into_formatted()) + } +} diff --git a/mingling_pathf/src/patterns.rs b/mingling_pathf/src/patterns.rs index 9aa5502..33d3503 100644 --- a/mingling_pathf/src/patterns.rs +++ b/mingling_pathf/src/patterns.rs @@ -1,3 +1,2 @@ -pub trait AnalyzePattern { - -} +mod basic_struct; +pub use basic_struct::*; diff --git a/mingling_pathf/src/patterns/basic_struct.rs b/mingling_pathf/src/patterns/basic_struct.rs new file mode 100644 index 0000000..eeb665a --- /dev/null +++ b/mingling_pathf/src/patterns/basic_struct.rs @@ -0,0 +1,51 @@ +use syn::Item; + +use crate::pattern_analyzer::{AnalyzeItem, AnalyzePattern}; + +/// Basic struct pattern analyzer. +/// +/// Used to identify and analyze struct definitions (`struct`) in Rust source code. +/// Supports analyzing root-level structs as well as structs within inline modules. +pub struct BasicStructPattern; + +impl AnalyzePattern for BasicStructPattern { + fn contains(&self, content: &str) -> bool { + content.contains("struct") + } + + fn analyze(&self, content: &str) -> Vec { + let Ok(syntax) = syn::parse_file(content) else { + return Vec::new(); + }; + + let mut items = Vec::new(); + + for item in &syntax.items { + match item { + // Root-level struct + Item::Struct(s) => { + items.push(AnalyzeItem { + module: String::new(), + item_name: s.ident.to_string(), + }); + } + // Struct within inline modules + Item::Mod(item_mod) => { + if let Some((_, nested)) = &item_mod.content { + for n in nested { + if let syn::Item::Struct(s) = n { + items.push(AnalyzeItem { + module: item_mod.ident.to_string(), + item_name: s.ident.to_string(), + }); + } + } + } + } + _ => {} + } + } + + items + } +} -- cgit