perl-module-resolution-uri 0.12.2

Resolve Perl module names to URIs with workspace-safe precedence and timeouts
Documentation
use perl_module_resolution_uri::{ModuleUriResolution, resolve_module_uri};
use std::time::Duration;

#[test]
fn resolves_workspace_module_from_file_uri_folder() -> Result<(), Box<dyn std::error::Error>> {
    let temp = tempfile::tempdir()?;
    let workspace = temp.path().join("workspace");
    let module_file = workspace.join("lib").join("Foo").join("Bar.pm");

    std::fs::create_dir_all(module_file.parent().unwrap_or(&workspace))?;
    std::fs::write(&module_file, "package Foo::Bar; 1;")?;

    let workspace_uri = url::Url::from_file_path(&workspace)
        .map_err(|()| "failed to create workspace URI")?
        .to_string();

    let result = resolve_module_uri(
        "Foo::Bar",
        &[],
        &[workspace_uri],
        &["lib".to_string()],
        false,
        &[],
        Duration::from_millis(100),
    );

    match result {
        ModuleUriResolution::Resolved(uri) => {
            assert!(uri.contains("Foo"));
            assert!(uri.contains("Bar.pm"));
        }
        other => return Err(format!("expected resolved URI, got {other:?}").into()),
    }

    Ok(())
}

#[test]
fn blocks_workspace_traversal_include_paths() -> Result<(), Box<dyn std::error::Error>> {
    let temp = tempfile::tempdir()?;
    let workspace = temp.path().join("workspace");
    let escaped_dir = temp.path().join("escaped");

    std::fs::create_dir_all(&workspace)?;
    std::fs::create_dir_all(&escaped_dir)?;

    let escaped_file = escaped_dir.join("Target.pm");
    std::fs::write(&escaped_file, "package escaped::Target; 1;")?;

    let workspace_uri = url::Url::from_file_path(&workspace)
        .map_err(|()| "failed to create workspace URI")?
        .to_string();

    let result = resolve_module_uri(
        "escaped::Target",
        &[],
        &[workspace_uri],
        &["..".to_string()],
        false,
        &[],
        Duration::from_millis(100),
    );

    assert_eq!(result, ModuleUriResolution::NotFound);
    Ok(())
}

#[test]
fn reports_timeout_for_large_search_space() {
    let workspace_folders: Vec<String> =
        (0..10_000).map(|i| format!("file:///workspace-{i}")).collect();
    let include_paths: Vec<String> = (0..128).map(|i| format!("inc-{i}")).collect();
    let system_inc: Vec<std::path::PathBuf> =
        (0..128).map(|i| std::path::PathBuf::from(format!("/inc/{i}"))).collect();

    let result = resolve_module_uri(
        "Never::Found",
        &[],
        &workspace_folders,
        &include_paths,
        true,
        &system_inc,
        Duration::from_nanos(1),
    );

    assert_eq!(result, ModuleUriResolution::TimedOut);
}