fast-shlibdeps 0.2.1

A fast ELF shared library dependency analyzer for Debian-based systems
use std::collections::HashSet;

use anyhow::Result;
use clap::Parser;
use walkdir::WalkDir;

use crate::{
    dpkg::{find_packages_for_paths, get_package_versions},
    elf::get_file_dependencies,
    search::get_ld_cache,
};

mod dpkg;
mod elf;
mod search;

#[derive(Parser, Debug)]
#[command(version, about)]
struct Args {
    paths: Vec<String>,
}

fn main() -> Result<()> {
    let args = Args::parse();
    let files = args
        .paths
        .iter()
        .flat_map(|p| WalkDir::new(p))
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file());
    let mut libraries = HashSet::new();
    for file in files {
        let deps = get_file_dependencies(file.path())?;
        libraries.extend(deps);
    }
    let ld_cache = get_ld_cache()?;
    let library_paths = libraries
        .iter()
        .flat_map(|l| ld_cache.get(l))
        .map(|s| s.as_str())
        .collect::<HashSet<&str>>();
    let path_to_package = find_packages_for_paths(library_paths.iter())?;
    let packages: HashSet<&str> = library_paths
        .iter()
        .filter_map(|path| path_to_package.get(*path))
        .map(|s| s.as_str())
        .collect();
    let package_versions = get_package_versions(packages.iter())?;
    let deps = packages
        .iter()
        .map(|p| {
            if let Some(version) = package_versions.get(*p) {
                format!("{p} (>={version})")
            } else {
                p.to_string()
            }
        })
        .collect::<Vec<String>>();
    let deps = deps.join(", ");
    println!("{deps}");
    Ok(())
}