biolic 0.1.0

A modular bioinformatics toolkit in Rust for long-read sequence processing
Documentation
//! Top-level CLI definition.
//!
//! Each subcommand dispatches to a module in the `modules` directory.
//! When adding a new module:
//! 1. Add it to `src/modules/mod.rs`
//! 2. Add a variant to the `Commands` enum below
//! 3. Add the dispatch in `run()`

use anyhow::Result;
use clap::{Parser, Subcommand};

use crate::modules;

/// biolic: a modular bioinformatics toolkit in Rust.
#[derive(Parser, Debug)]
#[command(
    name = "biolic",
    version,
    author,
    about = "A modular bioinformatics toolkit in Rust",
    long_about = "biolic is a modular, fast, memory-efficient toolkit for processing \
                  sequencing data, with first-class support for long reads (PacBio HiFi \
                  and Oxford Nanopore) and unaligned BAM input. Streaming-first design \
                  guarantees constant memory regardless of input file size."
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,

    /// Disable execution logging for this run.
    #[arg(long, global = true)]
    pub no_log: bool,

    /// Suppress progress output and informational messages.
    #[arg(short, long, global = true)]
    pub quiet: bool,

    /// Enable verbose output (info-level logging to stderr).
    #[arg(short, long, global = true)]
    pub verbose: bool,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Compute summary statistics for sequence files (N50, length distribution, quality).
    Stats(modules::stats::StatsArgs),

    /// Count reads and bases quickly (fastest module in biolic).
    Count(modules::count::CountArgs),

    /// Filter reads by length, quality, GC content, or N percentage.
    Filter(modules::filter::FilterArgs),

    /// Convert between sequence formats (FASTQ, FASTA, BAM).
    Convert(modules::convert::ConvertArgs),

    /// Subsample reads by count, proportion, or target coverage.
    Sample(modules::sample::SampleArgs),

    /// Search for sequence patterns in reads.
    Grep(modules::grep::GrepArgs),

    /// Extract the first N reads or bases.
    Head(modules::head::HeadArgs),

    /// Extract the last N reads or bases.
    Tail(modules::tail::TailArgs),

    /// Adaptive QC with mixture models, anomaly detection, and threshold recommendations.
    Qc(modules::qc::QcArgs),

    /// Query and manage execution logs.
    Logs(modules::logs::LogsArgs),
}

/// Dispatch the parsed CLI to the appropriate module.
pub fn run(cli: Cli) -> Result<()> {
    let context = RunContext {
        no_log: cli.no_log,
        quiet: cli.quiet,
        verbose: cli.verbose,
    };

    match cli.command {
        Commands::Stats(args) => modules::stats::run(args, &context),
        Commands::Count(args) => modules::count::run(args, &context),
        Commands::Filter(args) => modules::filter::run(args, &context),
        Commands::Convert(args) => modules::convert::run(args, &context),
        Commands::Sample(args) => modules::sample::run(args, &context),
        Commands::Grep(args) => modules::grep::run(args, &context),
        Commands::Head(args) => modules::head::run(args, &context),
        Commands::Tail(args) => modules::tail::run(args, &context),
        Commands::Qc(args) => modules::qc::run(args, &context),
        Commands::Logs(args) => modules::logs::run(args, &context),
    }
}

/// Shared runtime context passed to every module.
///
/// Holds the global flags from the top-level CLI.
#[derive(Debug, Clone)]
pub struct RunContext {
    pub no_log: bool,
    pub quiet: bool,
    pub verbose: bool,
}