<commit_before>extern crate std;
extern crate collections;
use std::io::File;
use std::io::BufferedReader;

fn getline(fname : &str, linenum : uint) -> ~str {
    let path = Path::new(fname);
    let mut i = 0;
    let mut file = BufferedReader::new(File::open(&path));
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.to_owned();
        }
    }
    return ~"not found";
}

fn is_path_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!') || (c == ':')
}

fn is_ident_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!')
}

fn expand_ident(s : &str, pos : uint) -> (uint,uint) {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    return (start, pos);
}

fn expand_searchstr(s : &str, pos : uint) -> ~str {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_path_char(c) {
            break;
        }
        start = i;
    }
    return s.slice(start,pos).to_owned();
}

fn find_end(s : &str, pos : uint) -> uint {
    // find end of word
    let sa = s.slice_from(pos);
    let mut end = pos;
    for (i, c) in sa.char_indices() {
        if !is_path_char(c) {
            break;
        }
        end = pos + i + 1;
    }
    return end;
}

fn find_in_module(path : &Path, s : &str, matchfn : &|&str,int,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    //let modsearchstr = "pub mod "+s;
    let modsearchstr = "mod ";
    let fnsearchstr = "fn ";
    let structsearchstr = "struct ";
    let cratesearchstr = "extern crate ";
    let mut i = 0;
    for line in file.lines() {
        i += 1;
        match line.find_str(modsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }
        match line.find_str(fnsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+3);
                let l = line.slice(n + 3, end);
                (*matchfn)(l + "(", i, path, line);
            }
            None => {}
        }
        match line.find_str(structsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+7);
                let l = line.slice(n+7, end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }

        match line.find_str(cratesearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+ cratesearchstr.len());
                let cratename = line.slice(n + cratesearchstr.len(), end);
                (*matchfn)(cratename+"::", i, path, line)
            }
            None => {}
        }

        match line.find_str(s) {
            Some(_) => {
                match line.find_str("pub use ") {
                    Some(n) => {
                        let end = find_end(line, n+8);
                        let modname = line.slice(n+8, end);

                        if modname.starts_with("self::") {
                            let mut l = modname.split_str("::");
                            let c : ~[&str] = l.collect();
                            if c.ends_with([""]) {
                                let mut c2 = c.slice_to(c.len()-1).to_owned();
                                c2.push(s);
                                search_f(path, c2.slice_from(1), matchfn);
                            } else if c[c.len()-1].starts_with(s) {
                                search_f(path, c.slice_from(1), matchfn);
                            }
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_f(path: &Path, p: &[&str], matchfn: &|&str,int,&Path,&str|) {
    debug!("search_f: {} {} ",path.as_str(),p);

    if p.len() == 0 {
        return find_in_module(path, "", matchfn);
    }

    if p.len() == 1 {
        return find_in_module(path, p[0], matchfn);
    }

    let mut file = BufferedReader::new(File::open(path));
    let modsearchstr = "mod ";
    let mut i = 0;
    for line in file.lines() {
        i+=1;
        match line.find_str(modsearchstr + p[0]) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                if p.len() == 1 {
                    (*matchfn)(l, i, path,line);
                } else {
                    debug!("PHIL NOT Found {} {} ",l,line);
                    let dir = path.dir_path();
                    debug!("PHIL DIR {}", dir.as_str().unwrap());
                    // try searching file.rs
                    search_f(&dir.join(l+".rs"), p.tail(), matchfn);
                    // try searching dir/mod.rs
                    search_f(&dir.join_many([l, "mod.rs"]), p.tail(), matchfn)
                }
            }
            None => {}
        }
    }
}

fn search_in_module(path : &Path, p : &[&str], f : &|&str,int,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        match line.find_str("use ") {
            Some(_) => {
                let mut s = line.slice_from(4).trim();

                match s.find_str(p[0]) {
                    Some(_) => {
                        let end = find_end(s, 0);
                        s = s.slice(0, end);
                        let pieces : ~[&str] = s.split_str("::").collect();
                        if p.len() == 1 && pieces[pieces.len()-1].starts_with(p[0]) {
                            search_crate(pieces, f);
                        } else if p.len() > 1 && pieces[pieces.len()-1] == p[0] {
                            let p2 = pieces + p.slice_from(1);
                            search_crate(p2, f);
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_crates(path : &Path, p : &[&str], matchfn : &|&str,int,&Path,&str|) {
    if p[0] == "std" {
        search_crate(p, matchfn);
        return;
    }

    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        let searchstr = "extern crate ";
        match line.find_str(searchstr+p[0]) {
            Some(n) => {
                let end = find_end(line, n+ searchstr.len());
                let cratename = line.slice(n + searchstr.len(), end);
                if p[0] == cratename {
                    search_crate(p, matchfn);
                }
            }
            None => {}
        }
    }
}

fn search_crate(p : &[&str], matchfn : &|&str,int,&Path,&str|) {
    let srcpaths = std::os::getenv("RUST_SRC_PATH").unwrap();
    let cratename = p[0];

    for srcpath in srcpaths.split_str(":") {
        {
            // try lib<cratename>/lib.rs, like in the rust source dir
            let cratelibname = "lib" + cratename;
            let path = Path::new(srcpath).join_many([Path::new(cratelibname),
                                                        Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/<cratename>.rs, like in the servo codebase
            let path = Path::new(srcpath).join_many([Path::new(cratename),
                                                     Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/lib.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename"),
                                                     Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try just <cratename>.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
    }
}

pub fn complete_from_file(fname : &str, linenum: uint, charnum: uint,
                          matchfn : &|&str,int,&Path,&str|) {
    let line = getline(fname, linenum);
    let s = expand_searchstr(line, charnum);

    let mut l = s.split_str("::");
    let c : ~[&str] = l.collect();

    search_crates(&Path::new(fname), c, matchfn);
    search_in_module(&Path::new(fname), c, matchfn);
    search_f(&Path::new(fname), c, matchfn);

    if c.len() == 1 && "std".starts_with(c[0]) {
        (*matchfn)("std::",1,&Path::new(fname),"std::");
    }

}

fn print_usage() {
    let program = std::os::args()[0].clone();
    println!("usage: {} complete linenum charnum fname", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)",program);
    println!("or:    {} prefix linenum charnum fname",program);
}

fn match_fn(l: &str, lineno: int, file:&Path,  linetxt: &str) {
    std::io::println("COMPLETE "+l +
                     "," + lineno.to_str() +
                     "," + file.as_str().unwrap() +
                     "," +  linetxt);
}

fn complete() {
   match std::uint::parse_bytes(std::os::args()[2].as_bytes(), 10) {
        Some(n) => {
            // input: linenum, colnum, fname
            let linenum = n;
            let charnum = std::uint::parse_bytes(std::os::args()[3].as_bytes(), 10).unwrap();
            let fname = std::os::args()[4];

            // print the start-end of the identifier being matched
            let line = getline(fname, linenum);
            let (start, pos) = expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));

            complete_from_file(fname, linenum, charnum,
                  &|l, linenum, file, line| match_fn(l, linenum, file, line));
        }
        None => {
            // input: a command line string passed in
            let arg = std::os::args()[2];
            let mut it = arg.split_str("::");
            let p : ~[&str] = it.collect();
            search_crate(p,
                  &|l, linenum, file, line|  match_fn(l, linenum, file, line));
        }
    }
}

fn prefix() {
    let args = std::os::args().to_owned();
    let linenum = std::uint::parse_bytes(args[2].as_bytes(), 10).unwrap();
    let charnum = std::uint::parse_bytes(args[3].as_bytes(), 10).unwrap();
    let fname = args[4];

    // print the start, end, and the identifier prefix being matched
    let line = getline(fname, linenum);
    let (start, pos) = expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));
}

fn main() {
    match std::os::getenv("RUST_SRC_PATH") {
        Some(_) => {}
        None() => {
            println!("RUST_SRC_PATH environment variable must be set");
            return;
        }
    }

    let args = std::os::args().to_owned();

    if args.len() == 1 {
        print_usage();
        return;
    }

    let command = args[1];
    match  command {
        ~"prefix" => prefix(),
        ~"complete" => complete(),
        ~"help" => print_usage(),
        _ => {
            println!("Sorry, I didn't understand command {}", command );
            print_usage();
            return;
        }
    }

 }

<commit_msg>Don't match the same line as the user is completing on<commit_after>extern crate std;
extern crate collections;
use std::io::File;
use std::io::BufferedReader;

fn getline(fname : &str, linenum : uint) -> ~str {
    let path = Path::new(fname);
    let mut i = 0;
    let mut file = BufferedReader::new(File::open(&path));
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.to_owned();
        }
    }
    return ~"not found";
}

fn is_path_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!') || (c == ':')
}

fn is_ident_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!')
}

fn expand_ident(s : &str, pos : uint) -> (uint,uint) {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    return (start, pos);
}

fn expand_searchstr(s : &str, pos : uint) -> ~str {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_path_char(c) {
            break;
        }
        start = i;
    }
    return s.slice(start,pos).to_owned();
}

fn find_end(s : &str, pos : uint) -> uint {
    // find end of word
    let sa = s.slice_from(pos);
    let mut end = pos;
    for (i, c) in sa.char_indices() {
        if !is_path_char(c) {
            break;
        }
        end = pos + i + 1;
    }
    return end;
}

fn find_in_module(path : &Path, s : &str, matchfn : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    //let modsearchstr = "pub mod "+s;
    let modsearchstr = "mod ";
    let fnsearchstr = "fn ";
    let structsearchstr = "struct ";
    let cratesearchstr = "extern crate ";
    let mut i = 0;
    for line in file.lines() {
        i += 1;
        match line.find_str(modsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }
        match line.find_str(fnsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+3);
                let l = line.slice(n + 3, end);
                (*matchfn)(l + "(", i, path, line);
            }
            None => {}
        }
        match line.find_str(structsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+7);
                let l = line.slice(n+7, end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }

        match line.find_str(cratesearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+ cratesearchstr.len());
                let cratename = line.slice(n + cratesearchstr.len(), end);
                (*matchfn)(cratename+"::", i, path, line)
            }
            None => {}
        }

        match line.find_str(s) {
            Some(_) => {
                match line.find_str("pub use ") {
                    Some(n) => {
                        let end = find_end(line, n+8);
                        let modname = line.slice(n+8, end);

                        if modname.starts_with("self::") {
                            let mut l = modname.split_str("::");
                            let c : ~[&str] = l.collect();
                            if c.ends_with([""]) {
                                let mut c2 = c.slice_to(c.len()-1).to_owned();
                                c2.push(s);
                                search_f(path, c2.slice_from(1), matchfn);
                            } else if c[c.len()-1].starts_with(s) {
                                search_f(path, c.slice_from(1), matchfn);
                            }
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_f(path: &Path, p: &[&str], matchfn: &|&str,uint,&Path,&str|) {
    debug!("search_f: {} {} ",path.as_str(),p);

    if p.len() == 0 {
        return find_in_module(path, "", matchfn);
    }

    if p.len() == 1 {
        return find_in_module(path, p[0], matchfn);
    }

    let mut file = BufferedReader::new(File::open(path));
    let modsearchstr = "mod ";
    let mut i = 0;
    for line in file.lines() {
        i+=1;
        match line.find_str(modsearchstr + p[0]) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                if p.len() == 1 {
                    (*matchfn)(l, i, path,line);
                } else {
                    debug!("PHIL NOT Found {} {} ",l,line);
                    let dir = path.dir_path();
                    debug!("PHIL DIR {}", dir.as_str().unwrap());
                    // try searching file.rs
                    search_f(&dir.join(l+".rs"), p.tail(), matchfn);
                    // try searching dir/mod.rs
                    search_f(&dir.join_many([l, "mod.rs"]), p.tail(), matchfn)
                }
            }
            None => {}
        }
    }
}

fn search_in_module(path : &Path, p : &[&str], f : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        match line.find_str("use ") {
            Some(_) => {
                let mut s = line.slice_from(4).trim();

                match s.find_str(p[0]) {
                    Some(_) => {
                        let end = find_end(s, 0);
                        s = s.slice(0, end);
                        let pieces : ~[&str] = s.split_str("::").collect();
                        if p.len() == 1 && pieces[pieces.len()-1].starts_with(p[0]) {
                            search_crate(pieces, f);
                        } else if p.len() > 1 && pieces[pieces.len()-1] == p[0] {
                            let p2 = pieces + p.slice_from(1);
                            search_crate(p2, f);
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_crates(path : &Path, p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    if p[0] == "std" {
        search_crate(p, matchfn);
        return;
    }

    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        let searchstr = "extern crate ";
        match line.find_str(searchstr+p[0]) {
            Some(n) => {
                let end = find_end(line, n+ searchstr.len());
                let cratename = line.slice(n + searchstr.len(), end);
                if p[0] == cratename {
                    search_crate(p, matchfn);
                }
            }
            None => {}
        }
    }
}

fn search_crate(p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    let srcpaths = std::os::getenv("RUST_SRC_PATH").unwrap();
    let cratename = p[0];

    for srcpath in srcpaths.split_str(":") {
        {
            // try lib<cratename>/lib.rs, like in the rust source dir
            let cratelibname = "lib" + cratename;
            let path = Path::new(srcpath).join_many([Path::new(cratelibname),
                                                        Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/<cratename>.rs, like in the servo codebase
            let path = Path::new(srcpath).join_many([Path::new(cratename),
                                                     Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/lib.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename"),
                                                     Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try just <cratename>.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
    }
}

pub fn complete_from_file(fname : &str, linenum: uint, charnum: uint,
                          matchfn : &|&str,uint,&Path,&str|) {
    let line = getline(fname, linenum);
    let s = expand_searchstr(line, charnum);

    let mut l = s.split_str("::");
    let c : ~[&str] = l.collect();

    search_crates(&Path::new(fname), c, matchfn);
    search_in_module(&Path::new(fname), c, matchfn);
    search_f(&Path::new(fname), c, matchfn);

    if c.len() == 1 && "std".starts_with(c[0]) {
        (*matchfn)("std::",1,&Path::new(fname),"std::");
    }

}

fn print_usage() {
    let program = std::os::args()[0].clone();
    println!("usage: {} complete linenum charnum fname", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)",program);
    println!("or:    {} prefix linenum charnum fname",program);
}

fn match_fn(l: &str, lineno: uint, file:&Path,  linetxt: &str) {
    std::io::println("COMPLETE "+l +
                     "," + lineno.to_str() +
                     "," + file.as_str().unwrap() +
                     "," +  linetxt);
}

fn complete() {
   match std::uint::parse_bytes(std::os::args()[2].as_bytes(), 10) {
        Some(n) => {
            // input: linenum, colnum, fname
            let linenum = n;
            let charnum = std::uint::parse_bytes(std::os::args()[3].as_bytes(), 10).unwrap();
            let fname = std::os::args()[4];

            // print the start-end of the identifier being matched
            let line = getline(fname, linenum);
            let (start, pos) = expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));

            complete_from_file(fname, linenum, charnum,
                  &|l, match_linenum, file, line| {
                     // ignore matches from the same line in the same file
                     if file.as_str().unwrap() == fname && match_linenum == linenum {
                         return;
                     }
                     match_fn(l, match_linenum, file, line);
                  });
        }
        None => {
            // input: a command line string passed in
            let arg = std::os::args()[2];
            let mut it = arg.split_str("::");
            let p : ~[&str] = it.collect();
            search_crate(p,
                  &|l, linenum, file, line|  match_fn(l, linenum, file, line));
        }
    }
}

fn prefix() {
    let args = std::os::args().to_owned();
    let linenum = std::uint::parse_bytes(args[2].as_bytes(), 10).unwrap();
    let charnum = std::uint::parse_bytes(args[3].as_bytes(), 10).unwrap();
    let fname = args[4];

    // print the start, end, and the identifier prefix being matched
    let line = getline(fname, linenum);
    let (start, pos) = expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));
}

fn main() {
    match std::os::getenv("RUST_SRC_PATH") {
        Some(_) => {}
        None() => {
            println!("RUST_SRC_PATH environment variable must be set");
            return;
        }
    }

    let args = std::os::args().to_owned();

    if args.len() == 1 {
        print_usage();
        return;
    }

    let command = args[1];
    match  command {
        ~"prefix" => prefix(),
        ~"complete" => complete(),
        ~"help" => print_usage(),
        _ => {
            println!("Sorry, I didn't understand command {}", command );
            print_usage();
            return;
        }
    }

 }

<|endoftext|>


<commit_before>use std::str::FromStr;
use ::token::parse_expressions;
use ::token::{Expression, Token, Multiplicity};

#[derive(PartialEq,Debug)]
pub struct Regex {
    pub pattern: String
}

impl Regex {
    pub fn from(pattern: &str) -> Regex {
        Regex {
            pattern: pattern.to_owned()
        }
    }

    pub fn first(&self, text: &str) -> Option<(usize, usize)> {
        let expressions = parse_expressions(&self.pattern).unwrap();

        let mut regex_i = 0;
        let mut text_i = 0;
        let mut match_start = 0;

        while regex_i < expressions.len() {
            let mut options = Self::ways_to_grab_text(&text[text_i..], &expressions[regex_i]);

            if options.len() == 0 {
                text_i += 1;
                match_start = text_i;

                if text_i >= text.len() {
                    return None;
                }

                continue;
            }

            let chosen_option = options.pop().unwrap();

            text_i += chosen_option;
            regex_i += 1;
        }

        Some((match_start, text_i))
    }

    fn ways_to_grab_text(text: &str, expr: &Expression) -> Vec<usize> {
        let mut valid_offsets = Vec::<usize>::new();
        let mut valid_chars = Vec::new();
        let mut valid_multiplicity = Multiplicity {
            minimum: Some(1), maximum: Some(1)
        };

        match expr {
            &Expression::Token(ref token, ref multiplicity) => {
                valid_multiplicity = multiplicity.clone();
                match token {
                    &Token::Literal(ref value) => {
                        valid_chars.push(value.clone());
                    },
                    &Token::Class(ref values) => {
                        valid_chars.append(&mut values.clone());
                    }
                    _ => ()
                }
            },
            _ => ()
        }

        if valid_multiplicity.minimum == None || valid_multiplicity.minimum == Some(0) {
            valid_offsets.push(0); //it is an option to do nothing with this expr
        }

        let mut offset = 0;

        for c in text.chars() {
            if !valid_chars.contains(&c) {
                break;
            }

            offset += 1;

            match valid_multiplicity.minimum {
                Some(min) => {
                    if min < offset { continue; }
                },
                None => ()
            }

            match valid_multiplicity.maximum {
                Some(max) => {
                    if max > offset { continue; }
                },
                None => ()
            }

            valid_offsets.push(offset);
        }

        valid_offsets
    }

    pub fn is_match(&self, text: &str) -> bool {
        return self.first(text).is_some();
    }
}

#[cfg(test)]
mod regex_spec {
    use super::Regex;

    #[test]
    fn matches_simple_examples() {
        assert!(Regex::from("ab?c").is_match("zac"));
        assert!(Regex::from("ab?c").is_match("abbbc"));
        assert!(Regex::from("a?").is_match(""));
        assert!(Regex::from("a+").is_match("a"));
        assert!(Regex::from("ab+").is_match("abbbb"));
    }

    #[test]
    fn does_not_match() {
        assert!(!Regex::from("ab?c").is_match("z"));
        assert!(!Regex::from("a+").is_match(""));
        assert!(!Regex::from("ab+").is_match("bbbb"));
    }

    #[test]
    fn matches_character_class() {
        assert!(Regex::from("[abc]").is_match("a"));
        assert!(Regex::from("[abc]").is_match("b"));
        assert!(Regex::from("[abc]").is_match("c"));
        assert!(!Regex::from("[abc]").is_match("["));
        assert!(!Regex::from("[abc]").is_match("]"));
        assert!(!Regex::from("[abc]").is_match("z"));
    }

    #[test]
    fn backtracks_to_find_match() {
        assert!(Regex::from("[bc]?c").is_match("cb"));
        assert!(Regex::from("[bc]?c").is_match("cc"));
        assert!(Regex::from("[bc]?c").is_match("c"));
        assert!(!Regex::from("[bc]?c").is_match("b"));
    }

    #[test]
    fn matches_zero_or_more() {
        let regex = Regex::from("ab*");
        assert!(regex.is_match("abbbb"));
        assert!(regex.is_match("ab"));
        assert!(regex.is_match("a"));
        assert!(!regex.is_match("bb"));
    }
}
<commit_msg>fix for metacharacter greediness<commit_after>use std::str::FromStr;
use ::token::parse_expressions;
use ::token::{Expression, Token, Multiplicity};

#[derive(PartialEq,Debug)]
pub struct Regex {
    pub pattern: String
}

impl Regex {
    pub fn from(pattern: &str) -> Regex {
        Regex {
            pattern: pattern.to_owned()
        }
    }

    pub fn first(&self, text: &str) -> Option<(usize, usize)> {
        let expressions = parse_expressions(&self.pattern).unwrap();

        let mut regex_i = 0;
        let mut text_i = 0;
        let mut match_start = 0;

        while regex_i < expressions.len() {
            let mut options = Self::ways_to_grab_text(&text[text_i..], &expressions[regex_i]);

            if options.len() == 0 {
                text_i += 1;
                match_start = text_i;

                if text_i >= text.len() {
                    return None;
                }

                continue;
            }

            let chosen_option = options.pop().unwrap();

            text_i += chosen_option;
            regex_i += 1;
        }

        Some((match_start, text_i))
    }

    fn ways_to_grab_text(text: &str, expr: &Expression) -> Vec<usize> {
        let mut valid_offsets = Vec::<usize>::new();
        let mut valid_chars = Vec::new();
        let mut valid_multiplicity = Multiplicity {
            minimum: Some(1), maximum: Some(1)
        };

        match expr {
            &Expression::Token(ref token, ref multiplicity) => {
                valid_multiplicity = multiplicity.clone();
                match token {
                    &Token::Literal(ref value) => {
                        valid_chars.push(value.clone());
                    },
                    &Token::Class(ref values) => {
                        valid_chars.append(&mut values.clone());
                    }
                    _ => ()
                }
            },
            _ => ()
        }

        if valid_multiplicity.minimum == None || valid_multiplicity.minimum == Some(0) {
            valid_offsets.push(0); //it is an option to do nothing with this expr
        }

        let mut offset = 0;

        for c in text.chars() {
            if !valid_chars.contains(&c) {
                break;
            }

            offset += 1;

            match valid_multiplicity.minimum {
                Some(min) => {
                    if offset < min { continue; }
                },
                None => ()
            }

            match valid_multiplicity.maximum {
                Some(max) => {
                    if offset > max { continue; }
                },
                None => ()
            }

            valid_offsets.push(offset);
        }

        valid_offsets
    }

    pub fn is_match(&self, text: &str) -> bool {
        return self.first(text).is_some();
    }
}

#[cfg(test)]
mod match_spec {
    use super::Regex;

    #[test]
    fn matches_simple_examples() {
        assert!(Regex::from("ab?c").is_match("zac"));
        assert!(Regex::from("ab?c").is_match("abbbc"));
        assert!(Regex::from("a?").is_match(""));
        assert!(Regex::from("a+").is_match("a"));
        assert!(Regex::from("ab+").is_match("abbbb"));
    }

    #[test]
    fn does_not_match() {
        assert!(!Regex::from("ab?c").is_match("z"));
        assert!(!Regex::from("a+").is_match(""));
        assert!(!Regex::from("ab+").is_match("bbbb"));
    }

    #[test]
    fn matches_character_class() {
        assert!(Regex::from("[abc]").is_match("a"));
        assert!(Regex::from("[abc]").is_match("b"));
        assert!(Regex::from("[abc]").is_match("c"));
        assert!(!Regex::from("[abc]").is_match("["));
        assert!(!Regex::from("[abc]").is_match("]"));
        assert!(!Regex::from("[abc]").is_match("z"));
    }

    #[test]
    fn backtracks_to_find_match() {
        assert!(Regex::from("[bc]?c").is_match("cb"));
        assert!(Regex::from("[bc]?c").is_match("cc"));
        assert!(Regex::from("[bc]?c").is_match("c"));
        assert!(!Regex::from("[bc]?c").is_match("b"));
    }

    #[test]
    fn finds_correct_match() {
        let regex = Regex::from("[bc]?c");
        assert_eq!(Some((0,1)),
                   regex.first("cb"));
        assert_eq!(Some((0,2)),
                   regex.first("cc"));
        assert_eq!(Some((0,1)),
                   regex.first("c"));
    }

    #[test]
    fn matches_zero_or_more() {
        let regex = Regex::from("ab*");
        assert!(regex.is_match("abbbb"));
        assert!(regex.is_match("ab"));
        assert!(regex.is_match("a"));
        assert!(!regex.is_match("bb"));
    }

    #[test]
    fn finds_correct_match_position() {
        let regex = Regex::from("a");
        assert_eq!(Some((0,1)), regex.first("a"));
        assert_eq!(Some((3,4)), regex.first("bbba"));
        assert_eq!(Some((0,4)), Regex::from("aaaa").first("aaaa"));
    }

    #[test]
    fn optional_metachar_is_greedy() {
        assert_eq!(Some((0,1)), Regex::from("a?").first("aa"));
    }

    #[test]
    fn one_or_more_metachar_is_greedy() {
        assert_eq!(Some((0,2)), Regex::from("a+").first("aa"));
    }

    #[test]
    fn zero_or_more_metachar_is_greedy() {
        assert_eq!(Some((0,2)), Regex::from("a*").first("aa"));
    }
}

<|endoftext|>


<commit_before>use approxeq::ApproxEq;
use num_traits::Float;
use trig::Trig;
use {Rotation3D, Transform3D, Vector3D, UnknownUnit};

/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
    pub rotation: Rotation3D<T, Src, Dst>,
    pub translation: Vector3D<T, Dst>,
}

// All matrix multiplication in this file is in row-vector notation,
// i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
// before `T2` you use `T1 * T2`

impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct a new rigid transformation, where the `rotation` applies first
    #[inline]
    pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
        Self {
            rotation,
            translation,
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct an identity transform
    #[inline]
    pub fn identity() -> Self {
        Self {
            rotation: Rotation3D::identity(),
            translation: Vector3D::zero(),
        }
    }

    /// Construct a new rigid transformation, where the `translation` applies first
    #[inline]
    pub fn new_from_reversed(
        translation: Vector3D<T, Src>,
        rotation: Rotation3D<T, Src, Dst>,
    ) -> Self {
        // T * R
        //   = (R * R^-1) * T * R
        //   = R * (R^-1 * T * R)
        //   = R * T'
        //
        // T' = (R^-1 * T * R) is also a translation matrix
        // It is equivalent to the translation matrix obtained by rotating the
        // translation by R

        let translation = rotation.transform_vector3d(translation);
        Self {
            rotation,
            translation,
        }
    }

    #[inline]
    pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
        Self {
            rotation,
            translation: Vector3D::zero(),
        }
    }

    #[inline]
    pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
        Self {
            translation,
            rotation: Rotation3D::identity(),
        }
    }

    /// Decompose this into a translation and an rotation to be applied in the opposite order
    ///
    /// i.e., the translation is applied _first_
    #[inline]
    pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
        // self = R * T
        //      = R * T * (R^-1 * R)
        //      = (R * T * R^-1) * R)
        //      = T' * R
        //
        // T' = (R^ * T * R^-1) is T rotated by R^-1

        let translation = self.rotation.inverse().transform_vector3d(self.translation);
        (translation, self.rotation)
    }

    /// Returns the multiplication of the two transforms such that
    /// other's transformation applies after self's transformation.
    ///
    /// i.e., this produces `self * other` in row-vector notation
    #[inline]
    pub fn post_transform<Dst2>(
        &self,
        other: &RigidTransform3D<T, Dst, Dst2>,
    ) -> RigidTransform3D<T, Src, Dst2> {
        // self = R1 * T1
        // other = R2 * T2
        // result = R1 * T1 * R2 * T2
        //        = R1 * (R2 * R2^-1) * T1 * R2 * T2
        //        = (R1 * R2) * (R2^-1 * T1 * R2) * T2
        //        = R' * T' * T2
        //        = R' * T''
        //
        // (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
        // R1 * R2  = R'
        // T' * T2 = T'' = vector addition of translations T2 and T'

        let t_prime = other
            .rotation
            .transform_vector3d(self.translation);
        let r_prime = self.rotation.post_rotate(&other.rotation);
        let t_prime2 = t_prime + other.translation;
        RigidTransform3D {
            rotation: r_prime,
            translation: t_prime2,
        }
    }

    /// Returns the multiplication of the two transforms such that
    /// self's transformation applies after other's transformation.
    ///
    /// i.e., this produces `other * self` in row-vector notation
    #[inline]
    pub fn pre_transform<Src2>(
        &self,
        other: &RigidTransform3D<T, Src2, Src>,
    ) -> RigidTransform3D<T, Src2, Dst> {
        other.post_transform(&self)
    }

    /// Inverts the transformation
    #[inline]
    pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
        // result = (self)^-1
        //        = (R * T)^-1
        //        = T^-1 * R^-1
        //        = (R^-1 * R) * T^-1 * R^-1
        //        = R^-1 * (R * T^-1 * R^-1)
        //        = R' * T'
        //
        // T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
        // R' = R^-1
        //
        // An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1

        RigidTransform3D::new_from_reversed(
            -self.translation,
            self.rotation.inverse(),
        )
    }

    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
    where
        T: Trig,
    {
        self.translation
            .to_transform()
            .pre_transform(&self.rotation.to_transform())
    }

    /// Drop the units, preserving only the numeric value.
    #[inline]
    pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
        RigidTransform3D {
            rotation: self.rotation.to_untyped(),
            translation: self.translation.to_untyped(),
        }
    }

    /// Tag a unitless value with units.
    #[inline]
    pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
        RigidTransform3D {
            rotation: Rotation3D::from_untyped(&transform.rotation),
            translation: Vector3D::from_untyped(transform.translation),
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
        Self::from_rotation(rot)
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(t: Vector3D<T, Dst>) -> Self {
        Self::from_translation(t)
    }
}

#[cfg(test)]
mod test {
    use super::RigidTransform3D;
    use default::{Rotation3D, Transform3D, Vector3D};

    #[test]
    fn test_rigid_construction() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        assert!(rigid
            .to_transform()
            .approx_eq(&translation.to_transform().pre_transform(&rotation.to_transform())));

        let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
        assert!(rigid.to_transform().approx_eq(
            &translation
                .to_transform()
                .post_transform(&rotation.to_transform())
        ));
    }

    #[test]
    fn test_rigid_decomposition() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let (t2, r2) = rigid.decompose_reversed();
        assert!(rigid
            .to_transform()
            .approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
    }

    #[test]
    fn test_rigid_inverse() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let inverse = rigid.inverse();
        assert!(rigid
            .post_transform(&inverse)
            .to_transform()
            .approx_eq(&Transform3D::identity()));
        assert!(inverse
            .to_transform()
            .approx_eq(&rigid.to_transform().inverse().unwrap()));
    }

    #[test]
    fn test_rigid_multiply() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
        let translation2 = Vector3D::new(9.3, -3.9, 1.1);
        let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
        let rigid = RigidTransform3D::new(rotation, translation);
        let rigid2 = RigidTransform3D::new(rotation2, translation2);

        assert!(rigid
            .post_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
        assert!(rigid
            .pre_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
    }
}
<commit_msg>Convert ordinal comment to doc-comment<commit_after>//! All matrix multiplication in this module is in row-vector notation,
//! i.e. a vector `v` is transformed with `v * T`, and if you want to apply `T1`
//! before `T2` you use `T1 * T2`

use approxeq::ApproxEq;
use num_traits::Float;
use trig::Trig;
use {Rotation3D, Transform3D, Vector3D, UnknownUnit};

/// A rigid transformation. All lengths are preserved under such a transformation.
///
///
/// Internally, this is a rotation and a translation, with the rotation
/// applied first (i.e. `Rotation * Translation`, in row-vector notation)
///
/// This can be more efficient to use over full matrices, especially if you
/// have to deal with the decomposed quantities often.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct RigidTransform3D<T, Src, Dst> {
    pub rotation: Rotation3D<T, Src, Dst>,
    pub translation: Vector3D<T, Dst>,
}

impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct a new rigid transformation, where the `rotation` applies first
    #[inline]
    pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
        Self {
            rotation,
            translation,
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
    /// Construct an identity transform
    #[inline]
    pub fn identity() -> Self {
        Self {
            rotation: Rotation3D::identity(),
            translation: Vector3D::zero(),
        }
    }

    /// Construct a new rigid transformation, where the `translation` applies first
    #[inline]
    pub fn new_from_reversed(
        translation: Vector3D<T, Src>,
        rotation: Rotation3D<T, Src, Dst>,
    ) -> Self {
        // T * R
        //   = (R * R^-1) * T * R
        //   = R * (R^-1 * T * R)
        //   = R * T'
        //
        // T' = (R^-1 * T * R) is also a translation matrix
        // It is equivalent to the translation matrix obtained by rotating the
        // translation by R

        let translation = rotation.transform_vector3d(translation);
        Self {
            rotation,
            translation,
        }
    }

    #[inline]
    pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
        Self {
            rotation,
            translation: Vector3D::zero(),
        }
    }

    #[inline]
    pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
        Self {
            translation,
            rotation: Rotation3D::identity(),
        }
    }

    /// Decompose this into a translation and an rotation to be applied in the opposite order
    ///
    /// i.e., the translation is applied _first_
    #[inline]
    pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
        // self = R * T
        //      = R * T * (R^-1 * R)
        //      = (R * T * R^-1) * R)
        //      = T' * R
        //
        // T' = (R^ * T * R^-1) is T rotated by R^-1

        let translation = self.rotation.inverse().transform_vector3d(self.translation);
        (translation, self.rotation)
    }

    /// Returns the multiplication of the two transforms such that
    /// other's transformation applies after self's transformation.
    ///
    /// i.e., this produces `self * other` in row-vector notation
    #[inline]
    pub fn post_transform<Dst2>(
        &self,
        other: &RigidTransform3D<T, Dst, Dst2>,
    ) -> RigidTransform3D<T, Src, Dst2> {
        // self = R1 * T1
        // other = R2 * T2
        // result = R1 * T1 * R2 * T2
        //        = R1 * (R2 * R2^-1) * T1 * R2 * T2
        //        = (R1 * R2) * (R2^-1 * T1 * R2) * T2
        //        = R' * T' * T2
        //        = R' * T''
        //
        // (R2^-1 * T2 * R2^) = T' = T2 rotated by R2
        // R1 * R2  = R'
        // T' * T2 = T'' = vector addition of translations T2 and T'

        let t_prime = other
            .rotation
            .transform_vector3d(self.translation);
        let r_prime = self.rotation.post_rotate(&other.rotation);
        let t_prime2 = t_prime + other.translation;
        RigidTransform3D {
            rotation: r_prime,
            translation: t_prime2,
        }
    }

    /// Returns the multiplication of the two transforms such that
    /// self's transformation applies after other's transformation.
    ///
    /// i.e., this produces `other * self` in row-vector notation
    #[inline]
    pub fn pre_transform<Src2>(
        &self,
        other: &RigidTransform3D<T, Src2, Src>,
    ) -> RigidTransform3D<T, Src2, Dst> {
        other.post_transform(&self)
    }

    /// Inverts the transformation
    #[inline]
    pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
        // result = (self)^-1
        //        = (R * T)^-1
        //        = T^-1 * R^-1
        //        = (R^-1 * R) * T^-1 * R^-1
        //        = R^-1 * (R * T^-1 * R^-1)
        //        = R' * T'
        //
        // T' = (R * T^-1 * R^-1) = (-T) rotated by R^-1
        // R' = R^-1
        //
        // An easier way of writing this is to use new_from_reversed() with R^-1 and T^-1

        RigidTransform3D::new_from_reversed(
            -self.translation,
            self.rotation.inverse(),
        )
    }

    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
    where
        T: Trig,
    {
        self.translation
            .to_transform()
            .pre_transform(&self.rotation.to_transform())
    }

    /// Drop the units, preserving only the numeric value.
    #[inline]
    pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
        RigidTransform3D {
            rotation: self.rotation.to_untyped(),
            translation: self.translation.to_untyped(),
        }
    }

    /// Tag a unitless value with units.
    #[inline]
    pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
        RigidTransform3D {
            rotation: Rotation3D::from_untyped(&transform.rotation),
            translation: Vector3D::from_untyped(transform.translation),
        }
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
        Self::from_rotation(rot)
    }
}

impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>>
    for RigidTransform3D<T, Src, Dst>
{
    fn from(t: Vector3D<T, Dst>) -> Self {
        Self::from_translation(t)
    }
}

#[cfg(test)]
mod test {
    use super::RigidTransform3D;
    use default::{Rotation3D, Transform3D, Vector3D};

    #[test]
    fn test_rigid_construction() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        assert!(rigid
            .to_transform()
            .approx_eq(&translation.to_transform().pre_transform(&rotation.to_transform())));

        let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
        assert!(rigid.to_transform().approx_eq(
            &translation
                .to_transform()
                .post_transform(&rotation.to_transform())
        ));
    }

    #[test]
    fn test_rigid_decomposition() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let (t2, r2) = rigid.decompose_reversed();
        assert!(rigid
            .to_transform()
            .approx_eq(&t2.to_transform().post_transform(&r2.to_transform())));
    }

    #[test]
    fn test_rigid_inverse() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);

        let rigid = RigidTransform3D::new(rotation, translation);
        let inverse = rigid.inverse();
        assert!(rigid
            .post_transform(&inverse)
            .to_transform()
            .approx_eq(&Transform3D::identity()));
        assert!(inverse
            .to_transform()
            .approx_eq(&rigid.to_transform().inverse().unwrap()));
    }

    #[test]
    fn test_rigid_multiply() {
        let translation = Vector3D::new(12.1, 17.8, -5.5);
        let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
        let translation2 = Vector3D::new(9.3, -3.9, 1.1);
        let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
        let rigid = RigidTransform3D::new(rotation, translation);
        let rigid2 = RigidTransform3D::new(rotation2, translation2);

        assert!(rigid
            .post_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().post_transform(&rigid2.to_transform())));
        assert!(rigid
            .pre_transform(&rigid2)
            .to_transform()
            .approx_eq(&rigid.to_transform().pre_transform(&rigid2.to_transform())));
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufRead, Write};
use super::encode_to_url;

