Parse ` /composer/autoload_namespaces.php` (PSR-0 map) and scan the listed directories for PHP classes. PSR-0 is a legacy autoloading standard where underscores in class names map to directory separators. For example, the prefix `HTMLPurifier` with base path `library/` means that the class `HTMLPurifier_Config` lives at `library/HTMLPurifier/Config.php`. Composer generates `autoload_name
(
workspace_root: &Path,
vendor_dir: &str,
)
| 514 | /// Returns an empty `HashMap` if the file does not exist or has no |
| 515 | /// entries. |
| 516 | pub fn parse_autoload_namespaces( |
| 517 | workspace_root: &Path, |
| 518 | vendor_dir: &str, |
| 519 | ) -> HashMap<String, PathBuf> { |
| 520 | let autoload_path = workspace_root |
| 521 | .join(vendor_dir) |
| 522 | .join("composer") |
| 523 | .join("autoload_namespaces.php"); |
| 524 | |
| 525 | let content = match fs::read_to_string(&autoload_path) { |
| 526 | Ok(c) => c, |
| 527 | Err(_) => return HashMap::new(), |
| 528 | }; |
| 529 | |
| 530 | // Collect all base directories listed in the PSR-0 map. |
| 531 | // Each line looks like: |
| 532 | // 'Prefix' => array($vendorDir . '/org/pkg/src'), |
| 533 | // 'Prefix' => array($vendorDir . '/org/pkg/src', $baseDir . '/lib'), |
| 534 | let mut base_dirs: Vec<PathBuf> = Vec::new(); |
| 535 | |
| 536 | for line in content.lines() { |
| 537 | let trimmed = line.trim(); |
| 538 | |
| 539 | // Skip lines that don't look like array entries. |
| 540 | if !trimmed.contains("=>") { |
| 541 | continue; |
| 542 | } |
| 543 | |
| 544 | // Extract everything after `=> array(` or `=> [` |
| 545 | let rhs = if let Some(pos) = trimmed.find("=> array(") { |
| 546 | &trimmed[pos + "=> array(".len()..] |
| 547 | } else if let Some(pos) = trimmed.find("=> [") { |
| 548 | &trimmed[pos + "=> [".len()..] |
| 549 | } else { |
| 550 | continue; |
| 551 | }; |
| 552 | |
| 553 | // Strip trailing `)`, `],`, etc. |
| 554 | let rhs = |
| 555 | rhs.trim_end_matches(|c: char| c == ')' || c == ']' || c == ',' || c.is_whitespace()); |
| 556 | |
| 557 | // Split by comma to handle multiple paths per prefix. |
| 558 | for segment in rhs.split(',') { |
| 559 | let segment = segment.trim(); |
| 560 | if let Some(relative_path) = resolve_autoload_path_entry(segment, vendor_dir) { |
| 561 | let full_path = workspace_root.join(&relative_path); |
| 562 | if full_path.is_dir() { |
| 563 | base_dirs.push(full_path); |
| 564 | } |
| 565 | } |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | // Scan each base directory for PHP class files. |
| 570 | let mut classmap = HashMap::new(); |
| 571 | |
| 572 | for base_dir in &base_dirs { |
| 573 | scan_directory_for_classes(base_dir, &mut classmap); |
no test coverage detected