chromsize 0.0.34

just get your chrom sizes
Documentation
use chromsize::{get_sizes, writer};
use flate2::write::GzEncoder;
use flate2::Compression;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use twobit::convert::fasta::FastaReader;
use twobit::convert::to_2bit;

const FASTA: &str = ">chr1\nACGT\n>chr2\nAAC\n";
const EXPECTED_OUTPUT: &str = "chr1\t4\nchr2\t3\n";

struct TempDir {
    path: PathBuf,
}

impl TempDir {
    fn new() -> Self {
        let nanos = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("time should be after UNIX_EPOCH")
            .as_nanos();
        let path = std::env::temp_dir().join(format!(
            "chromsize-tests-{}-{}",
            std::process::id(),
            nanos
        ));
        fs::create_dir_all(&path).expect("failed to create temporary test directory");
        Self { path }
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.path);
    }
}

fn expected_sizes() -> Vec<(String, u64)> {
    vec![("chr1".to_string(), 4), ("chr2".to_string(), 3)]
}

fn assert_sizes_and_output(input: &Path, outdir: &Path, outname: &str) {
    let sizes = get_sizes(input).expect("failed to parse sequence input");
    assert_eq!(sizes, expected_sizes());

    writer(&sizes, outdir, outname.to_string()).expect("failed to write chrom sizes output");
    let output = fs::read_to_string(outdir.join(outname)).expect("failed to read output file");
    assert_eq!(output, EXPECTED_OUTPUT);
}

#[test]
fn parses_fa_and_writes_expected_output() {
    let tmp = TempDir::new();
    let fa_path = tmp.path.join("sample.fa");
    fs::write(&fa_path, FASTA).expect("failed to write FASTA fixture");

    assert_sizes_and_output(&fa_path, &tmp.path, "fa.chrom.sizes");
}

#[test]
fn parses_fa_gz_and_writes_expected_output() {
    let tmp = TempDir::new();
    let gz_path = tmp.path.join("sample.fa.gz");
    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
    encoder
        .write_all(FASTA.as_bytes())
        .expect("failed to gzip FASTA fixture");
    let compressed = encoder.finish().expect("failed to finish gzip stream");
    fs::write(&gz_path, compressed).expect("failed to write gz fixture");

    assert_sizes_and_output(&gz_path, &tmp.path, "fa-gz.chrom.sizes");
}

#[test]
fn parses_2bit_and_writes_expected_output() {
    let tmp = TempDir::new();
    let twobit_path = tmp.path.join("sample.2bit");
    let fasta_reader = FastaReader::mem_open(FASTA.as_bytes().to_vec())
        .expect("failed to create FASTA reader for 2bit conversion");
    let mut twobit_file = File::create(&twobit_path).expect("failed to create 2bit fixture file");
    to_2bit(&mut twobit_file, &fasta_reader).expect("failed to create 2bit fixture");
    twobit_file.flush().expect("failed to flush 2bit fixture");

    assert_sizes_and_output(&twobit_path, &tmp.path, "2bit.chrom.sizes");
}