#[derive(Debug)]
pub struct Converter {
    output_state: State,
    blank_line_count: usize,
    buffered_code: String,
    meta_note: Option<String>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State { MarkdownFirstLine, MarkdownLines, Rust, }
impl Converter {
    pub fn new() -> Converter {
        Converter { output_state: State::MarkdownFirstLine,
                    blank_line_count: 0,
                    buffered_code: String::new(),
                    meta_note: None, }
    }
}

#[derive(Debug)]
enum Effect<'a> {
    BlankLn,
    WriteLn(&'a str),
    StartCodeBlock,
    FinisCodeBlock,
    BlankLitComment,
}

#[derive(Debug)]
enum EffectContext<'a> {
    Finalize,
    NonblankLine(&'a str),
    Transition(State),
}

impl Converter {
    pub fn convert<R:io::Read, W:io::Write>(&mut self, r:R, mut w:W) -> io::Result<()> {
        let source = io::BufReader::new(r);
        for line in source.lines() {
            let line = (line)?;
            (self.handle(&line, &mut w))?;
        }
        self.finalize(&mut w)
    }

    pub fn finalize(&mut self, w: &mut dyn Write) -> io::Result<()> {
        match self.output_state {
            State::Rust =>
                self.effect(EffectContext::Finalize, Effect::FinisCodeBlock, w),
            State::MarkdownFirstLine |
            State::MarkdownLines =>
                Ok(())
        }
    }

