1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Streaming editor for fastq data using a find/replace.
//! 
//! # Examples
//! 
//! # Force a motif to be lowercase
//! ```bash
//! cat file.fastq | fasten_replace --which SEQ --find ATAT --replace atat > file.fastq
//! ```
//! # Mutate the middle base of a kmer
//! ```bash
//! cat file.fastq | fasten_replace --which SEQ --find AAAAA --replace AATAA > file.fastq
//! ```
//! 
//! ```text
//! # Usage
//! 
//! Usage: fasten_replace [-h] [-n INT] [-p] [-v] [-f STRING] [-r STRING] [-w STRING]
//! 
//! Options:
//!     -h, --help          Print this help menu.
//!     -n, --numcpus INT   Number of CPUs (default: 1)
//!     -p, --paired-end    The input reads are interleaved paired-end
//!     -v, --verbose       Print more status messages
//!     -f, --find STRING   Regular expression (default: '.')
//!     -r, --replace STRING
//!                         String to replace each match
//!     -w, --which STRING  Which field to match on? ID, SEQ, QUAL. Default: SEQ
//! ```
extern crate getopts;
extern crate fasten;
extern crate regex;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::env;

use regex::Regex;

use fasten::fasten_base_options;
use fasten::logmsg;

fn main(){
    let args: Vec<String> = env::args().collect();
    let mut opts = fasten_base_options();
    // Options specific to this script
    opts.optopt("f","find","Regular expression (default: '.')","STRING");
    opts.optopt("r","replace","String to replace each match","STRING");
    opts.optopt("w","which","Which field to match on? ID, SEQ, QUAL. Default: SEQ","STRING");

    let matches = opts.parse(&args[1..]).expect("ERROR: could not parse parameters");
    if matches.opt_present("help") {
        println!("Streaming editor for fastq data using a find/replace.\n{}", opts.usage(&opts.short_usage(&args[0])));
        std::process::exit(0);
    }

    if matches.opt_present("paired-end") {
        logmsg("WARNING: --paired-end is not utilized in this script");
    }

    //which field does the user want to find and replace?
    let which_field={
        if matches.opt_present("which") {
            matches.opt_str("which").expect("ERROR parsing --which")
                .to_uppercase()
        } else {
            "SEQ".to_string()
        }
    };

    // Figure out what we are searching for
    let find_param={
        if matches.opt_present("find") {
            matches.opt_str("find")
                .expect("ERROR: could not parse find parameter")
        } else {
            String::from(".")
        }
    };

    // form the regex
    let find_regex = Regex::new(&find_param)
        .expect("malformed seq regex given by --find");

    // Make the replace string
    let replace :String = {
        if matches.opt_present("replace") {
            matches.opt_str("replace")
                .expect("ERROR: could not parse --replace parameter")
        } else {
            String::from("")
        }
    };
    // but it requires a &str
    let replace_str :&str = replace.as_str();

    let my_file = File::open("/dev/stdin").expect("Could not open file");
    let my_buffer=BufReader::new(my_file);
    let mut buffer_iter = my_buffer.lines();
    while let Some(line) = buffer_iter.next() {
        let mut id  = line.expect("ERROR reading the ID line");
        let mut seq = buffer_iter.next().expect("ERROR reading a sequence line")
            .expect("ERROR reading a sequence line");
                  buffer_iter.next().expect("ERROR reading a plus line")
                      .expect("ERROR reading the plus line");
        let mut qual= buffer_iter.next().expect("ERROR reading a qual line")
            .expect("ERROR reading a qual line");

        // Find and replace
        if &which_field == "SEQ" {
            seq  = find_regex.replace_all(&seq, replace_str).into_owned();
        } else if &which_field == "QUAL" {
            qual = find_regex.replace_all(&qual, replace_str).into_owned();
        } else if &which_field == "ID" {
            id   = find_regex.replace_all(&id, replace_str).into_owned();
        } else {
            panic!("Not implemented for --which: {}", which_field);
        }
        println!("{}\n{}\n+\n{}",id,seq,qual);

    }
}