provenant-cli 0.0.15

Rust-based ScanCode-compatible scanner for licenses, package metadata, SBOMs, and provenance data.
Documentation
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{Context, Result};

pub fn find_files_with_extension(dir: &Path, extension: &str) -> Result<Vec<PathBuf>> {
    let mut paths = Vec::new();
    if !dir.is_dir() {
        return Ok(paths);
    }

    fn recurse(dir: &Path, extension: &str, out: &mut Vec<PathBuf>) -> Result<()> {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                recurse(&path, extension, out)?;
            } else if path
                .extension()
                .and_then(|ext| ext.to_str())
                .is_some_and(|ext| ext == extension)
            {
                out.push(path);
            }
        }
        Ok(())
    }

    recurse(dir, extension, &mut paths)?;
    paths.sort();
    Ok(paths)
}

pub fn run_prettier(paths: &[PathBuf]) -> Result<()> {
    if paths.is_empty() {
        return Ok(());
    }

    let ignore_path = std::env::temp_dir().join("provenant-empty-prettierignore");
    if !ignore_path.exists() {
        fs::write(&ignore_path, "").context("failed to create empty prettier ignore file")?;
    }

    const CHUNK_SIZE: usize = 100;
    for chunk in paths.chunks(CHUNK_SIZE) {
        let mut cmd = Command::new("npm");
        cmd.args([
            "exec",
            "--",
            "prettier",
            "--write",
            "--ignore-path",
            ignore_path
                .to_str()
                .context("temporary prettier ignore path is not valid UTF-8")?,
        ]);
        for path in chunk {
            cmd.arg(path);
        }

        let output = cmd
            .output()
            .context("failed to run `npm exec -- prettier --write`")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            anyhow::bail!(
                "prettier formatting failed (status: {}): {}",
                output.status,
                stderr.trim()
            );
        }
    }

    Ok(())
}