    pub fn handle(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
        let line_right = line.trim_left();
        if line_right.is_empty() {
            self.blank_line(w)
        } else if line_right.starts_with("//@ ") {
            let line = &line_right[4..];
            if line.trim().is_empty() {
                (self.blank_line(w))?
            }
            match self.output_state {
                State::Rust =>
                    (self.transition(w, State::MarkdownFirstLine))?,
                State::MarkdownFirstLine =>
                    (self.transition(w, State::MarkdownLines))?,
                State::MarkdownLines =>
                    {}
            }
            if line.trim().is_empty() {
                Ok(())
            } else {
                self.nonblank_line(line, w)
            }
        } else if line_right.starts_with("//@@@") {
            let line = &line_right[5..];
            if !line.trim().is_empty() {
                match self.output_state {
                    State::Rust => {
                        (self.transition(w, State::MarkdownFirstLine))?;
                        (self.emit_named_code(line.trim(), w))?;
                    }
                    State::MarkdownFirstLine => {
                        (self.transition(w, State::MarkdownLines))?;
                        (self.emit_named_code(line.trim(), w))?;
                    }
                    State::MarkdownLines => {
                        (self.emit_named_code(line.trim(), w))?;
                    }
                }
            }
            Ok(())
        } else if line_right.starts_with("//@@") {
            let line = &line_right[4..];
            if !line.trim().is_empty() {
                self.set_meta_note(line.trim());
            }
            Ok(())
        } else if line_right.starts_with("//@") {
            let line = &line_right[3..];
            match self.output_state {
                State::Rust =>
                    (self.transition(w, State::MarkdownFirstLine))?,
                State::MarkdownFirstLine =>
                    (self.transition(w, State::MarkdownLines))?,
                State::MarkdownLines =>
                {}
            }
            if line.trim().is_empty() {
                self.blank_line(w)
            } else {
                self.nonblank_line(line, w)
            }
        } else {
            match self.output_state {
                State::MarkdownFirstLine |
                State::MarkdownLines =>
                    (self.transition(w, State::Rust))?,
                _ => {}
            }
            self.nonblank_line(line, w)
        }
    }

