thumper 0.2.2

Sync static files to BunnyCDN
use anyhow::{Context, anyhow};
use fxhash::{FxHashMap, FxHashSet};
use std::fs;
use std::path::PathBuf;

pub fn files_by_remote_name(
    root: &str,
    remote_root: &str,
) -> anyhow::Result<FxHashMap<String, PathBuf>> {
    let files = discover_files(root)?;
    let remote_root = remote_root.trim_start_matches("/").trim_end_matches("/");
    let mut by_name = FxHashMap::default();
    for file in files {
        let remote_name = file
            .strip_prefix(root)?
            .to_str()
            .context("Invalid utf8")?
            .to_owned();
        if remote_root.is_empty() {
            by_name.insert(remote_name, file);
        } else {
            by_name.insert(format!("{remote_root}/{remote_name}"), file);
        }
    }
    Ok(by_name)
}

fn discover_files(root: &str) -> anyhow::Result<FxHashSet<PathBuf>> {
    let root_path = PathBuf::from(root);
    let mut files = FxHashSet::default();

    if root_path.is_dir() {
        for entry in fs::read_dir(&root_path)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                files.extend(discover_files(path.to_str().context("Invalid utf8")?)?);
            } else if path.is_file() && !path.is_symlink() {
                files.insert(path);
            }
        }
        Ok(files)
    } else {
        Err(anyhow!("{root} is not a directory"))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn files_by_remote_name_smoketest() {
        let files = files_by_remote_name("src", "sources").unwrap();
        assert_eq!(
            files.get("sources/main.rs"),
            Some(&PathBuf::new().join("src").join("main.rs"))
        );
    }
}