intspan 0.7.3

Command line tools for IntSpan related bioinformatics operations
Documentation
use clap::*;
use intspan::*;
use std::io::BufRead;

// Create clap subcommand arguments
pub fn make_subcommand() -> Command {
    Command::new("create")
        .about("Create block fasta files from links of ranges")
        .after_help(
            r###"
* <genome.fa> is a multi-fasta file contains genome sequences

* <infiles> are files of links of ranges, usually generated by `linkr`
* infile == stdin means reading from STDIN

* Need `samtools` in $PATH

"###,
        )
        .arg(
            Arg::new("genome.fa")
                .required(true)
                .num_args(1)
                .index(1)
                .help("Path to genome.fa"),
        )
        .arg(
            Arg::new("infiles")
                .required(true)
                .num_args(1..)
                .index(2)
                .help("Sets the input files to use"),
        )
        .arg(
            Arg::new("name")
                .long("name")
                .num_args(1)
                .help("Set a species name for ranges"),
        )
        .arg(
            Arg::new("outfile")
                .long("outfile")
                .short('o')
                .num_args(1)
                .default_value("stdout")
                .help("Output filename. [stdout] for screen"),
        )
}

// command implementation
pub fn execute(args: &ArgMatches) -> anyhow::Result<()> {
    //----------------------------
    // Loading
    //----------------------------
    let mut writer = writer(args.get_one::<String>("outfile").unwrap());
    let genome = args.get_one::<String>("genome.fa").unwrap();

    for infile in args.get_many::<String>("infiles").unwrap() {
        let reader = reader(infile);
        for line in reader.lines().map_while(Result::ok) {
            let parts: Vec<&str> = line.split('\t').collect();

            for part in &parts {
                let mut range = Range::from_str(part);
                if !range.is_valid() {
                    continue;
                }

                let seq = get_seq(&range, genome)?;
                if args.contains_id("name") {
                    let name = args.get_one::<String>("name").unwrap();
                    *range.name_mut() = name.to_string();
                }

                writer.write_all(format!(">{}\n{}\n", range, seq).as_ref())?;
            }

            // end of a block
            writer.write_all("\n".as_ref())?;
        }
    }

    Ok(())
}

fn get_seq(range: &Range, genome: &str) -> anyhow::Result<String> {
    let pos = format!("{}:{}-{}", range.chr(), range.start(), range.end());
    let mut gseq = get_seq_faidx(genome, &pos)?;

    if range.strand() == "-" {
        gseq = std::str::from_utf8(&bio::alphabets::dna::revcomp(gseq.bytes()))
            .unwrap()
            .to_string();
    }

    Ok(gseq)
}