    fn emit_named_code(&mut self, name: &str, w: &mut dyn Write) -> io::Result<()> {
        writeln!(w, "[{}]: {}", name, encode_to_url(&self.buffered_code))
    }

    fn set_meta_note(&mut self, note: &str) {
        if let Some(ref prev_note) = self.meta_note {
            println!("warning: discarding meta note {} for {}", prev_note, note);
        }
        self.meta_note = Some(note.to_string());
    }

    fn effect(&mut self, _c: EffectContext, e: Effect, w: &mut dyn Write) -> io::Result<()> {
        // println!("effect _c: {:?} e: {:?}", _c, e);
        match e {
            Effect::BlankLn => writeln!(w, ""),
            Effect::WriteLn(line) => writeln!(w, "{}", line),
            Effect::StartCodeBlock => {
                if let Some(ref note) = self.meta_note {
                    assert_eq!(note.chars().next(), Some('{'));
                    (writeln!(w, "```{{.rust{}", &note[1..]))?;
                } else {
                    (writeln!(w, "```rust"))?;
                }
                self.meta_note = None;
                self.buffered_code = String::new();
                Ok(())
            }
            Effect::FinisCodeBlock => {
                (writeln!(w, "```"))?;
                Ok(())
            }
            Effect::BlankLitComment => writeln!(w, ""),
        }
    }

    fn nonblank_line(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
        for _ in 0..self.blank_line_count {
            (self.effect(EffectContext::NonblankLine(line), Effect::BlankLn, w))?;
        }
        if State::Rust == self.output_state {
            self.buffered_code = format!("{}\n{}", self.buffered_code, line);
        }
        self.blank_line_count = 0;
        self.effect(EffectContext::NonblankLine(line), Effect::WriteLn(line), w)
    }

    fn blank_line(&mut self, _w: &mut dyn Write) -> io::Result<()> {
        self.blank_line_count += 1;
        if State::Rust == self.output_state {
            self.buffered_code = format!("{}\n", self.buffered_code);
        }
        Ok(())
    }

    fn finish_section(&mut self, _w: &mut dyn Write) -> io::Result<()> {
        Ok(())
    }

    fn transition(&mut self, w: &mut dyn Write, s: State) -> io::Result<()> {
        match s {
            State::MarkdownFirstLine => {
                assert_eq!(self.output_state, State::Rust);
                (self.effect(EffectContext::Transition(s), Effect::FinisCodeBlock, w))?;
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLn, w))?;
                }
                self.blank_line_count = 0;
            }
            State::MarkdownLines => {
                assert_eq!(self.output_state, State::MarkdownFirstLine);
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLitComment, w))?;
                }
                self.blank_line_count = 0;
            }
            State::Rust => {
                assert!(self.output_state != State::Rust);
                (self.finish_section(w))?;
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLn, w))?;
                }
                self.blank_line_count = 0;
                (self.effect(EffectContext::Transition(s), Effect::StartCodeBlock, w))?;
            }
        }
        self.output_state = s;
        Ok(())
    }
}
<commit_msg>removed use of deprecated `trim_left` method, which has been replaced by `trim_start` to make clearer its behavior with respect to RTL text.<commit_after>use std::io::{self, BufRead, Write};
use super::encode_to_url;

#[derive(Debug)]
pub struct Converter {
    output_state: State,
    blank_line_count: usize,
    buffered_code: String,
    meta_note: Option<String>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State { MarkdownFirstLine, MarkdownLines, Rust, }
impl Converter {
    pub fn new() -> Converter {
        Converter { output_state: State::MarkdownFirstLine,
                    blank_line_count: 0,
                    buffered_code: String::new(),
                    meta_note: None, }
    }
}

#[derive(Debug)]
enum Effect<'a> {
    BlankLn,
    WriteLn(&'a str),
    StartCodeBlock,
    FinisCodeBlock,
    BlankLitComment,
}

#[derive(Debug)]
enum EffectContext<'a> {
    Finalize,
    NonblankLine(&'a str),
    Transition(State),
}

impl Converter {
    pub fn convert<R:io::Read, W:io::Write>(&mut self, r:R, mut w:W) -> io::Result<()> {
        let source = io::BufReader::new(r);
        for line in source.lines() {
            let line = (line)?;
            (self.handle(&line, &mut w))?;
        }
        self.finalize(&mut w)
    }

    pub fn finalize(&mut self, w: &mut dyn Write) -> io::Result<()> {
        match self.output_state {
            State::Rust =>
                self.effect(EffectContext::Finalize, Effect::FinisCodeBlock, w),
            State::MarkdownFirstLine |
            State::MarkdownLines =>
                Ok(())
        }
    }

