diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-10 16:30:20 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-10 16:30:20 +0800 |
| commit | 03b487f0f93ac32835149d4c1a0fb420341134f8 (patch) | |
| tree | 80e3e901c054581dcffc09da471053d15eb12560 /mingling_pathf | |
| parent | 1be60c990f27cd9006fa3db8aa5bd623c33840e8 (diff) | |
feat(pathf): enforce pedantic lints and update API ergonomics
- Add `#[must_use]` to public constructors and getters
- Change `init_with_config` to accept `&PathfinderConfig` instead of
owned value
- Refactor `map_or_else` and `or_insert_with` for clippy compliance
- Add `#![deny(clippy::pedantic)]` and `#![deny(clippy::nursery)]`
- Document error cases in public API docs
- Simplify duplicated dispatcher_clap analysis logic
Diffstat (limited to 'mingling_pathf')
| -rw-r--r-- | mingling_pathf/src/config.rs | 3 | ||||
| -rw-r--r-- | mingling_pathf/src/lib.rs | 2 | ||||
| -rw-r--r-- | mingling_pathf/src/module_pathf.rs | 17 | ||||
| -rw-r--r-- | mingling_pathf/src/pattern_analyzer.rs | 35 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher.rs | 24 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/dispatcher_clap.rs | 176 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/group.rs | 34 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/metadata.rs | 7 | ||||
| -rw-r--r-- | mingling_pathf/src/patterns/pack.rs | 27 | ||||
| -rw-r--r-- | mingling_pathf/src/type_mapping_builder.rs | 22 | ||||
| -rw-r--r-- | mingling_pathf/test/src/lib.rs | 4 |
11 files changed, 164 insertions, 187 deletions
diff --git a/mingling_pathf/src/config.rs b/mingling_pathf/src/config.rs index 1199b2c..758aa34 100644 --- a/mingling_pathf/src/config.rs +++ b/mingling_pathf/src/config.rs @@ -17,7 +17,8 @@ pub struct PathfinderConfig { impl PathfinderConfig { /// Create a config with `use_dispatch_tree` enabled. - pub fn with_dispatch_tree() -> Self { + #[must_use] + pub const fn with_dispatch_tree() -> Self { Self { use_dispatch_tree: true, } diff --git a/mingling_pathf/src/lib.rs b/mingling_pathf/src/lib.rs index 69ff3ac..d54cf58 100644 --- a/mingling_pathf/src/lib.rs +++ b/mingling_pathf/src/lib.rs @@ -1,6 +1,8 @@ #![allow(clippy::needless_doctest_main)] #![doc = include_str!("../README.md")] #![deny(missing_docs)] +#![deny(clippy::pedantic)] +#![deny(clippy::nursery)] pub mod config; pub mod error; diff --git a/mingling_pathf/src/module_pathf.rs b/mingling_pathf/src/module_pathf.rs index f0a06d1..ebb7979 100644 --- a/mingling_pathf/src/module_pathf.rs +++ b/mingling_pathf/src/module_pathf.rs @@ -25,11 +25,13 @@ pub struct MappingItem { impl MappingItem { /// Returns the path of the source file (relative to the crate root, with `./` prefix). + #[must_use] pub fn file_path(&self) -> &Path { &self.file_path } /// Returns the effective module path corresponding to this file (e.g., `"crate::foo::bar"`). + #[must_use] pub fn module_path(&self) -> &str { &self.module_path } @@ -50,6 +52,12 @@ pub struct ModulePathMapping { /// Analyzes the module structure of a crate and returns the effective module path for each source file. /// /// `crate_dir` is the crate root directory (i.e., the directory containing `Cargo.toml`). +/// +/// # Errors +/// +/// Returns an error if the `src/` directory does not exist, no entry point file is found, +/// a source file cannot be read or parsed, a child module file cannot be resolved, or +/// an I/O error occurs while traversing the crate directory. pub fn analyze(crate_dir: &Path) -> Result<ModulePathMapping, MinglingPathfinderError> { let src_dir = crate_dir.join("src"); if !src_dir.is_dir() { @@ -121,11 +129,8 @@ impl Context { } fn relative_path(&self, abs: &Path) -> PathBuf { - if let Ok(rel) = abs.strip_prefix(&self.crate_dir) { - PathBuf::from("./").join(rel) - } else { - abs.to_path_buf() - } + abs.strip_prefix(&self.crate_dir) + .map_or_else(|_| abs.to_path_buf(), |rel| PathBuf::from("./").join(rel)) } } @@ -276,7 +281,7 @@ fn collect_reexports(tree: &UseTree, results: &mut Vec<String>) { UseTree::Rename(rename) => { results.push(rename.ident.to_string()); } - _ => {} + UseTree::Glob(_) => {} } } diff --git a/mingling_pathf/src/pattern_analyzer.rs b/mingling_pathf/src/pattern_analyzer.rs index 1fc0cbe..44df7ad 100644 --- a/mingling_pathf/src/pattern_analyzer.rs +++ b/mingling_pathf/src/pattern_analyzer.rs @@ -17,16 +17,21 @@ use std::path::Path; use crate::config::PathfinderConfig; use crate::error::MinglingPathfinderError; -use crate::patterns::*; +use crate::patterns::{ + ChainPattern, CommandPattern, CompletionPattern, DispatcherClapPattern, DispatcherPattern, + GroupPattern, GroupedDerivePattern, HelpPattern, MetadataPattern, PackPattern, RendererPattern, +}; /// Creates a default `PatternAnalyzer` with all built-in patterns pre-registered. +#[must_use] pub fn init() -> PatternAnalyzer { - init_with_config(PathfinderConfig::default()) + init_with_config(&PathfinderConfig::default()) } /// Creates a `PatternAnalyzer` with the given config, used by `mingling_core`'s pathf wrapper /// to inject feature-dependent settings (e.g., `dispatch_tree`). -pub fn init_with_config(config: PathfinderConfig) -> PatternAnalyzer { +#[must_use] +pub fn init_with_config(config: &PathfinderConfig) -> PatternAnalyzer { let mut analyzer = PatternAnalyzer::new(); analyzer.add_pattern(PackPattern); analyzer.add_pattern(GroupPattern); @@ -57,7 +62,8 @@ pub struct AnalyzeItem { impl AnalyzeItem { /// Creates a local `AnalyzeItem` (not foreign, will be prefixed with the file's module path). - pub fn local(module: String, item_name: String) -> Self { + #[must_use] + pub const fn local(module: String, item_name: String) -> Self { Self { module, item_name, @@ -67,7 +73,8 @@ impl AnalyzeItem { } /// Creates a local module item — generates a `use path::item_name::*;` glob import. - pub fn local_module(module: String, item_name: String) -> Self { + #[must_use] + pub const fn local_module(module: String, item_name: String) -> Self { Self { module, item_name, @@ -77,7 +84,8 @@ impl AnalyzeItem { } /// Creates a foreign `AnalyzeItem` (resolved via `use`, the `module` field is the full import path). - pub fn foreign(module: String, item_name: String) -> Self { + #[must_use] + pub const fn foreign(module: String, item_name: String) -> Self { Self { module, item_name, @@ -95,11 +103,13 @@ pub struct AnalyzeResult { impl AnalyzeResult { /// Creates an empty `AnalyzeResult` instance - pub fn new() -> Self { + #[must_use] + pub const fn new() -> Self { Self { items: Vec::new() } } /// Formats all items into a set of strings in the format `"::{module_path}::{item_name}"` + #[must_use] pub fn into_formatted(self) -> HashSet<String> { self.items .into_iter() @@ -125,7 +135,7 @@ 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 + /// Analyze the content and return all found `AnalyzeItem`s fn analyze(&self, content: &str) -> Vec<AnalyzeItem>; } @@ -138,6 +148,7 @@ pub struct PatternAnalyzer { impl PatternAnalyzer { /// Creates a new empty `PatternAnalyzer`. + #[must_use] pub fn new() -> Self { Self::default() } @@ -148,6 +159,10 @@ impl PatternAnalyzer { } /// Analyzes a single file and returns a formatted set of strings. + /// + /// # Errors + /// + /// Returns a [`MinglingPathfinderError`] if the file cannot be read. pub fn analyze_file( &self, path: impl AsRef<Path>, @@ -157,6 +172,10 @@ impl PatternAnalyzer { } /// Analyzes a single file and returns the raw `Vec<AnalyzeItem>`. + /// + /// # Errors + /// + /// Returns a [`MinglingPathfinderError`] if the file cannot be read. pub fn analyze_file_items( &self, path: impl AsRef<Path>, diff --git a/mingling_pathf/src/patterns/dispatcher.rs b/mingling_pathf/src/patterns/dispatcher.rs index cedad9f..85220b5 100644 --- a/mingling_pathf/src/patterns/dispatcher.rs +++ b/mingling_pathf/src/patterns/dispatcher.rs @@ -32,7 +32,8 @@ impl DispatcherPattern { /// * `use_dispatch_tree` — when `true`, the generated dispatcher also produces a /// `__internal_dispatcher_*` static dispatch tree item. Set this based on whether /// your macro invocation includes the `use_dispatch_tree` configuration. - pub fn new(use_dispatch_tree: bool) -> Self { + #[must_use] + pub const fn new(use_dispatch_tree: bool) -> Self { Self { use_dispatch_tree } } } @@ -102,9 +103,8 @@ fn extract_all_types( use_dispatch_tree: bool, ) -> Vec<AnalyzeItem> { let (cmd_name, cmd_struct, entry_struct) = parse_dispatcher_args(tokens); - let cmd_name = match cmd_name { - Some(n) => n, - None => return Vec::new(), + let Some(cmd_name) = cmd_name else { + return Vec::new(); }; let mut items = Vec::new(); @@ -128,7 +128,7 @@ fn extract_all_types( items } -/// Parses dispatcher arguments and returns (command_name, cmd_struct, entry_struct). +/// Parses dispatcher arguments and returns (`command_name`, `cmd_struct`, `entry_struct`). fn parse_dispatcher_args( tokens: &proc_macro2::TokenStream, ) -> (Option<String>, Option<String>, Option<String>) { @@ -166,9 +166,8 @@ fn parse_dispatcher_args( } // Implicit form: "name" - let cmd_name = match extract_string_literal(&stream) { - Some(n) => n, - None => return (None, None, None), + let Some(cmd_name) = extract_string_literal(&stream) else { + return (None, None, None); }; let pascal = to_pascal_case(&cmd_name); ( @@ -192,15 +191,14 @@ fn to_pascal_case(s: &str) -> String { .filter(|s| !s.is_empty()) .map(|s| { let mut c = s.chars(); - match c.next() { - None => String::new(), - Some(f) => f.to_uppercase().collect::<String>() + c.as_str(), - } + c.next().map_or_else(String::new, |f| { + f.to_uppercase().collect::<String>() + c.as_str() + }) }) .collect() } -/// Simple snake_case conversion (replaces `.`, `-` with `_`). +/// Simple `snake_case` conversion (replaces `.`, `-` with `_`). fn snake_case(s: &str) -> String { s.replace(['.', '-'], "_").to_lowercase() } diff --git a/mingling_pathf/src/patterns/dispatcher_clap.rs b/mingling_pathf/src/patterns/dispatcher_clap.rs index 25a7093..ca2ce9e 100644 --- a/mingling_pathf/src/patterns/dispatcher_clap.rs +++ b/mingling_pathf/src/patterns/dispatcher_clap.rs @@ -39,7 +39,8 @@ impl DispatcherClapPattern { /// # Parameters /// - `use_dispatch_tree`: When `true`, enables analysis of the `__internal_dispatcher_*` /// static dispatch tree for each matched command. - pub fn new(use_dispatch_tree: bool) -> Self { + #[must_use] + pub const fn new(use_dispatch_tree: bool) -> Self { Self { use_dispatch_tree } } } @@ -59,52 +60,7 @@ impl AnalyzePattern for DispatcherClapPattern { for item in &syntax.items { match item { Item::Struct(s) if has_attr(&s.attrs, "dispatcher_clap") => { - // Entry type (struct name) — always - let entry_name = s.ident.to_string(); - items.push(AnalyzeItem::local(String::new(), entry_name.clone())); - - // Parse the attribute to extract CMD, error, and help info - if let Some(attr) = s.attrs.iter().find(|a| { - a.path() - .segments - .last() - .is_some_and(|seg| seg.ident == "dispatcher_clap") - }) { - let args = attr.meta.require_list().ok(); - let args_str = args.map(|l| l.tokens.to_string()).unwrap_or_default(); - let parsed = parse_dispatcher_clap_args(&args_str); - - // CMD type — always - if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem::local(String::new(), cmd.clone())); - } - - // Error type — if error = TypeName - if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem::local(String::new(), err.clone())); - } - - // Help internal struct — if help = true - if parsed.help_enabled - && let Some(ref cmd) = parsed.cmd_type - { - let help_fn = format!("__{}_help", just_fmt::snake_case!(cmd)); - let help_struct = - format!("__internal_help_{}", just_fmt::snake_case!(&help_fn)); - items.push(AnalyzeItem::local(String::new(), help_struct)); - } - - // __internal_dispatcher_* — when configured - if self.use_dispatch_tree - && let Some(ref cmd_name) = parsed.cmd_name - { - let internal_name = format!( - "__internal_dispatcher_{}", - just_fmt::snake_case!(cmd_name) - ); - items.push(AnalyzeItem::local(String::new(), internal_name)); - } - } + items.extend(self.analyze_struct(s, "")); } Item::Mod(item_mod) => { if let Some((_, nested)) = &item_mod.content { @@ -112,67 +68,7 @@ impl AnalyzePattern for DispatcherClapPattern { if let Item::Struct(s) = n && has_attr(&s.attrs, "dispatcher_clap") { - let entry_name = s.ident.to_string(); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - entry_name.clone(), - )); - - if let Some(attr) = s.attrs.iter().find(|a| { - a.path() - .segments - .last() - .is_some_and(|seg| seg.ident == "dispatcher_clap") - }) { - let args = attr.meta.require_list().ok(); - let args_str = - args.map(|l| l.tokens.to_string()).unwrap_or_default(); - let parsed = parse_dispatcher_clap_args(&args_str); - - if let Some(ref cmd) = parsed.cmd_type { - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - cmd.clone(), - )); - } - - if let Some(ref err) = parsed.error_type { - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - err.clone(), - )); - } - - // Help internal struct — same naming rule as root level - if parsed.help_enabled - && let Some(ref cmd) = parsed.cmd_type - { - let help_fn = - format!("__{}_help", just_fmt::snake_case!(cmd)); - let help_struct = format!( - "__internal_help_{}", - just_fmt::snake_case!(&help_fn) - ); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - help_struct, - )); - } - - // __internal_dispatcher_* — when configured - if self.use_dispatch_tree - && let Some(ref cmd_name) = parsed.cmd_name - { - let internal_name = format!( - "__internal_dispatcher_{}", - just_fmt::snake_case!(cmd_name) - ); - items.push(AnalyzeItem::local( - item_mod.ident.to_string(), - internal_name, - )); - } - } + items.extend(self.analyze_struct(s, &item_mod.ident.to_string())); } } } @@ -185,6 +81,58 @@ impl AnalyzePattern for DispatcherClapPattern { } } +impl DispatcherClapPattern { + fn analyze_struct(&self, s: &syn::ItemStruct, module: &str) -> Vec<AnalyzeItem> { + let mut items = Vec::new(); + + // Entry type (struct name) — always + let entry_name = s.ident.to_string(); + items.push(AnalyzeItem::local(module.to_string(), entry_name)); + + // Parse the attribute to extract CMD, error, and help info + if let Some(attr) = s.attrs.iter().find(|a| { + a.path() + .segments + .last() + .is_some_and(|seg| seg.ident == "dispatcher_clap") + }) { + let args = attr.meta.require_list().ok(); + let args_str = args.map(|l| l.tokens.to_string()).unwrap_or_default(); + let parsed = parse_dispatcher_clap_args(&args_str); + + // CMD type — always + if let Some(ref cmd) = parsed.cmd_type { + items.push(AnalyzeItem::local(module.to_string(), cmd.clone())); + } + + // Error type — if error = TypeName + if let Some(ref err) = parsed.error_type { + items.push(AnalyzeItem::local(module.to_string(), err.clone())); + } + + // Help internal struct — if help = true + if parsed.help_enabled + && let Some(ref cmd) = parsed.cmd_type + { + let help_fn = format!("__{}_help", just_fmt::snake_case!(cmd)); + let help_struct = format!("__internal_help_{}", just_fmt::snake_case!(&help_fn)); + items.push(AnalyzeItem::local(module.to_string(), help_struct)); + } + + // __internal_dispatcher_* — when configured + if self.use_dispatch_tree + && let Some(ref cmd_name) = parsed.cmd_name + { + let internal_name = + format!("__internal_dispatcher_{}", just_fmt::snake_case!(cmd_name)); + items.push(AnalyzeItem::local(module.to_string(), internal_name)); + } + } + + items + } +} + struct ParsedClapArgs { cmd_name: Option<String>, cmd_type: Option<String>, @@ -202,17 +150,13 @@ fn parse_dispatcher_clap_args(args: &str) -> ParsedClapArgs { let args = args.trim(); // Extract the first quoted string (the command name) - let after_cmd = if let Some(start) = args.find('"') { + let after_cmd = args.find('"').map_or(args, |start| { let after_open = &args[start + 1..]; - if let Some(end) = after_open.find('"') { + after_open.find('"').map_or(args, |end| { cmd_name = Some(after_open[..end].to_string()); after_open[end + 1..].trim() - } else { - args - } - } else { - args - }; + }) + }); // Split by commas and parse each part for part in after_cmd.split(',') { diff --git a/mingling_pathf/src/patterns/group.rs b/mingling_pathf/src/patterns/group.rs index 0f9ecff..905afa9 100644 --- a/mingling_pathf/src/patterns/group.rs +++ b/mingling_pathf/src/patterns/group.rs @@ -129,7 +129,8 @@ fn collect_from_use_tree( UseTree::Name(name) => { let module = prefix.to_string(); let alias = name.ident.to_string(); - map.entry(alias).or_insert((module, name.ident.to_string())); + map.entry(alias) + .or_insert_with(|| (module, name.ident.to_string())); } UseTree::Path(use_path) => { let new_prefix = if prefix.is_empty() { @@ -143,7 +144,7 @@ fn collect_from_use_tree( let module = prefix.to_string(); let alias = rename.ident.to_string(); map.entry(alias) - .or_insert((module, rename.ident.to_string())); + .or_insert_with(|| (module, rename.ident.to_string())); } UseTree::Glob(_) => { // `use path::*;` — skip glob imports @@ -178,24 +179,21 @@ fn extract_group_name(tokens: &proc_macro2::TokenStream) -> Option<String> { let mut iter = stream.into_iter(); loop { - match iter.next()? { - proc_macro2::TokenTree::Ident(ident) => { - let name = ident.to_string(); - - // Check if there is a `=` following - let next = iter.next(); - match next { - Some(proc_macro2::TokenTree::Punct(p)) if p.as_char() == '=' => { - // group!(Alias = path::Type) - return Some(name); - } - _ => { - // group!(TypeName) - return Some(name); - } + if let proc_macro2::TokenTree::Ident(ident) = iter.next()? { + let name = ident.to_string(); + + // Check if there is a `=` following + let next = iter.next(); + match next { + Some(proc_macro2::TokenTree::Punct(p)) if p.as_char() == '=' => { + // group!(Alias = path::Type) + return Some(name); + } + _ => { + // group!(TypeName) + return Some(name); } } - _ => continue, } } } diff --git a/mingling_pathf/src/patterns/metadata.rs b/mingling_pathf/src/patterns/metadata.rs index 24243bf..32a283a 100644 --- a/mingling_pathf/src/patterns/metadata.rs +++ b/mingling_pathf/src/patterns/metadata.rs @@ -95,7 +95,7 @@ fn extract_bind_type(attrs: &[syn::Attribute]) -> Option<String> { continue; } if let syn::Meta::List(meta_list) = &attr.meta { - for token in meta_list.tokens.clone().into_iter() { + for token in meta_list.tokens.clone() { if let proc_macro2::TokenTree::Ident(ident) = token { return Some(ident.to_string()); } @@ -139,7 +139,8 @@ fn collect_from_use_tree( UseTree::Name(name) => { let module = prefix.to_string(); let alias = name.ident.to_string(); - map.entry(alias).or_insert((module, name.ident.to_string())); + map.entry(alias) + .or_insert_with(|| (module, name.ident.to_string())); } UseTree::Path(use_path) => { let new_prefix = if prefix.is_empty() { @@ -153,7 +154,7 @@ fn collect_from_use_tree( let module = prefix.to_string(); let alias = rename.ident.to_string(); map.entry(alias) - .or_insert((module, rename.ident.to_string())); + .or_insert_with(|| (module, rename.ident.to_string())); } UseTree::Glob(_) => {} UseTree::Group(group) => { diff --git a/mingling_pathf/src/patterns/pack.rs b/mingling_pathf/src/patterns/pack.rs index 76597ba..e5e14e6 100644 --- a/mingling_pathf/src/patterns/pack.rs +++ b/mingling_pathf/src/patterns/pack.rs @@ -84,23 +84,20 @@ fn try_extract_pack_name(m: &syn::ItemMacro) -> Option<String> { // Skip leading attributes/doc comments loop { - match iter.next()? { - proc_macro2::TokenTree::Ident(ident) => { - // Found the first ident, this is the type name - let type_name = ident.to_string(); - - // Check if `=` follows - if let Some(proc_macro2::TokenTree::Punct(p)) = iter.next() - && p.as_char() == '=' - { - // pack!(TypeName = InnerType) - return Some(type_name); - } - - // pack_err!(TypeName) — only a single ident + if let proc_macro2::TokenTree::Ident(ident) = iter.next()? { + // Found the first ident, this is the type name + let type_name = ident.to_string(); + + // Check if `=` follows + if let Some(proc_macro2::TokenTree::Punct(p)) = iter.next() + && p.as_char() == '=' + { + // pack!(TypeName = InnerType) return Some(type_name); } - _ => continue, + + // pack_err!(TypeName) — only a single ident + return Some(type_name); } } } diff --git a/mingling_pathf/src/type_mapping_builder.rs b/mingling_pathf/src/type_mapping_builder.rs index fb59b43..9f8980d 100644 --- a/mingling_pathf/src/type_mapping_builder.rs +++ b/mingling_pathf/src/type_mapping_builder.rs @@ -3,6 +3,7 @@ //! type mapping files used by the pathfinder system. use std::collections::HashSet; +use std::fmt::Write as FmtWrite; use std::path::Path; use crate::config::PathfinderConfig; @@ -14,16 +15,21 @@ use crate::pattern_analyzer; /// /// `crate_dir` — crate root directory (i.e., the directory containing Cargo.toml) /// `output_dir` — directory where mapping files will be written -/// `config` — pathfinder configuration (e.g., dispatch_tree detection) +/// `config` — pathfinder configuration (e.g., [`dispatch_tree`] detection) /// /// Mapping file format per line: `TypeName = crate::module::path::TypeName` +/// +/// # Errors +/// +/// Returns a [`MinglingPathfinderError`] if the module analysis fails, the output +/// directory cannot be created, or the mapping files cannot be written. pub fn analyze_and_build_type_mapping_for( crate_dir: &Path, output_dir: &Path, config: &PathfinderConfig, ) -> Result<(), MinglingPathfinderError> { let module_mapping = module_pathf::analyze(crate_dir)?; - let analyzer = pattern_analyzer::init_with_config(config.clone()); + let analyzer = pattern_analyzer::init_with_config(config); let mut type_mappings: Vec<(String, String, bool)> = Vec::new(); @@ -66,16 +72,16 @@ pub fn analyze_and_build_type_mapping_for( let mut content_mapping = String::new(); for (name, path, _) in &type_mappings { - content_mapping.push_str(&format!("{name} = {path}\n")); + let _ = writeln!(content_mapping, "{name} = {path}"); } std::fs::write(&output_path, content_mapping)?; let mut content_using = String::new(); for (_, path, is_module) in &type_mappings { if *is_module { - content_using.push_str(&format!("use {path}::*;\n")); + let _ = writeln!(content_using, "use {path}::*;"); } else { - content_using.push_str(&format!("use {path};\n")); + let _ = writeln!(content_using, "use {path};"); } } std::fs::write(&type_using_path, content_using)?; @@ -87,6 +93,12 @@ pub fn analyze_and_build_type_mapping_for( /// from environment variables. /// /// Reads `CARGO_PKG_NAME` and `OUT_DIR`, and outputs to `{OUT_DIR}/{CARGO_PKG_NAME}/`. +/// +/// # Errors +/// +/// Returns a [`MinglingPathfinderError`] if the required environment variables +/// (`CARGO_PKG_NAME`, `OUT_DIR`) are not set, the current directory cannot be +/// determined, or the type mapping generation fails. pub fn analyze_and_build_type_mapping() -> Result<(), MinglingPathfinderError> { let crate_name = std::env::var("CARGO_PKG_NAME").map_err(|_| { MinglingPathfinderError::IoError(std::io::Error::new( diff --git a/mingling_pathf/test/src/lib.rs b/mingling_pathf/test/src/lib.rs index b543a15..95d7410 100644 --- a/mingling_pathf/test/src/lib.rs +++ b/mingling_pathf/test/src/lib.rs @@ -300,7 +300,7 @@ fn test_dispatcher_dispatch_tree() { assert!(r1.contains("::sub::CMDGreet")); // With dispatch_tree: Entry + CMD + __internal_dispatcher - let r2 = pattern_analyzer::init_with_config(PathfinderConfig::with_dispatch_tree()) + let r2 = pattern_analyzer::init_with_config(&PathfinderConfig::with_dispatch_tree()) .analyze_file(&file) .unwrap(); // 8 (from above) + 2 __internal (root) + 2 __internal (sub) = 12 @@ -377,7 +377,7 @@ fn test_dispatcher_clap_dispatch_tree() { assert_eq!(r1.len(), 26); // With dispatch_tree: 26 + 4 __internal (root) + 3 __internal (sub, no "full") = 33 - let r2 = pattern_analyzer::init_with_config(PathfinderConfig::with_dispatch_tree()) + let r2 = pattern_analyzer::init_with_config(&PathfinderConfig::with_dispatch_tree()) .analyze_file(&file) .unwrap(); assert_eq!(r2.len(), 33); |
