comment-stripper-rs 0.1.0

Production-grade CLI to strip comments from Rust source trees using ra-ap-rustc_lexer
Documentation
use comment_stripper_rs::{run, Config};
use std::fs;
use std::path::PathBuf;
use tempfile::tempdir;

const COMMENTED: &str = "fn f() { // strip me\n}\n";

fn cfg(roots: Vec<PathBuf>) -> Config {
    Config {
        roots,
        check: false,
        verbose: false,
        hidden: false,
        follow_links: false,
        no_backup: true,
        strip_doc_comments: false,
        backup_suffix: ".bak".into(),
        exclude_dirs: vec!["target".into(), ".git".into()],
        include_globs: vec![],
    }
}

#[test]
fn workspace_tree_is_processed() {
    let dir = tempdir().unwrap();
    let root = dir.path();
    fs::create_dir_all(root.join("crate_a/src")).unwrap();
    fs::create_dir_all(root.join("crate_b/src")).unwrap();

    fs::write(
        root.join("crate_a/src/lib.rs"),
        "/// keep\nfn a() { // remove\n let x = 1; }\n",
    )
    .unwrap();
    fs::write(
        root.join("crate_b/src/main.rs"),
        "/*! keep */\nfn main() { /* remove */ }\n",
    )
    .unwrap();

    let stats = run(&cfg(vec![root.to_path_buf()])).unwrap();
    assert_eq!(stats.files_seen, 2);
    assert_eq!(stats.files_changed, 2);

    let a = fs::read_to_string(root.join("crate_a/src/lib.rs")).unwrap();
    let b = fs::read_to_string(root.join("crate_b/src/main.rs")).unwrap();
    assert!(a.contains("/// keep"));
    assert!(!a.contains("remove"));
    assert!(b.contains("/*! keep */"));
    assert!(!b.contains("remove"));
}

/// Several roots are each walked, and traversal is bounded to them: a sibling
/// outside every root is left untouched.
#[test]
fn multiple_roots_are_each_processed_and_bound_the_scope() {
    let dir = tempdir().unwrap();
    let root = dir.path();
    for crate_name in ["crate_a", "crate_b", "crate_c"] {
        fs::create_dir_all(root.join(crate_name).join("src")).unwrap();
        fs::write(root.join(crate_name).join("src/lib.rs"), COMMENTED).unwrap();
    }

    let stats = run(&cfg(vec![
        root.join("crate_a"),
        root.join("crate_b"),
    ]))
    .unwrap();

    assert_eq!(stats.files_seen, 2);
    assert_eq!(stats.files_changed, 2);

    assert!(!fs::read_to_string(root.join("crate_a/src/lib.rs"))
        .unwrap()
        .contains("strip me"));
    assert!(!fs::read_to_string(root.join("crate_b/src/lib.rs"))
        .unwrap()
        .contains("strip me"));
    // crate_c was not a root, so it is never visited.
    assert_eq!(
        fs::read_to_string(root.join("crate_c/src/lib.rs")).unwrap(),
        COMMENTED
    );
}

/// A file reachable from more than one root (here a parent and one of its
/// children) is processed only once, not once per root.
#[test]
fn files_reachable_from_multiple_roots_are_deduped() {
    let dir = tempdir().unwrap();
    let root = dir.path();
    fs::create_dir_all(root.join("crate_a/src")).unwrap();
    fs::create_dir_all(root.join("crate_b/src")).unwrap();
    fs::write(root.join("crate_a/src/lib.rs"), COMMENTED).unwrap();
    fs::write(root.join("crate_b/src/lib.rs"), COMMENTED).unwrap();

    // The whole tree, plus crate_a again — crate_a's file is reachable twice.
    let stats = run(&cfg(vec![root.to_path_buf(), root.join("crate_a")])).unwrap();

    // Two distinct files, even though crate_a was walked from both roots.
    assert_eq!(stats.files_seen, 2);
    assert_eq!(stats.files_changed, 2);
}

/// With backups enabled, the original contents are preserved next to the file.
#[test]
fn backups_are_created() {
    let dir = tempdir().unwrap();
    let root = dir.path();
    fs::write(root.join("lib.rs"), COMMENTED).unwrap();

    let mut config = cfg(vec![root.to_path_buf()]);
    config.no_backup = false;

    let stats = run(&config).unwrap();
    assert_eq!(stats.files_changed, 1);

    assert!(!fs::read_to_string(root.join("lib.rs"))
        .unwrap()
        .contains("strip me"));
    assert_eq!(
        fs::read_to_string(root.join("lib.rs.bak")).unwrap(),
        COMMENTED
    );
}