    pub fn handle(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
        let line_right = line.trim_start();
        if line_right.is_empty() {
            self.blank_line(w)
        } else if line_right.starts_with("//@ ") {
            let line = &line_right[4..];
            if line.trim().is_empty() {
                (self.blank_line(w))?
            }
            match self.output_state {
                State::Rust =>
                    (self.transition(w, State::MarkdownFirstLine))?,
                State::MarkdownFirstLine =>
                    (self.transition(w, State::MarkdownLines))?,
                State::MarkdownLines =>
                    {}
            }
            if line.trim().is_empty() {
                Ok(())
            } else {
                self.nonblank_line(line, w)
            }
        } else if line_right.starts_with("//@@@") {
            let line = &line_right[5..];
            if !line.trim().is_empty() {
                match self.output_state {
                    State::Rust => {
                        (self.transition(w, State::MarkdownFirstLine))?;
                        (self.emit_named_code(line.trim(), w))?;
                    }
                    State::MarkdownFirstLine => {
                        (self.transition(w, State::MarkdownLines))?;
                        (self.emit_named_code(line.trim(), w))?;
                    }
                    State::MarkdownLines => {
                        (self.emit_named_code(line.trim(), w))?;
                    }
                }
            }
            Ok(())
        } else if line_right.starts_with("//@@") {
            let line = &line_right[4..];
            if !line.trim().is_empty() {
                self.set_meta_note(line.trim());
            }
            Ok(())
        } else if line_right.starts_with("//@") {
            let line = &line_right[3..];
            match self.output_state {
                State::Rust =>
                    (self.transition(w, State::MarkdownFirstLine))?,
                State::MarkdownFirstLine =>
                    (self.transition(w, State::MarkdownLines))?,
                State::MarkdownLines =>
                {}
            }
            if line.trim().is_empty() {
                self.blank_line(w)
            } else {
                self.nonblank_line(line, w)
            }
        } else {
            match self.output_state {
                State::MarkdownFirstLine |
                State::MarkdownLines =>
                    (self.transition(w, State::Rust))?,
                _ => {}
            }
            self.nonblank_line(line, w)
        }
    }

    fn emit_named_code(&mut self, name: &str, w: &mut dyn Write) -> io::Result<()> {
        writeln!(w, "[{}]: {}", name, encode_to_url(&self.buffered_code))
    }

    fn set_meta_note(&mut self, note: &str) {
        if let Some(ref prev_note) = self.meta_note {
            println!("warning: discarding meta note {} for {}", prev_note, note);
        }
        self.meta_note = Some(note.to_string());
    }

    fn effect(&mut self, _c: EffectContext, e: Effect, w: &mut dyn Write) -> io::Result<()> {
        // println!("effect _c: {:?} e: {:?}", _c, e);
        match e {
            Effect::BlankLn => writeln!(w, ""),
            Effect::WriteLn(line) => writeln!(w, "{}", line),
            Effect::StartCodeBlock => {
                if let Some(ref note) = self.meta_note {
                    assert_eq!(note.chars().next(), Some('{'));
                    (writeln!(w, "```{{.rust{}", &note[1..]))?;
                } else {
                    (writeln!(w, "```rust"))?;
                }
                self.meta_note = None;
                self.buffered_code = String::new();
                Ok(())
            }
            Effect::FinisCodeBlock => {
                (writeln!(w, "```"))?;
                Ok(())
            }
            Effect::BlankLitComment => writeln!(w, ""),
        }
    }

    fn nonblank_line(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
        for _ in 0..self.blank_line_count {
            (self.effect(EffectContext::NonblankLine(line), Effect::BlankLn, w))?;
        }
        if State::Rust == self.output_state {
            self.buffered_code = format!("{}\n{}", self.buffered_code, line);
        }
        self.blank_line_count = 0;
        self.effect(EffectContext::NonblankLine(line), Effect::WriteLn(line), w)
    }

    fn blank_line(&mut self, _w: &mut dyn Write) -> io::Result<()> {
        self.blank_line_count += 1;
        if State::Rust == self.output_state {
            self.buffered_code = format!("{}\n", self.buffered_code);
        }
        Ok(())
    }

    fn finish_section(&mut self, _w: &mut dyn Write) -> io::Result<()> {
        Ok(())
    }

    fn transition(&mut self, w: &mut dyn Write, s: State) -> io::Result<()> {
        match s {
            State::MarkdownFirstLine => {
                assert_eq!(self.output_state, State::Rust);
                (self.effect(EffectContext::Transition(s), Effect::FinisCodeBlock, w))?;
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLn, w))?;
                }
                self.blank_line_count = 0;
            }
            State::MarkdownLines => {
                assert_eq!(self.output_state, State::MarkdownFirstLine);
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLitComment, w))?;
                }
                self.blank_line_count = 0;
            }
            State::Rust => {
                assert!(self.output_state != State::Rust);
                (self.finish_section(w))?;
                for _ in 0..self.blank_line_count {
                    (self.effect(EffectContext::Transition(s), Effect::BlankLn, w))?;
                }
                self.blank_line_count = 0;
                (self.effect(EffectContext::Transition(s), Effect::StartCodeBlock, w))?;
            }
        }
        self.output_state = s;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;

use model::Model;
use resource::ResourceManager;

pub struct Scene<'a> {
  /// Resource manager.
  res_manager: ResourceManager,
  /// List of all models used in the scene.
  models: Vec<Model<'a>>,
  /// Model cache used to resolve Id based on instance name.
  model_cache: HashMap<String, Id>,
  /// Scene model instances.
  model_instances: Vec<Id>
}

impl<'a> Scene<'a> {
  pub fn new<P>(root: P) -> Self where P: AsRef<Path>{
    Scene {
      res_manager: ResourceManager::new(root),
      models: Vec::new(),
      model_cache: HashMap::new(),
      model_instances: Vec::new()
    }
  }

  pub fn get_id<T>(&mut self, inst_name: &str) -> Option<Id> where T: GetId {
    T::get_id(self, inst_name)
  }

  pub fn resource_manager(&mut self) -> &mut ResourceManager {
    &mut self.res_manager
  }
}

pub type Id = u32;

pub trait GetId {
  fn get_id<'a>(scene: &mut Scene<'a>, inst_name: &str) -> Option<Id>;
}

impl<'b> GetId for Model<'b> {
  fn get_id<'a>(scene: &mut Scene<'a>, inst_name: &str) -> Option<Id> {
    match scene.model_cache.get(inst_name).cloned() {
      id@Some(..) => id,
      None => {
        // cache miss; load then
        let path_str = format!("data/models/{}.obj", inst_name);
        let path = Path::new(&path_str);

        if path.exists() {
          match Model::load(&mut scene.res_manager, path) {
            Ok(model) => {
              let model_id = scene.models.len() as u32;

              // add the model to the list of loaded models
              scene.models.push(model);
              // add the model to the cache
              scene.model_cache.insert(inst_name.to_owned(), model_id);

              Some(model_id)
            },
            Err(e) => {
              err!("unable to load model '{}': '{:?}'", inst_name, e);
              None
            }
          }
        } else { // TODO: add a manifest override to look in somewhere else
          err!("model '{}' cannot be found", inst_name);
          None
        }
      }
    }
  }
}
<commit_msg>Added Object. Removed model_instances.<commit_after>use std::collections::HashMap;
use std::path::Path;

use model::Model;
use resource::ResourceManager;
use transform::Transform;

pub type Id = u32;

pub struct Scene<'a> {
  /// Resource manager.
  res_manager: ResourceManager,
  /// List of all models used in the scene.
  models: Vec<Model<'a>>,
  /// Model cache used to resolve Id based on instance name.
  model_cache: HashMap<String, Id>,
}

pub struct Object {
  model_id: Id,
  transform: Transform
}

impl<'a> Scene<'a> {
  pub fn new<P>(root: P) -> Self where P: AsRef<Path>{
    Scene {
      res_manager: ResourceManager::new(root),
      models: Vec::new(),
      model_cache: HashMap::new(),
    }
  }

  pub fn get_id<T>(&mut self, inst_name: &str) -> Option<Id> where T: GetId {
    T::get_id(self, inst_name)
  }

  pub fn resource_manager(&mut self) -> &mut ResourceManager {
    &mut self.res_manager
  }
}

pub trait GetId {
  fn get_id<'a>(scene: &mut Scene<'a>, inst_name: &str) -> Option<Id>;
}

