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
//! 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 regex::Regex;

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

fn main(){
    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 = fasten_base_options_matches("Streaming editor for fastq data using a find/replace.", opts);

    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);

    }
}