diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-28 06:21:56 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-28 06:21:56 +0800 |
| commit | 0f84a88d0b2c9205ec1b3cbfa18ffe05478e5a64 (patch) | |
| tree | 0d5d3ddff999937599f82e7c0fd1b585d826c9e7 /mingling_pathf/src/pattern_analyzer.rs | |
| parent | da5e1a21fce9a303767af4a6d3cab8f0d66e5c87 (diff) | |
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.
Diffstat (limited to 'mingling_pathf/src/pattern_analyzer.rs')
| -rw-r--r-- | mingling_pathf/src/pattern_analyzer.rs | 140 |
1 files changed, 140 insertions, 0 deletions
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<AnalyzeItem>, +} + +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<String> { + 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<AnalyzeItem>; +} + +/// A pattern analyzer that registers and runs multiple `AnalyzePattern` instances to parse +/// referenceable items from code. +/// +/// Internally maintains a `Vec<Box<dyn AnalyzePattern>>` 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<Box<dyn AnalyzePattern>>, +} + +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<String>`. + /// + /// # Parameters + /// - `path` —— The path to the file to analyze, supporting any type that implements `AsRef<Path>`. + /// + /// # Returns + /// - `Ok(HashSet<String>)` —— 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<Path>) -> Result<HashSet<String>, 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()) + } +} |