impl<'b> GetId for Model<'b> {
  fn get_id<'a>(scene: &mut Scene<'a>, inst_name: &str) -> Option<Id> {
    match scene.model_cache.get(inst_name).cloned() {
      id@Some(..) => id,
      None => {
        // cache miss; load then
        let path_str = format!("data/models/{}.obj", inst_name);
        let path = Path::new(&path_str);

        if path.exists() {
          match Model::load(&mut scene.res_manager, path) {
            Ok(model) => {
              let model_id = scene.models.len() as u32;

              // add the model to the list of loaded models
              scene.models.push(model);
              // add the model to the cache
              scene.model_cache.insert(inst_name.to_owned(), model_id);

              Some(model_id)
            },
            Err(e) => {
              err!("unable to load model '{}': '{:?}'", inst_name, e);
              None
            }
          }
        } else { // TODO: add a manifest override to look in somewhere else
          err!("model '{}' cannot be found", inst_name);
          None
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>use slack_hook::{Attachment, AttachmentBuilder, Field, PayloadBuilder, Slack, SlackText, SlackLink};
use slack_hook::SlackTextContent::{Text, Link};

use taste::{BenchmarkResult, TastingResult};

pub struct SlackNotifier {
    conn: Slack,
    channel: String,
    github_repo: String,
}

impl SlackNotifier {
    pub fn new(hook_url: &str, channel: &str, repo_url: &str) -> SlackNotifier {
        SlackNotifier {
            conn: Slack::new(hook_url).unwrap(),
            channel: String::from(channel),
            github_repo: String::from(repo_url),
        }
    }

    pub fn notify(&self, res: &TastingResult) -> Result<(), String> {
        let payload = PayloadBuilder::new()
            .text(vec![Text("I've tasted commit _".into()),
                       Text(format!("\"{}\"_ -- ", res.commit_msg.lines().next().unwrap())
                           .into()),
                       Link(SlackLink::new(&format!("{}/commit/{}",
                                                    self.github_repo,
                                                    res.commit_id),
                                           &res.commit_id[0..6]))]
                .as_slice())
            .attachments(vec![result_to_attachment(&res)])
            .channel(self.channel.clone())
            .username("taster")
            .icon_emoji(":tea:")
            .build()
            .unwrap();

        match self.conn.send(&payload) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{:?}", e)),
        }
    }
}

fn result_to_attachment(res: &TastingResult) -> Attachment {
    let color = if !res.build || !res.bench {
        "danger"
    } else {
        "good"
    };

    let title = if !res.build {
        "Build failure!"
    } else if !res.bench {
        "Benchmark failure!"
    } else {
        "Performance results:"
    };

    let taste = if !res.build || !res.bench {
        "was inedible"
    } else {
        "tasted nice"
    };

    let mut allfields = Vec::new();
    for res in &res.results {
        let mut nv = res.iter()
            .map(|(k, v)| {
                let val = match v {
                    &BenchmarkResult::Neutral(ref s, _) => s,
                    _ => unimplemented!(),
                };
                Field {
                    title: k.clone(),
                    value: SlackText::new(val.clone()),
                    short: Some(true),
                }
            })
            .collect::<Vec<_>>();
        nv.sort_by(|a, b| b.title.cmp(&a.title));
        allfields.extend(nv);
    }

    AttachmentBuilder::new(format!("It {}.", taste))
        .color(color)
        .title(title)
        .fields(allfields)
        .build()
        .unwrap()
}
<commit_msg>Slack: multiple benchmarks, multiple attachments<commit_after>use slack_hook::{Attachment, AttachmentBuilder, Field, PayloadBuilder, Slack, SlackText, SlackLink};
use slack_hook::SlackTextContent::{Text, Link};

use taste::{BenchmarkResult, TastingResult};

pub struct SlackNotifier {
    conn: Slack,
    channel: String,
    github_repo: String,
}

impl SlackNotifier {
    pub fn new(hook_url: &str, channel: &str, repo_url: &str) -> SlackNotifier {
        SlackNotifier {
            conn: Slack::new(hook_url).unwrap(),
            channel: String::from(channel),
            github_repo: String::from(repo_url),
        }
    }

    pub fn notify(&self, res: &TastingResult) -> Result<(), String> {
        let payload = PayloadBuilder::new()
            .text(vec![Text("I've tasted commit _".into()),
                       Text(format!("\"{}\"_ -- ", res.commit_msg.lines().next().unwrap())
                           .into()),
                       Link(SlackLink::new(&format!("{}/commit/{}",
                                                    self.github_repo,
                                                    res.commit_id),
                                           &res.commit_id[0..6]))]
                .as_slice())
            .attachments(result_to_attachments(&res))
            .channel(self.channel.clone())
            .username("taster")
            .icon_emoji(":tea:")
            .build()
            .unwrap();

        match self.conn.send(&payload) {
            Ok(_) => Ok(()),
            Err(e) => Err(format!("{:?}", e)),
        }
    }
}

fn result_to_attachments(res: &TastingResult) -> Vec<Attachment> {
    let color = if !res.build || !res.bench {
        "danger"
    } else {
        "good"
    };

    let title = if !res.build {
        "Build failure!"
    } else if !res.bench {
        "Benchmark failure!"
    } else {
        "Performance results:"
    };

    let taste = if !res.build || !res.bench {
        "was inedible"
    } else {
        "tasted nice"
    };

    let mut attachments = Vec::new();
    for res in &res.results {
        let mut nv = res.iter()
            .map(|(k, v)| {
                let val = match v {
                    &BenchmarkResult::Neutral(ref s, _) => s,
                    _ => unimplemented!(),
                };
                Field {
                    title: k.clone(),
                    value: SlackText::new(val.clone()),
                    short: Some(true),
                }
            })
            .collect::<Vec<_>>();
        nv.sort_by(|a, b| b.title.cmp(&a.title));
        let att = AttachmentBuilder::new(format!("It {}.", taste))
            .color(color)
            .title(title)
            .fields(nv)
            .build()
            .unwrap();
        attachments.push(att);
    }
    attachments
}
<|endoftext|>


<commit_before>//  rpc-perf - RPC Performance Testing
//  Copyright 2015 Twitter, Inc
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use common::stats::Stat;
use request::BenchmarkConfig;
use std::process::exit;
use tic::{Interest, Meters, Percentile, Receiver, Sample};

pub fn stats_receiver_init(config: &BenchmarkConfig,
                           listen: Option<String>,
                           waterfall: Option<String>,
                           trace: Option<String>)
                           -> Receiver<Stat> {
    let mut stats_config = Receiver::<Stat>::configure()
        .batch_size(128)
        .capacity(65536)
        .duration(config.duration())
        .windows(config.windows());

    if let Some(addr) = listen {
        stats_config = stats_config.http_listen(addr);
    }

    let mut stats_receiver = stats_config.build();

    let counts = vec![Stat::Window,
                      Stat::ResponseOk,
                      Stat::ResponseOkHit,
                      Stat::ResponseOkMiss,
                      Stat::ResponseError,
                      Stat::ResponseTimeout,
                      Stat::RequestPrepared,
                      Stat::RequestSent,
                      Stat::ConnectOk,
                      Stat::ConnectError,
                      Stat::ConnectTimeout,
                      Stat::SocketCreate,
                      Stat::SocketClose,
                      Stat::SocketRead,
                      Stat::SocketFlush,
                      Stat::SocketWrite];

    for c in counts {
        stats_receiver.add_interest(Interest::Count(c));
    }

    for c in vec![Stat::ResponseOk,
                  Stat::ResponseOkHit,
                  Stat::ResponseOkMiss,
                  Stat::ConnectOk] {
        stats_receiver.add_interest(Interest::Percentile(c));
    }

    if let Some(w) = waterfall {
        stats_receiver.add_interest(Interest::Waterfall(Stat::ResponseOk, w));
    }

    if let Some(t) = trace {
        stats_receiver.add_interest(Interest::Waterfall(Stat::ResponseOk, t));
    }

    stats_receiver
}

pub fn meters_delta(t0: &Meters<Stat>, t1: &Meters<Stat>, stat: &Stat) -> u64 {
    *t1.get_count(stat).unwrap_or(&0) - *t0.get_count(stat).unwrap_or(&0)
}

pub fn run(mut receiver: Receiver<Stat>, windows: usize, infinite: bool) {

    let mut window = 0;
    let mut warmup = true;

    let clocksource = receiver.get_clocksource().clone();
    let mut sender = receiver.get_sender().clone();
    sender.set_batch_size(1);

    let mut t0 = clocksource.counter();
    let mut m0 = receiver.clone_meters();

    debug!("stats: collection ready");
    loop {
        receiver.run_once();
        let t1 = clocksource.counter();
        let _ = sender.send(Sample::new(t0, t1, Stat::Window));
        let m1 = receiver.clone_meters();

        if warmup {
            info!("-----");
            if meters_delta(&m0, &m1, &Stat::ConnectOk) == 0 {
                error!("No connections established. Please check that server(s) are available");
                exit(1);
            }
            info!("Warmup complete");
            warmup = false;
        } else {
            let responses = meters_delta(&m0, &m1, &Stat::ResponseOk) +
                            meters_delta(&m0, &m1, &Stat::ResponseError);

            let rate = responses as f64 /
                       ((clocksource.convert(t1) - clocksource.convert(t0)) as f64 /
                        1_000_000_000.0);

            let success_rate = if responses > 0 {
                100.0 * (responses - meters_delta(&m0, &m1, &Stat::ResponseError)) as f64 /
                (responses + meters_delta(&m0, &m1, &Stat::ResponseTimeout)) as f64
            } else {
                0.0
            };

            let hit = meters_delta(&m0, &m1, &Stat::ResponseOkHit);
            let miss = meters_delta(&m0, &m1, &Stat::ResponseOkMiss);

            let hit_rate = if (hit + miss) > 0 {
                100.0 * hit as f64 / (hit + miss) as f64
            } else {
                0.0
            };

            info!("-----");
            info!("Window: {}", *m1.get_count(&Stat::Window).unwrap_or(&0));
            let inflight = *m1.get_count(&Stat::RequestSent).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseOk).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseError).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseTimeout).unwrap_or(&0) as i64;
            let open = *m1.get_count(&Stat::SocketCreate).unwrap_or(&0) as i64 -
                       *m1.get_count(&Stat::SocketClose).unwrap_or(&0) as i64;
            info!("Connections: Ok: {} Error: {} Timeout: {} Open: {}",
                  meters_delta(&m0, &m1, &Stat::ConnectOk),
                  meters_delta(&m0, &m1, &Stat::ConnectError),
                  meters_delta(&m0, &m1, &Stat::ConnectTimeout),
                  open);
            info!("Sockets: Create: {} Close: {} Read: {} Write: {} Flush: {}",
                  meters_delta(&m0, &m1, &Stat::SocketCreate),
                  meters_delta(&m0, &m1, &Stat::SocketClose),
                  meters_delta(&m0, &m1, &Stat::SocketRead),
                  meters_delta(&m0, &m1, &Stat::SocketWrite),
                  meters_delta(&m0, &m1, &Stat::SocketFlush));
            info!("Requests: Sent: {} Prepared: {} In-Flight: {}",
                  meters_delta(&m0, &m1, &Stat::RequestSent),
                  meters_delta(&m0, &m1, &Stat::RequestPrepared),
                  inflight);
            info!("Responses: Ok: {} Error: {} Timeout: {} Hit: {} Miss: {}",
                  meters_delta(&m0, &m1, &Stat::ResponseOk),
                  meters_delta(&m0, &m1, &Stat::ResponseError),
                  meters_delta(&m0, &m1, &Stat::ResponseTimeout),
                  meters_delta(&m0, &m1, &Stat::ResponseOkHit),
                  meters_delta(&m0, &m1, &Stat::ResponseOkMiss));
            info!("Rate: {:.*} rps Success: {:.*} % Hit Rate: {:.*} %",
                  2,
                  rate,
                  2,
                  success_rate,
                  2,
                  hit_rate);
            display_percentiles(&m1, &Stat::ResponseOk, "Response OK");
        }

        m0 = m1;
        t0 = t1;
        window += 1;

        if window > windows {
            receiver.save_files();
            if infinite {
                window = 0;
                receiver.clear_heatmaps();
            } else {
                break;
            }
        }
    }
}

