Skip to content

Commit 421b600

Browse files
committed
feat: finish work on proc macros
1 parent 19090ae commit 421b600

45 files changed

Lines changed: 638 additions & 513 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cargo_pup_common/src/cli.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ mod tests {
147147
let args = parse_args(&["cargo-pup", "check"]);
148148
assert_eq!(args.command, PupCommand::Check);
149149
assert!(args.cargo_args.is_empty());
150-
150+
151151
// Test generate-config command
152152
let args = parse_args(&["cargo-pup", "generate-config"]);
153153
assert_eq!(args.command, PupCommand::GenerateConfig);
@@ -187,21 +187,27 @@ mod tests {
187187
assert_eq!(args.command, PupCommand::PrintModules);
188188
assert_eq!(args.cargo_args, vec!["--features=foo"]);
189189
}
190-
190+
191191
#[test]
192192
fn test_pup_config_argument() {
193193
// Test with --pup-config argument
194194
let args = parse_args(&["cargo-pup", "check", "--pup-config", "/tmp/pup.ron"]);
195195
assert_eq!(args.command, PupCommand::Check);
196196
assert_eq!(args.config_path, Some("/tmp/pup.ron".to_string()));
197197
assert!(args.cargo_args.is_empty());
198-
198+
199199
// Test with cargo args along with --pup-config
200-
let args = parse_args(&["cargo-pup", "check", "--pup-config", "/tmp/pup.ron", "--features=foo"]);
200+
let args = parse_args(&[
201+
"cargo-pup",
202+
"check",
203+
"--pup-config",
204+
"/tmp/pup.ron",
205+
"--features=foo",
206+
]);
201207
assert_eq!(args.command, PupCommand::Check);
202208
assert_eq!(args.config_path, Some("/tmp/pup.ron".to_string()));
203209
assert_eq!(args.cargo_args, vec!["--features=foo"]);
204-
210+
205211
// Test via cargo pup
206212
let args = parse_args(&["cargo", "pup", "check", "--pup-config", "/tmp/pup.ron"]);
207213
assert_eq!(args.command, PupCommand::Check);

cargo_pup_common/src/project_context.rs

Lines changed: 89 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl ProjectContext {
6969
base_dir: PathBuf::from(PUP_DIR),
7070
}
7171
}
72-
72+
7373
/// Creates a new empty project context with a custom base directory
7474
pub fn with_base_dir(dir_path: impl AsRef<Path>) -> Self {
7575
Self {
@@ -79,44 +79,42 @@ impl ProjectContext {
7979
base_dir: dir_path.as_ref().to_path_buf(),
8080
}
8181
}
82-
82+
8383
/// Creates a project context with provided data and default base directory (.pup)
84-
pub fn with_data(
85-
modules: Vec<String>,
86-
module_root: String,
87-
traits: Vec<TraitInfo>
88-
) -> Self {
84+
pub fn with_data(modules: Vec<String>, module_root: String, traits: Vec<TraitInfo>) -> Self {
8985
// Convert string modules to ModuleInfo
90-
let module_infos = modules.into_iter()
91-
.map(|name| ModuleInfo {
92-
name,
93-
applicable_lints: Vec::new()
86+
let module_infos = modules
87+
.into_iter()
88+
.map(|name| ModuleInfo {
89+
name,
90+
applicable_lints: Vec::new(),
9491
})
9592
.collect();
96-
93+
9794
Self {
9895
modules: module_infos,
9996
module_root,
10097
traits,
10198
base_dir: PathBuf::from(PUP_DIR),
10299
}
103100
}
104-
101+
105102
/// Creates a project context with provided data and a custom base directory
106103
pub fn with_data_and_base_dir(
107-
modules: Vec<String>,
108-
module_root: String,
104+
modules: Vec<String>,
105+
module_root: String,
109106
traits: Vec<TraitInfo>,
110-
dir_path: impl AsRef<Path>
107+
dir_path: impl AsRef<Path>,
111108
) -> Self {
112109
// Convert string modules to ModuleInfo
113-
let module_infos = modules.into_iter()
114-
.map(|name| ModuleInfo {
115-
name,
116-
applicable_lints: Vec::new()
110+
let module_infos = modules
111+
.into_iter()
112+
.map(|name| ModuleInfo {
113+
name,
114+
applicable_lints: Vec::new(),
117115
})
118116
.collect();
119-
117+
120118
Self {
121119
modules: module_infos,
122120
module_root,
@@ -135,8 +133,10 @@ impl ProjectContext {
135133
}
136134

137135
// Ensure the base directory exists
138-
fs::create_dir_all(&self.base_dir)
139-
.context(format!("Failed to create directory: {}", self.base_dir.display()))?;
136+
fs::create_dir_all(&self.base_dir).context(format!(
137+
"Failed to create directory: {}",
138+
self.base_dir.display()
139+
))?;
140140

141141
// Create a predictable filename using just the crate name
142142
let filename = format!("{}{}", self.module_root, CONTEXT_FILE_SUFFIX);
@@ -176,7 +176,10 @@ impl ProjectContext {
176176
/// along with a list of all crate names that were found
177177
pub fn load_all_contexts_from_dir(dir_path: &Path) -> Result<(ProjectContext, Vec<String>)> {
178178
if !dir_path.exists() {
179-
return Err(anyhow::anyhow!("Directory not found: {}", dir_path.display()));
179+
return Err(anyhow::anyhow!(
180+
"Directory not found: {}",
181+
dir_path.display()
182+
));
180183
}
181184

182185
// Create aggregated context with the specified base directory
@@ -235,8 +238,10 @@ impl ProjectContext {
235238
return Ok(()); // Nothing to clean if directory doesn't exist
236239
}
237240

238-
let entries = fs::read_dir(&self.base_dir)
239-
.context(format!("Failed to read directory: {}", self.base_dir.display()))?;
241+
let entries = fs::read_dir(&self.base_dir).context(format!(
242+
"Failed to read directory: {}",
243+
self.base_dir.display()
244+
))?;
240245

241246
for entry in entries.filter_map(Result::ok) {
242247
let path = entry.path();
@@ -334,7 +339,7 @@ mod tests {
334339
assert_eq!(deserialized.modules[0].applicable_lints.len(), 2);
335340
assert_eq!(deserialized.modules[1].name, "test_crate::module2");
336341
assert_eq!(deserialized.modules[1].applicable_lints.len(), 1);
337-
342+
338343
assert_eq!(deserialized.traits.len(), 1);
339344
assert_eq!(deserialized.traits[0].name, "test_crate::Trait1");
340345
assert_eq!(deserialized.traits[0].implementors.len(), 2);
@@ -346,12 +351,10 @@ mod tests {
346351
fn test_serialize_empty_module_root_error() {
347352
// Create a context with empty module_root
348353
let mut context = ProjectContext::new();
349-
context.modules = vec![
350-
ModuleInfo {
351-
name: "test::module".to_string(),
352-
applicable_lints: vec![],
353-
}
354-
];
354+
context.modules = vec![ModuleInfo {
355+
name: "test::module".to_string(),
356+
applicable_lints: vec![],
357+
}];
355358

356359
// This doesn't actually try to write to a file, just checks the validation logic
357360
let result = context.serialize_to_file();
@@ -367,25 +370,16 @@ mod tests {
367370
#[test]
368371
fn test_with_data_conversion() {
369372
// Test that the with_data method properly converts strings to ModuleInfo
370-
let modules = vec![
371-
"crate1::module1".to_string(),
372-
"crate1::module2".to_string(),
373-
];
374-
375-
let traits = vec![
376-
TraitInfo {
377-
name: "crate1::Trait1".to_string(),
378-
implementors: vec!["Type1".to_string()],
379-
applicable_lints: vec![],
380-
}
381-
];
382-
383-
let context = ProjectContext::with_data(
384-
modules.clone(),
385-
"crate1".to_string(),
386-
traits
387-
);
388-
373+
let modules = vec!["crate1::module1".to_string(), "crate1::module2".to_string()];
374+
375+
let traits = vec![TraitInfo {
376+
name: "crate1::Trait1".to_string(),
377+
implementors: vec!["Type1".to_string()],
378+
applicable_lints: vec![],
379+
}];
380+
381+
let context = ProjectContext::with_data(modules.clone(), "crate1".to_string(), traits);
382+
389383
assert_eq!(context.modules.len(), 2);
390384
assert_eq!(context.modules[0].name, modules[0]);
391385
assert_eq!(context.modules[1].name, modules[1]);
@@ -396,11 +390,11 @@ mod tests {
396390
#[test]
397391
fn roundtrip_through_files() {
398392
use tempfile::TempDir;
399-
393+
400394
// Create a test-specific temp directory
401395
let temp_dir = TempDir::new().expect("Failed to create temporary directory");
402396
let test_dir_path = temp_dir.path();
403-
397+
404398
// Create first context with custom base directory
405399
let mut context1 = ProjectContext::with_base_dir(test_dir_path);
406400
context1.module_root = "crate1".to_string();
@@ -414,17 +408,15 @@ mod tests {
414408
applicable_lints: vec!["lint2".to_string()],
415409
},
416410
];
417-
context1.traits = vec![
418-
TraitInfo {
419-
name: "crate1::Trait1".to_string(),
420-
implementors: vec!["crate1::Type1".to_string()],
421-
applicable_lints: vec!["lint3".to_string()],
422-
}
423-
];
411+
context1.traits = vec![TraitInfo {
412+
name: "crate1::Trait1".to_string(),
413+
implementors: vec!["crate1::Type1".to_string()],
414+
applicable_lints: vec!["lint3".to_string()],
415+
}];
424416

425417
// Create second context with same custom base directory
426418
let mut context2 = ProjectContext::with_base_dir(test_dir_path);
427-
context2.module_root = "crate2".to_string();
419+
context2.module_root = "crate2".to_string();
428420
context2.modules = vec![
429421
ModuleInfo {
430422
name: "crate2::moduleA".to_string(),
@@ -435,68 +427,80 @@ mod tests {
435427
applicable_lints: vec!["lintB".to_string()],
436428
},
437429
];
438-
context2.traits = vec![
439-
TraitInfo {
440-
name: "crate2::TraitX".to_string(),
441-
implementors: vec!["crate2::TypeX".to_string()],
442-
applicable_lints: vec!["lintX".to_string()],
443-
}
444-
];
445-
430+
context2.traits = vec![TraitInfo {
431+
name: "crate2::TraitX".to_string(),
432+
implementors: vec!["crate2::TypeX".to_string()],
433+
applicable_lints: vec!["lintX".to_string()],
434+
}];
435+
446436
// Serialize both contexts to the temp directory
447-
let file1 = context1.serialize_to_file().expect("Failed to serialize context1");
448-
let file2 = context2.serialize_to_file().expect("Failed to serialize context2");
437+
let file1 = context1
438+
.serialize_to_file()
439+
.expect("Failed to serialize context1");
440+
let file2 = context2
441+
.serialize_to_file()
442+
.expect("Failed to serialize context2");
449443

450444
// Verify files exist
451445
assert!(file1.exists(), "Context file 1 should exist");
452446
assert!(file2.exists(), "Context file 2 should exist");
453447

454448
// Load all contexts back from our test directory
455-
let (loaded_context, crate_names) = ProjectContext::load_all_contexts_from_dir(test_dir_path)
456-
.expect("Failed to load contexts");
449+
let (loaded_context, crate_names) =
450+
ProjectContext::load_all_contexts_from_dir(test_dir_path)
451+
.expect("Failed to load contexts");
457452

458453
// Validate the loaded context
459454
// Should have a valid module root
460-
assert!(!loaded_context.module_root.is_empty(), "Module root should not be empty");
461-
455+
assert!(
456+
!loaded_context.module_root.is_empty(),
457+
"Module root should not be empty"
458+
);
459+
462460
// Should contain all modules from both contexts
463461
assert_eq!(loaded_context.modules.len(), 4, "Should have all 4 modules");
464-
462+
465463
// Check modules by name
466-
let module_names: Vec<String> = loaded_context.modules.iter()
464+
let module_names: Vec<String> = loaded_context
465+
.modules
466+
.iter()
467467
.map(|m| m.name.clone())
468468
.collect();
469469
assert!(module_names.contains(&"crate1::module1".to_string()));
470470
assert!(module_names.contains(&"crate1::module2".to_string()));
471471
assert!(module_names.contains(&"crate2::moduleA".to_string()));
472472
assert!(module_names.contains(&"crate2::moduleB".to_string()));
473-
473+
474474
// Should have both traits
475475
assert_eq!(loaded_context.traits.len(), 2, "Should have both traits");
476-
476+
477477
// Verify first trait exists
478-
let trait1 = loaded_context.traits.iter()
478+
let trait1 = loaded_context
479+
.traits
480+
.iter()
479481
.find(|t| t.name == "crate1::Trait1")
480482
.expect("Should find first trait");
481483
assert_eq!(trait1.implementors.len(), 1);
482484
assert_eq!(trait1.implementors[0], "crate1::Type1");
483485
assert_eq!(trait1.applicable_lints.len(), 1);
484486
assert_eq!(trait1.applicable_lints[0], "lint3");
485-
487+
486488
// Verify second trait exists
487-
let trait2 = loaded_context.traits.iter()
489+
let trait2 = loaded_context
490+
.traits
491+
.iter()
488492
.find(|t| t.name == "crate2::TraitX")
489493
.expect("Should find second trait");
490494
assert_eq!(trait2.implementors.len(), 1);
491495
assert_eq!(trait2.implementors[0], "crate2::TypeX");
492496
assert_eq!(trait2.applicable_lints.len(), 1);
493497
assert_eq!(trait2.applicable_lints[0], "lintX");
494-
498+
495499
// Verify both crate names were detected
496500
assert_eq!(crate_names.len(), 2, "Should have found 2 crate names");
497501
assert!(crate_names.contains(&"crate1".to_string()));
498502
assert!(crate_names.contains(&"crate2".to_string()));
499-
503+
500504
// temp_dir will be automatically cleaned up when it goes out of scope
501505
}
502506
}

cargo_pup_common/src/workspace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ pub fn find_workspace_pup_ron() -> Option<PathBuf> {
1212
} else {
1313
None
1414
}
15-
}
15+
}

cargo_pup_lint_config/src/function_lint/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'a> FunctionNamedBuilder<'a> {
5151
}
5252

5353
/// Define function matching using the fluent DSL
54-
///
54+
///
5555
/// # Example
5656
/// ```
5757
/// use cargo_pup_lint_config::{FunctionLintExt, LintBuilder};

0 commit comments

Comments
 (0)