biolic 0.1.0

A modular bioinformatics toolkit in Rust for long-read sequence processing
Documentation
//! `biolic count`: fast read and base counting.
//!
//! Fully implemented. This is the fastest module in biolic — no quality
//! computation, no GC, no allocations beyond the parser internals.

use std::path::PathBuf;

use anyhow::{Context, Result};
use clap::Args;
use serde::Serialize;

use crate::cli::RunContext;
use crate::io::reader::open_input;
use crate::output::{write, HumanDisplay, OutputFormat};

#[derive(Args, Debug)]
pub struct CountArgs {
    /// Input file. Use "-" for stdin.
    pub input: PathBuf,

    /// Output in JSON format.
    #[arg(long, conflicts_with = "tsv")]
    pub json: bool,

    /// Output in TSV format.
    #[arg(long, conflicts_with = "json")]
    pub tsv: bool,
}

#[derive(Debug, Serialize)]
pub struct CountResult {
    pub file: String,
    pub reads: u64,
    pub bases: u64,
}

pub fn run(args: CountArgs, _ctx: &RunContext) -> Result<()> {
    let result = compute(&args)?;

    let format = if args.json {
        OutputFormat::Json
    } else if args.tsv {
        OutputFormat::Tsv
    } else {
        OutputFormat::auto()
    };

    write(&result, format)?;
    Ok(())
}

fn compute(args: &CountArgs) -> Result<CountResult> {
    let mut reader = open_input(&args.input).context("opening input")?;
    let mut reads = 0u64;
    let mut bases = 0u64;

    while let Some(record) = reader.next_record().context("reading record")? {
        reads += 1;
        bases += record.len() as u64;
    }

    Ok(CountResult {
        file: args.input.display().to_string(),
        reads,
        bases,
    })
}

impl HumanDisplay for CountResult {
    fn write_human(&self, w: &mut dyn std::io::Write) -> Result<()> {
        writeln!(
            w,
            "{}\treads={}\tbases={}",
            self.file, self.reads, self.bases
        )?;
        Ok(())
    }

    fn write_tsv(&self, w: &mut dyn std::io::Write) -> Result<()> {
        writeln!(w, "file\treads\tbases")?;
        writeln!(w, "{}\t{}\t{}", self.file, self.reads, self.bases)?;
        Ok(())
    }
}