fn display_percentiles(meters: &Meters<Stat>, stat: &Stat, label: &str) {
    info!("Percentiles: {} (us): min: {} p50: {} p90: {} p99: {} p999: {} p9999: {} max: {}",
                    label,
                    meters.get_percentile(stat,
                        Percentile("min".to_owned(), 0.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p50".to_owned(), 50.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p90".to_owned(), 90.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p99".to_owned(), 99.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p999".to_owned(), 99.9)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p9999".to_owned(), 99.99)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("max".to_owned(), 100.0)).unwrap_or(&0) / 1000,
                );
}
<commit_msg>fix: trace file<commit_after>//  rpc-perf - RPC Performance Testing
//  Copyright 2015 Twitter, Inc
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use common::stats::Stat;
use request::BenchmarkConfig;
use std::process::exit;
use tic::{Interest, Meters, Percentile, Receiver, Sample};

pub fn stats_receiver_init(config: &BenchmarkConfig,
                           listen: Option<String>,
                           waterfall: Option<String>,
                           trace: Option<String>)
                           -> Receiver<Stat> {
    let mut stats_config = Receiver::<Stat>::configure()
        .batch_size(128)
        .capacity(65536)
        .duration(config.duration())
        .windows(config.windows());

    if let Some(addr) = listen {
        stats_config = stats_config.http_listen(addr);
    }

    let mut stats_receiver = stats_config.build();

    let counts = vec![Stat::Window,
                      Stat::ResponseOk,
                      Stat::ResponseOkHit,
                      Stat::ResponseOkMiss,
                      Stat::ResponseError,
                      Stat::ResponseTimeout,
                      Stat::RequestPrepared,
                      Stat::RequestSent,
                      Stat::ConnectOk,
                      Stat::ConnectError,
                      Stat::ConnectTimeout,
                      Stat::SocketCreate,
                      Stat::SocketClose,
                      Stat::SocketRead,
                      Stat::SocketFlush,
                      Stat::SocketWrite];

    for c in counts {
        stats_receiver.add_interest(Interest::Count(c));
    }

    for c in vec![Stat::ResponseOk,
                  Stat::ResponseOkHit,
                  Stat::ResponseOkMiss,
                  Stat::ConnectOk] {
        stats_receiver.add_interest(Interest::Percentile(c));
    }

    if let Some(w) = waterfall {
        stats_receiver.add_interest(Interest::Waterfall(Stat::ResponseOk, w));
    }

    if let Some(t) = trace {
        stats_receiver.add_interest(Interest::Trace(Stat::ResponseOk, t));
    }

    stats_receiver
}

pub fn meters_delta(t0: &Meters<Stat>, t1: &Meters<Stat>, stat: &Stat) -> u64 {
    *t1.get_count(stat).unwrap_or(&0) - *t0.get_count(stat).unwrap_or(&0)
}

pub fn run(mut receiver: Receiver<Stat>, windows: usize, infinite: bool) {

    let mut window = 0;
    let mut warmup = true;

    let clocksource = receiver.get_clocksource().clone();
    let mut sender = receiver.get_sender().clone();
    sender.set_batch_size(1);

    let mut t0 = clocksource.counter();
    let mut m0 = receiver.clone_meters();

    debug!("stats: collection ready");
    loop {
        receiver.run_once();
        let t1 = clocksource.counter();
        let _ = sender.send(Sample::new(t0, t1, Stat::Window));
        let m1 = receiver.clone_meters();

        if warmup {
            info!("-----");
            if meters_delta(&m0, &m1, &Stat::ConnectOk) == 0 {
                error!("No connections established. Please check that server(s) are available");
                exit(1);
            }
            info!("Warmup complete");
            warmup = false;
        } else {
            let responses = meters_delta(&m0, &m1, &Stat::ResponseOk) +
                            meters_delta(&m0, &m1, &Stat::ResponseError);

            let rate = responses as f64 /
                       ((clocksource.convert(t1) - clocksource.convert(t0)) as f64 /
                        1_000_000_000.0);

            let success_rate = if responses > 0 {
                100.0 * (responses - meters_delta(&m0, &m1, &Stat::ResponseError)) as f64 /
                (responses + meters_delta(&m0, &m1, &Stat::ResponseTimeout)) as f64
            } else {
                0.0
            };

            let hit = meters_delta(&m0, &m1, &Stat::ResponseOkHit);
            let miss = meters_delta(&m0, &m1, &Stat::ResponseOkMiss);

            let hit_rate = if (hit + miss) > 0 {
                100.0 * hit as f64 / (hit + miss) as f64
            } else {
                0.0
            };

            info!("-----");
            info!("Window: {}", *m1.get_count(&Stat::Window).unwrap_or(&0));
            let inflight = *m1.get_count(&Stat::RequestSent).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseOk).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseError).unwrap_or(&0) as i64 -
                           *m1.get_count(&Stat::ResponseTimeout).unwrap_or(&0) as i64;
            let open = *m1.get_count(&Stat::SocketCreate).unwrap_or(&0) as i64 -
                       *m1.get_count(&Stat::SocketClose).unwrap_or(&0) as i64;
            info!("Connections: Ok: {} Error: {} Timeout: {} Open: {}",
                  meters_delta(&m0, &m1, &Stat::ConnectOk),
                  meters_delta(&m0, &m1, &Stat::ConnectError),
                  meters_delta(&m0, &m1, &Stat::ConnectTimeout),
                  open);
            info!("Sockets: Create: {} Close: {} Read: {} Write: {} Flush: {}",
                  meters_delta(&m0, &m1, &Stat::SocketCreate),
                  meters_delta(&m0, &m1, &Stat::SocketClose),
                  meters_delta(&m0, &m1, &Stat::SocketRead),
                  meters_delta(&m0, &m1, &Stat::SocketWrite),
                  meters_delta(&m0, &m1, &Stat::SocketFlush));
            info!("Requests: Sent: {} Prepared: {} In-Flight: {}",
                  meters_delta(&m0, &m1, &Stat::RequestSent),
                  meters_delta(&m0, &m1, &Stat::RequestPrepared),
                  inflight);
            info!("Responses: Ok: {} Error: {} Timeout: {} Hit: {} Miss: {}",
                  meters_delta(&m0, &m1, &Stat::ResponseOk),
                  meters_delta(&m0, &m1, &Stat::ResponseError),
                  meters_delta(&m0, &m1, &Stat::ResponseTimeout),
                  meters_delta(&m0, &m1, &Stat::ResponseOkHit),
                  meters_delta(&m0, &m1, &Stat::ResponseOkMiss));
            info!("Rate: {:.*} rps Success: {:.*} % Hit Rate: {:.*} %",
                  2,
                  rate,
                  2,
                  success_rate,
                  2,
                  hit_rate);
            display_percentiles(&m1, &Stat::ResponseOk, "Response OK");
        }

        m0 = m1;
        t0 = t1;
        window += 1;

        if window > windows {
            receiver.save_files();
            if infinite {
                window = 0;
                receiver.clear_heatmaps();
            } else {
                break;
            }
        }
    }
}

fn display_percentiles(meters: &Meters<Stat>, stat: &Stat, label: &str) {
    info!("Percentiles: {} (us): min: {} p50: {} p90: {} p99: {} p999: {} p9999: {} max: {}",
                    label,
                    meters.get_percentile(stat,
                        Percentile("min".to_owned(), 0.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p50".to_owned(), 50.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p90".to_owned(), 90.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p99".to_owned(), 99.0)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p999".to_owned(), 99.9)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("p9999".to_owned(), 99.99)).unwrap_or(&0) / 1000,
                    meters.get_percentile(stat,
                        Percentile("max".to_owned(), 100.0)).unwrap_or(&0) / 1000,
                );
}
<|endoftext|>