<commit_before>extern crate loc;

extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate walkdir;
extern crate regex;

use clap::{Arg, App, AppSettings};
use walkdir::WalkDir;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};
use regex::Regex;

use loc::*;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

#[derive(Clone)]
struct FileCount {
    path: String,
    lang: Lang,
    count: Count,
}

impl Worker {
    fn run(self) -> Vec<FileCount> {
        let mut v: Vec<FileCount> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = lang_from_ext(&path);
                    if lang != Lang::Unrecognized {
                        let count = count(&path);
                        v.push(FileCount {
                            lang: lang,
                            path: String::from(path),
                            count: count,
                        });
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version("0.1")
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        // TODO(cgag): actually implement filtering
        .arg(Arg::with_name("exclude")
            .required(false)
            .long("exclude")
            .value_name("REGEX")
            .takes_value(true)
            .help("comma separated list of files/directories to ignore"))
        .arg(Arg::with_name("files")
             .required(false)
             .long("files")
             .takes_value(false)
             .help("Show stats for individual files"))
        .arg(Arg::with_name("sort")
            .required(false)
            .long("sort")
            .value_name("COLUMN")
            .takes_value(true)
            .help("Column to sort by"))
        .arg(Arg::with_name("target")
            .required(true)
            .help("File or directory to count"))
        .get_matches();

    let filepaths = matches.values_of("target").unwrap();
    let sort = matches.value_of("sort").unwrap_or("language");
    let by_file = matches.is_present("files");
    let exclude_regex = match matches.value_of("exclude") {
        Some(rx_str) => {
            match Regex::new(rx_str) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for filepath in filepaths {
        // TODO(cgag): cleanup?
        if exclude_regex.is_some() {
            let r = &&exclude_regex.clone().unwrap();
            for entry in WalkDir::new(filepath).into_iter().filter_map(|e| e.ok()) {
                if entry.file_type().is_file() {
                    let path = entry.path().to_str().unwrap();
                    if !r.is_match(path) {
                        workq.push(Work::File(String::from(path)));
                    }
                }
            }
        } else {
            for entry in WalkDir::new(filepath).into_iter().filter_map(|e| e.ok()) {
                if entry.file_type().is_file() {
                    let path = entry.path().to_str().unwrap();
                    workq.push(Work::File(String::from(path)));
                }
            }
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut filecounts: Vec<FileCount> = Vec::new();
    for worker in workers {
        filecounts.extend(worker.join().unwrap().iter().cloned())
    }

    let mut by_lang: HashMap<Lang, Vec<FileCount>> = HashMap::new();
    for fc in filecounts {
        match by_lang.entry(fc.lang) {
            Entry::Occupied(mut by_lang) => by_lang.get_mut().push(fc),
            Entry::Vacant(by_lang) => {
                by_lang.insert(vec![fc]);
            }
        };
    }

    let linesep = str_repeat("-", 81);

    if by_file {
        // TODO(cgag): Need sorting for by_file as well.
        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        for (lang, filecounts) in by_lang {
            let mut total = Count::default();
            for fc in &filecounts {
                total.merge(&fc.count);
            }

            println!("{}", linesep);
            println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     filecounts.len(),
                     total.lines,
                     total.blank,
                     total.comment,
                     total.code);

            println!("{}", linesep);
            for fc in filecounts {
                println!("|{0: <25} {1: >12} {2: >12} {3: >12} {4: >12}",
                         last_n_chars(&fc.path, 25),
                         fc.count.lines,
                         fc.count.blank,
                         fc.count.comment,
                         fc.count.code);
            }
        }
    } else {
        let mut lang_totals: HashMap<&Lang, LangTotal> = HashMap::new();
        for (lang, filecounts) in &by_lang {
            let mut lang_total = Count::default();
            for fc in filecounts {
                lang_total.merge(&fc.count);
            }
            lang_totals.insert(lang,
                               LangTotal {
                                   files: filecounts.len() as u32,
                                   count: lang_total,
                               });
        }

        let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&Lang, &LangTotal)>>();
        match sort {
            "language" => totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s())),
            "files" => totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.files.cmp(&c1.files)),
            "code" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
            "comment" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.comment.cmp(&c1.count.comment))
            }
            "blank" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.blank.cmp(&c1.count.blank))
            }
            "lines" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.lines.cmp(&c1.count.lines))
            }
            _ => {
                println!("invalid sort option {}, sorting by language", sort);
                totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s()))
            }
        }

        let mut totals = LangTotal {
            files: 0,
            count: Count::default(),
        };
        for &(_, total) in &totals_by_lang {
            totals.files += total.files;
            totals.count.code += total.count.code;
            totals.count.blank += total.count.blank;
            totals.count.comment += total.count.comment;
            totals.count.lines += total.count.lines;
        }

        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        for &(lang, total) in &totals_by_lang {
            println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     total.files,
                     total.count.lines,
                     total.count.blank,
                     total.count.comment,
                     total.count.code);
        }

        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Total",
                 totals.files,
                 totals.count.lines,
                 totals.count.blank,
                 totals.count.comment,
                 totals.count.code);
        println!("{}", linesep);
    }
}

fn last_n_chars(s: &str, n: usize) -> String {
    if s.len() <= n {
        return String::from(s);
    }
    s.chars().skip(s.len() - n).collect::<String>()
}

fn str_repeat(s: &str, n: usize) -> String {
    std::iter::repeat(s).take(n).collect::<Vec<_>>().join("")
}
<commit_msg>move total calculation closer to use site<commit_after>extern crate loc;

extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate walkdir;
extern crate regex;

use clap::{Arg, App, AppSettings};
use walkdir::WalkDir;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};
use regex::Regex;

use loc::*;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

#[derive(Clone)]
struct FileCount {
    path: String,
    lang: Lang,
    count: Count,
}

impl Worker {
    fn run(self) -> Vec<FileCount> {
        let mut v: Vec<FileCount> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = lang_from_ext(&path);
                    if lang != Lang::Unrecognized {
                        let count = count(&path);
                        v.push(FileCount {
                            lang: lang,
                            path: String::from(path),
                            count: count,
                        });
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version("0.1")
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        // TODO(cgag): actually implement filtering
        .arg(Arg::with_name("exclude")
            .required(false)
            .long("exclude")
            .value_name("REGEX")
            .takes_value(true)
            .help("comma separated list of files/directories to ignore"))
        .arg(Arg::with_name("files")
             .required(false)
             .long("files")
             .takes_value(false)
             .help("Show stats for individual files"))
        .arg(Arg::with_name("sort")
            .required(false)
            .long("sort")
            .value_name("COLUMN")
            .takes_value(true)
            .help("Column to sort by"))
        .arg(Arg::with_name("target")
            .required(true)
            .help("File or directory to count"))
        .get_matches();

    let filepaths = matches.values_of("target").unwrap();
    let sort = matches.value_of("sort").unwrap_or("language");
    let by_file = matches.is_present("files");
    let exclude_regex = match matches.value_of("exclude") {
        Some(rx_str) => {
            match Regex::new(rx_str) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for filepath in filepaths {
        // TODO(cgag): cleanup?
        if exclude_regex.is_some() {
            let r = &&exclude_regex.clone().unwrap();
            for entry in WalkDir::new(filepath).into_iter().filter_map(|e| e.ok()) {
                if entry.file_type().is_file() {
                    let path = entry.path().to_str().unwrap();
                    if !r.is_match(path) {
                        workq.push(Work::File(String::from(path)));
                    }
                }
            }
        } else {
            for entry in WalkDir::new(filepath).into_iter().filter_map(|e| e.ok()) {
                if entry.file_type().is_file() {
                    let path = entry.path().to_str().unwrap();
                    workq.push(Work::File(String::from(path)));
                }
            }
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut filecounts: Vec<FileCount> = Vec::new();
    for worker in workers {
        filecounts.extend(worker.join().unwrap().iter().cloned())
    }

    let mut by_lang: HashMap<Lang, Vec<FileCount>> = HashMap::new();
    for fc in filecounts {
        match by_lang.entry(fc.lang) {
            Entry::Occupied(mut by_lang) => by_lang.get_mut().push(fc),
            Entry::Vacant(by_lang) => {
                by_lang.insert(vec![fc]);
            }
        };
    }

    let linesep = str_repeat("-", 81);

    if by_file {
        // TODO(cgag): Need sorting for by_file as well.
        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        for (lang, filecounts) in by_lang {
            let mut total = Count::default();
            for fc in &filecounts {
                total.merge(&fc.count);
            }

            println!("{}", linesep);
            println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     filecounts.len(),
                     total.lines,
                     total.blank,
                     total.comment,
                     total.code);

            println!("{}", linesep);
            for fc in filecounts {
                println!("|{0: <25} {1: >12} {2: >12} {3: >12} {4: >12}",
                         last_n_chars(&fc.path, 25),
                         fc.count.lines,
                         fc.count.blank,
                         fc.count.comment,
                         fc.count.code);
            }
        }
    } else {
        let mut lang_totals: HashMap<&Lang, LangTotal> = HashMap::new();
        for (lang, filecounts) in &by_lang {
            let mut lang_total = Count::default();
            for fc in filecounts {
                lang_total.merge(&fc.count);
            }
            lang_totals.insert(lang,
                               LangTotal {
                                   files: filecounts.len() as u32,
                                   count: lang_total,
                               });
        }

        let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&Lang, &LangTotal)>>();
        match sort {
            "language" => totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s())),
            "files" => totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.files.cmp(&c1.files)),
            "code" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
            "comment" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.comment.cmp(&c1.count.comment))
            }
            "blank" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.blank.cmp(&c1.count.blank))
            }
            "lines" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.lines.cmp(&c1.count.lines))
            }
            _ => {
                println!("invalid sort option {}, sorting by language", sort);
                totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s()))
            }
        }

        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        for &(lang, total) in &totals_by_lang {
            println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     total.files,
                     total.count.lines,
                     total.count.blank,
                     total.count.comment,
                     total.count.code);
        }

        let mut totals = LangTotal {
            files: 0,
            count: Count::default(),
        };
        for &(_, total) in &totals_by_lang {
            totals.files += total.files;
            totals.count.code += total.count.code;
            totals.count.blank += total.count.blank;
            totals.count.comment += total.count.comment;
            totals.count.lines += total.count.lines;
        }

        println!("{}", linesep);
        println!(" {0: <18} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Total",
                 totals.files,
                 totals.count.lines,
                 totals.count.blank,
                 totals.count.comment,
                 totals.count.code);
        println!("{}", linesep);
    }
}

fn last_n_chars(s: &str, n: usize) -> String {
    if s.len() <= n {
        return String::from(s);
    }
    s.chars().skip(s.len() - n).collect::<String>()
}

fn str_repeat(s: &str, n: usize) -> String {
    std::iter::repeat(s).take(n).collect::<Vec<_>>().join("")
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App, ArgMatches};

mod http_status;
mod log_parser;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap_or("-");

    let time_filter = match args.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let conditions = filter::FilterConditions {
        include_terms: match args.value_of("include_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        exclude_terms: match args.value_of("exclude_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        latest_time: time_filter,
    };

    let input: Box<io::Read> = match filename {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(filename).unwrap())
    };

    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            match Request::new_from_log_line(&line_value) {
                Ok(request) => {
                    if first_request.is_none() {
                        first_request = Some(request.clone());
                    }
                    requests.push(request);
                },
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        if line_value.contains("<-") {
            match Response::new_from_log_line(&line_value) {
                Ok(response) => responses.push(response),
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

        let mut new_times: Vec<i64> = pairs.iter()
            .filter(|pair| filter::matches_filter(&pair, &conditions) )
            .map(|pair| pair.response.response_time.num_milliseconds())
            .collect();

        times.append(&mut new_times);
    }

    let result = analyzer::analyze(&times);

    match result {
        Some(result) => {
            if args.is_present("graphite-server") {
                let stream = TcpStream::connect(
                    (
                        args.value_of("graphite-server").unwrap(),
                        args.value_of("graphite-port").unwrap().parse().unwrap()
                    )
                ).expect("Could not connect to the Graphite server");

                let timezone = first_request.unwrap().time.timezone();

                render::render_graphite(result, UTC::now().with_timezone(&timezone), args.value_of("graphite-prefix"), stream);
            } else {
                render::render_terminal(result);
            }
        },
        None => println_stderr!("No matching log lines in file.")
    }
}
<commit_msg>Remove temporary variable for consistency<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;

extern crate clap;
use clap::{Arg, App, ArgMatches};

mod http_status;
mod log_parser;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(false)
            .help("Log file to analyze, defaults to stdin")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("graphite-server")
            .value_name("GRAPHITE_SERVER")
            .long("graphite-server")
            .help("Send values to this Graphite server instead of stdout")
            .takes_value(true))
        .arg(Arg::with_name("graphite-port")
            .value_name("GRAPHITE_PORT")
            .long("graphite-port")
            .takes_value(true)
            .default_value("2003"))
        .arg(Arg::with_name("graphite-prefix")
            .value_name("GRAPHITE_PREFIX")
            .long("graphite-prefix")
            .help("Prefix for Graphite key, e.g. 'servers.prod.publisher1'")
            .takes_value(true))
        .get_matches()
}

fn main() {
    let args = parse_args();

    let filename = args.value_of("filename").unwrap_or("-");

    let conditions = filter::FilterConditions {
        include_terms: match args.value_of("include_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        exclude_terms: match args.value_of("exclude_term") {
            Some(value) => Some(vec![value.to_string()]),
            None => None
        },
        latest_time: match args.value_of("time_filter_minutes") {
            Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
            None => None
        }
    };

    let input: Box<io::Read> = match filename {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(filename).unwrap())
    };

    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            match Request::new_from_log_line(&line_value) {
                Ok(request) => {
                    if first_request.is_none() {
                        first_request = Some(request.clone());
                    }
                    requests.push(request);
                },
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        if line_value.contains("<-") {
            match Response::new_from_log_line(&line_value) {
                Ok(response) => responses.push(response),
                Err(err) => println_stderr!("Skipped a line: {}", err)
            }
        }

        let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

        let mut new_times: Vec<i64> = pairs.iter()
            .filter(|pair| filter::matches_filter(&pair, &conditions) )
            .map(|pair| pair.response.response_time.num_milliseconds())
            .collect();

        times.append(&mut new_times);
    }

    let result = analyzer::analyze(&times);

    match result {
        Some(result) => {
            if args.is_present("graphite-server") {
                let stream = TcpStream::connect(
                    (
                        args.value_of("graphite-server").unwrap(),
                        args.value_of("graphite-port").unwrap().parse().unwrap()
                    )
                ).expect("Could not connect to the Graphite server");

                let timezone = first_request.unwrap().time.timezone();

                render::render_graphite(result, UTC::now().with_timezone(&timezone), args.value_of("graphite-prefix"), stream);
            } else {
                render::render_terminal(result);
            }
        },
        None => println_stderr!("No matching log lines in file.")
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::cmp;

#[derive(Debug, PartialEq)]
enum ProgressState {
    Probe,
    Replicate,
    Snapshot,
}

struct Progress {
    matched: u64,
    next_idx: u64,
    // When in ProgressStateProbe, leader sends at most one replication message
    // per heartbeat interval. It also probes actual progress of the follower.
    //
    // When in ProgressStateReplicate, leader optimistically increases next
    // to the latest entry sent after sending replication message. This is
    // an optimized state for fast replicating log entries to the follower.
    //
    // When in ProgressStateSnapshot, leader should have sent out snapshot
    // before and stops sending any replication message.
    state: ProgressState,
    // Paused is used in ProgressStateProbe.
    // When Paused is true, raft should pause sending replication message to this peer.
    paused: bool,
    // pending_snapshot is used in ProgressStateSnapshot.
    // If there is a pending snapshot, the pendingSnapshot will be set to the
    // index of the snapshot. If pendingSnapshot is set, the replication process of
    // this Progress will be paused. raft will not resend snapshot until the pending one
    // is reported to be failed.
    pending_snapshot: u64,

    // recent_active is true if the progress is recently active. Receiving any messages
    // from the corresponding follower indicates the progress is active.
    // RecentActive can be reset to false after an election timeout.
    recent_active: bool,

    // Inflights is a sliding window for the inflight messages.
    // When inflights is full, no more message should be sent.
    // When a leader sends out a message, the index of the last
    // entry should be added to inflights. The index MUST be added
    // into inflights in order.
    // When a leader receives a reply, the previous inflights should
    // be freed by calling inflights.freeTo.
    ins: Inflights,
}

impl Progress {
    pub fn reset_state(&mut self, state: ProgressState) {
        self.paused = false;
        self.recent_active = false;
        self.pending_snapshot = 0;
        self.state = state;
        self.ins.reset();
    }

    pub fn become_probe(&mut self) {
        // If the original state is ProgressStateSnapshot, progress knows that
        // the pending snapshot has been sent to this peer successfully, then
        // probes from pendingSnapshot + 1.
        if self.state == ProgressState::Snapshot {
            let pending_snapshot = self.pending_snapshot;
            self.reset_state(ProgressState::Probe);
            self.next_idx = cmp::max(self.matched + 1, pending_snapshot + 1);
        } else {
            self.reset_state(ProgressState::Probe);
            self.next_idx = self.matched + 1;
        }
    }

    pub fn become_replicate(&mut self) {
        self.reset_state(ProgressState::Replicate);
        self.next_idx = self.matched + 1;
    }

    pub fn become_snapshot(&mut self, snapshot_idx: u64) {
        self.reset_state(ProgressState::Snapshot);
        self.pending_snapshot = snapshot_idx;
    }

    pub fn snapshot_failure(&self) -> bool {
        self.pending_snapshot == 0
    }

    // maybe_snapshot_abort unsets pendingSnapshot if Match is equal or higher than
    // the pendingSnapshot
    pub fn maybe_snapshot_abort(&self) -> bool {
        self.state == ProgressState::Snapshot && self.matched >= self.pending_snapshot
    }

    // maybe_update returns false if the given n index comes from an outdated message.
    // Otherwise it updates the progress and returns true.
    pub fn maybe_update(&mut self, n: u64) -> bool {
        let mut updated = false;
        if self.matched < n {
            self.matched = n;
            updated = true;
            self.resume()
        }

        if self.next_idx < n + 1 {
            self.next_idx = n + 1
        }

        updated
    }

    pub fn optimistic_update(&mut self, n: u64) {
        self.next_idx = n + 1;
    }

    // maybe_decr_to returns false if the given to index comes from an out of order message.
    // Otherwise it decreases the progress next index to min(rejected, last) and returns true.
    pub fn maybe_decr_to(&mut self, rejected: u64, last: u64) -> bool {
        if self.state == ProgressState::Replicate {
            // the rejection must be stale if the progress has matched and "rejected"
            // is smaller than "match".
            if rejected <= self.matched {
                return false;
            }
            self.next_idx = self.matched + 1;
            return true;
        }

        // the rejection must be stale if "rejected" does not match next - 1
        if self.next_idx - 1 != rejected {
            return false;
        }

        self.next_idx = cmp::min(rejected, last + 1);
        if self.next_idx < 1 {
            self.next_idx = 1;
        }
        self.resume();
        true
    }

    pub fn is_paused(&self) -> bool {
        match self.state {
            ProgressState::Probe => self.paused,
            ProgressState::Replicate => self.ins.full(),
            ProgressState::Snapshot => true,
        }
    }

    pub fn resume(&mut self) {
        self.paused = false;
    }

    pub fn pause(&mut self) {
        self.paused = true;
    }
}

#[derive(Debug, Default)]
struct Inflights {
    // the starting index in the buffer
    start: usize,
    // number of inflights in the buffer
    count: usize,

    // ring buffer
    buffer: Vec<u64>,
}

impl Inflights {
    pub fn new(cap: usize) -> Inflights {
        Inflights { buffer: Vec::with_capacity(cap), ..Default::default() }
    }

    // full returns true if the inflights is full.
    pub fn full(&self) -> bool {
        self.count == self.cap()
    }

    pub fn cap(&self) -> usize {
        self.buffer.capacity()
    }

    // add adds an inflight into inflights
    pub fn add(&mut self, inflight: u64) {
        if self.full() {
            panic!("cannot add into a full inflights")
        }

        let mut next = self.start + self.count;
        if next >= self.cap() {
            next -= self.cap();
        }
        self.buffer[next] = inflight;
        self.count += 1;
    }

    // free_to frees the inflights smaller or equal to the given `to` flight.
    pub fn free_to(&mut self, to: u64) {
        if self.count == 0 || to < self.buffer[self.start] {
            // out of the left side of the window
            return;
        }

        let mut i = 0usize;
        let mut idx = self.start;
        while i < self.count {
            if to < self.buffer[idx] {
                // found the first large inflight
                break;
            }

            // increase index and maybe rotate
            idx += 1;
            if idx >= self.cap() {
                idx -= self.cap();
            }

            i += 1;
        }

        // free i inflights and set new start index
        self.count -= i;
        self.start = idx;
    }

    pub fn free_first_one(&mut self) {
        let start = self.start;
        self.free_to(start);
    }

    // resets frees all inflights.
    pub fn reset(&mut self) {
        self.count = 0;
        self.start = 0;
    }
}
<commit_msg>Fix free_first_one<commit_after>#![allow(dead_code)]

use std::cmp;

#[derive(Debug, PartialEq)]
enum ProgressState {
    Probe,
    Replicate,
    Snapshot,
}

struct Progress {
    matched: u64,
    next_idx: u64,
    // When in ProgressStateProbe, leader sends at most one replication message
    // per heartbeat interval. It also probes actual progress of the follower.
    //
    // When in ProgressStateReplicate, leader optimistically increases next
    // to the latest entry sent after sending replication message. This is
    // an optimized state for fast replicating log entries to the follower.
    //
    // When in ProgressStateSnapshot, leader should have sent out snapshot
    // before and stops sending any replication message.
    state: ProgressState,
    // Paused is used in ProgressStateProbe.
    // When Paused is true, raft should pause sending replication message to this peer.
    paused: bool,
    // pending_snapshot is used in ProgressStateSnapshot.
    // If there is a pending snapshot, the pendingSnapshot will be set to the
    // index of the snapshot. If pendingSnapshot is set, the replication process of
    // this Progress will be paused. raft will not resend snapshot until the pending one
    // is reported to be failed.
    pending_snapshot: u64,

    // recent_active is true if the progress is recently active. Receiving any messages
    // from the corresponding follower indicates the progress is active.
    // RecentActive can be reset to false after an election timeout.
    recent_active: bool,

    // Inflights is a sliding window for the inflight messages.
    // When inflights is full, no more message should be sent.
    // When a leader sends out a message, the index of the last
    // entry should be added to inflights. The index MUST be added
    // into inflights in order.
    // When a leader receives a reply, the previous inflights should
    // be freed by calling inflights.freeTo.
    ins: Inflights,
}

impl Progress {
    pub fn reset_state(&mut self, state: ProgressState) {
        self.paused = false;
        self.recent_active = false;
        self.pending_snapshot = 0;
        self.state = state;
        self.ins.reset();
    }

    pub fn become_probe(&mut self) {
        // If the original state is ProgressStateSnapshot, progress knows that
        // the pending snapshot has been sent to this peer successfully, then
        // probes from pendingSnapshot + 1.
        if self.state == ProgressState::Snapshot {
            let pending_snapshot = self.pending_snapshot;
            self.reset_state(ProgressState::Probe);
            self.next_idx = cmp::max(self.matched + 1, pending_snapshot + 1);
        } else {
            self.reset_state(ProgressState::Probe);
            self.next_idx = self.matched + 1;
        }
    }

    pub fn become_replicate(&mut self) {
        self.reset_state(ProgressState::Replicate);
        self.next_idx = self.matched + 1;
    }

    pub fn become_snapshot(&mut self, snapshot_idx: u64) {
        self.reset_state(ProgressState::Snapshot);
        self.pending_snapshot = snapshot_idx;
    }

    pub fn snapshot_failure(&self) -> bool {
        self.pending_snapshot == 0
    }

    // maybe_snapshot_abort unsets pendingSnapshot if Match is equal or higher than
    // the pendingSnapshot
    pub fn maybe_snapshot_abort(&self) -> bool {
        self.state == ProgressState::Snapshot && self.matched >= self.pending_snapshot
    }

    // maybe_update returns false if the given n index comes from an outdated message.
    // Otherwise it updates the progress and returns true.
    pub fn maybe_update(&mut self, n: u64) -> bool {
        let mut updated = false;
        if self.matched < n {
            self.matched = n;
            updated = true;
            self.resume()
        }

        if self.next_idx < n + 1 {
            self.next_idx = n + 1
        }

        updated
    }

    pub fn optimistic_update(&mut self, n: u64) {
        self.next_idx = n + 1;
    }

    // maybe_decr_to returns false if the given to index comes from an out of order message.
    // Otherwise it decreases the progress next index to min(rejected, last) and returns true.
    pub fn maybe_decr_to(&mut self, rejected: u64, last: u64) -> bool {
        if self.state == ProgressState::Replicate {
            // the rejection must be stale if the progress has matched and "rejected"
            // is smaller than "match".
            if rejected <= self.matched {
                return false;
            }
            self.next_idx = self.matched + 1;
            return true;
        }

        // the rejection must be stale if "rejected" does not match next - 1
        if self.next_idx - 1 != rejected {
            return false;
        }

        self.next_idx = cmp::min(rejected, last + 1);
        if self.next_idx < 1 {
            self.next_idx = 1;
        }
        self.resume();
        true
    }

    pub fn is_paused(&self) -> bool {
        match self.state {
            ProgressState::Probe => self.paused,
            ProgressState::Replicate => self.ins.full(),
            ProgressState::Snapshot => true,
        }
    }

    pub fn resume(&mut self) {
        self.paused = false;
    }

    pub fn pause(&mut self) {
        self.paused = true;
    }
}

#[derive(Debug, Default)]
struct Inflights {
    // the starting index in the buffer
    start: usize,
    // number of inflights in the buffer
    count: usize,

    // ring buffer
    buffer: Vec<u64>,
}

impl Inflights {
    pub fn new(cap: usize) -> Inflights {
        Inflights { buffer: Vec::with_capacity(cap), ..Default::default() }
    }

    // full returns true if the inflights is full.
    pub fn full(&self) -> bool {
        self.count == self.cap()
    }

    pub fn cap(&self) -> usize {
        self.buffer.capacity()
    }

    // add adds an inflight into inflights
    pub fn add(&mut self, inflight: u64) {
        if self.full() {
            panic!("cannot add into a full inflights")
        }

        let mut next = self.start + self.count;
        if next >= self.cap() {
            next -= self.cap();
        }
        self.buffer[next] = inflight;
        self.count += 1;
    }

    // free_to frees the inflights smaller or equal to the given `to` flight.
    pub fn free_to(&mut self, to: u64) {
        if self.count == 0 || to < self.buffer[self.start] {
            // out of the left side of the window
            return;
        }

        let mut i = 0usize;
        let mut idx = self.start;
        while i < self.count {
            if to < self.buffer[idx] {
                // found the first large inflight
                break;
            }

            // increase index and maybe rotate
            idx += 1;
            if idx >= self.cap() {
                idx -= self.cap();
            }

            i += 1;
        }

        // free i inflights and set new start index
        self.count -= i;
        self.start = idx;
    }

    pub fn free_first_one(&mut self) {
        let start = self.buffer[self.start];
        self.free_to(start);
    }

    // resets frees all inflights.
    pub fn reset(&mut self) {
        self.count = 0;
        self.start = 0;
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use rotor::Scope;

use {Transport, Request, StreamSocket};

// #[derive(Clone, Clone)]
// This could be Copy, but I think it could be implemented efficient enough
// without Copy and Clone. Probably we will enable them for the user code later
#[derive(Debug)]
pub enum Expectation {
    /// Read number of bytes
    ///
    /// The buffer that is passed to bytes_read might contain more bytes, but
    /// `num` will contain a number of bytes passed into `Bytes` constructor.
    ///
    /// Note that real number of bytes that `netbuf::Buf` might contain is less
    /// than 4Gb. So this value can't be as big as `usize::MAX`
    Bytes(usize),
    /// Read until delimiter, but no more than N bytes.
    ///
    /// Only static strings are support for delimiter now.
    ///
    /// `bytes_read` action gets passed `num` bytes before the delimeter, or
    /// in other words, the position of the delimiter in the buffer.
    /// The delimiter is guaranteed to be in the buffer too.
    Delimiter(&'static [u8], usize),
    /// Wait until no more than N bytes is in output buffer
    ///
    /// This is going to be used for several cases:
    ///
    /// 1. `Flush(0)` before closing the connection
    /// 2. `Flush(0)` to before receiving new request (if needed)
    /// 3. `Flush(N)` to wait when you can continue producing some data, this
    ///    allows TCP pushback. To be able not to put everything in output
    ///    buffer at once. Still probably more efficient than `Flush(0)`
    Flush(usize),
    /// Wait until deadline
    ///
    /// This useful for two cases:
    ///
    /// 1. Just wait before doing anything if required by business logic
    /// 2. Wait until `wakeup` happens or atimeout whatever comes first
    Sleep,
}

pub trait Protocol<C, S: StreamSocket>: Sized {
    type Seed: Any+Sized;
    /// Starting the protocol (e.g. accepted a socket)
    fn create(seed: Self::Seed, sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>;

    /// The action WaitBytes or WaitDelimiter is complete
    fn bytes_read(self, transport: &mut Transport,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>;

    /// The action Flush is complete
    fn bytes_flushed(self, transport: &mut Transport,
                     scope: &mut Scope<C>)
        -> Request<Self>;

    /// Timeout happened, which means either deadline reached in
    /// Bytes, Delimiter, Flush. Or Sleep has passed.
    fn timeout(self, scope: &mut Scope<C>) -> Request<Self>;

    /// Message received (from the main loop)
    fn wakeup(self, scope: &mut Scope<C>) -> Request<Self>;
}
<commit_msg>A little piece of docs<commit_after>use std::any::Any;
use rotor::Scope;

use {Transport, Request, StreamSocket};

// #[derive(Clone, Clone)]
// This could be Copy, but I think it could be implemented efficient enough
// without Copy and Clone. Probably we will enable them for the user code later
#[derive(Debug)]
pub enum Expectation {
    /// Read number of bytes
    ///
    /// The buffer that is passed to bytes_read might contain more bytes, but
    /// `num` will contain a number of bytes passed into `Bytes` constructor.
    ///
    /// Note that real number of bytes that `netbuf::Buf` might contain is less
    /// than 4Gb. So this value can't be as big as `usize::MAX`
    Bytes(usize),
    /// Read until delimiter, but no more than N bytes.
    ///
    /// Only static strings are support for delimiter now.
    ///
    /// `bytes_read` action gets passed `num` bytes before the delimeter, or
    /// in other words, the position of the delimiter in the buffer.
    /// The delimiter is guaranteed to be in the buffer too.
    Delimiter(&'static [u8], usize),
    /// Wait until no more than N bytes is in output buffer
    ///
    /// This is going to be used for several cases:
    ///
    /// 1. `Flush(0)` before closing the connection
    /// 2. `Flush(0)` to before receiving new request (if needed)
    /// 3. `Flush(N)` to wait when you can continue producing some data, this
    ///    allows TCP pushback. To be able not to put everything in output
    ///    buffer at once. Still probably more efficient than `Flush(0)`
    Flush(usize),
    /// Wait until deadline
    ///
    /// This useful for two cases:
    ///
    /// 1. Just wait before doing anything if required by business logic
    /// 2. Wait until `wakeup` happens or atimeout whatever comes first
    Sleep,
}

pub trait Protocol<C, S: StreamSocket>: Sized {
    type Seed: Any+Sized;
    /// Starting the protocol (e.g. accepted a socket)
    fn create(seed: Self::Seed, sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>;

    /// The action WaitBytes or WaitDelimiter is complete
    ///
    /// Note you don't have to consume input buffer. The data is in the
    /// transport, but you are free to ignore it. This may be useful for
    /// example to yield `Bytes(4)` to read the header size and then yield
    /// bigger value to read the whole header at once. But be careful, if
    /// you don't consume bytes you will repeatedly receive them again.
    fn bytes_read(self, transport: &mut Transport,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>;

    /// The action Flush is complete
    fn bytes_flushed(self, transport: &mut Transport,
                     scope: &mut Scope<C>)
        -> Request<Self>;

    /// Timeout happened, which means either deadline reached in
    /// Bytes, Delimiter, Flush. Or Sleep has passed.
    // TODO(tailhook) add Transport argument
    fn timeout(self, scope: &mut Scope<C>) -> Request<Self>;

    /// Message received (from the main loop)
    // TODO(tailhook) add Transport argument
    fn wakeup(self, scope: &mut Scope<C>) -> Request<Self>;
}
<|endoftext|>


<commit_before>use mio::{Buf, MutBuf};
use std::mem;
use std::io::Write;
use std::cmp;
use std::str;
use std::fmt;
use tendril;
use queue_backend::Message;

pub type ByteTendril = tendril::Tendril<tendril::fmt::Bytes, tendril::Atomic>;
pub type StrTendril = tendril::Tendril<tendril::fmt::UTF8, tendril::Atomic>;

#[derive(Eq, PartialEq, Debug)]
pub enum ProtocolError {
    Incomplete,
    Invalid(&'static str),
}

impl From<&'static str> for ProtocolError {
    fn from(from: &'static str) -> ProtocolError {
        ProtocolError::Invalid(from)
    }
}

pub type ProtocolResult<T> = Result<T, ProtocolError>;

#[derive(Clone)]
pub enum Value {
    Nil,
    Int(i64),
    Data(ByteTendril),
    Bulk(Vec<Value>),
    Status(StrTendril),
    Error(StrTendril),
    Message(Message),
}

impl Value {
    fn serialize_to(self, f: &mut Vec<u8>) {
        match self {
            Value::Nil =>
                write!(f, "$-1\r\n"),
            Value::Int(v) =>
                write!(f, ":{}\r\n", v),
            Value::Data(v) => {
                write!(f, "${}\r\n", v.len32()).unwrap();
                f.write_all(v.as_ref()).unwrap();
                write!(f, "\r\n")
            }
            Value::Bulk(b) => {
                write!(f, "*{}\r\n", b.len()).unwrap();
                for v in b {
                    v.serialize_to(f);
                }
                write!(f, "\r\n")
            },
            Value::Status(v) =>
                write!(f, "+{}\r\n", v.as_ref()),
            Value::Error(v) =>
                write!(f, "-{}\r\n", v.as_ref()),
            Value::Message(message) => {
                write!(f, "*2\r\n").unwrap();
                write!(f, ":{}\r\n${}\r\n", message.id(), message.len()).unwrap();
                f.write(message.body()).unwrap();
                write!(f, "\r\n")
            }
        }.unwrap()
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Value::Nil =>
                write!(f, "Nil"),
            Value::Int(v) =>
                write!(f, "Int({:?})", v),
            Value::Data(ref v) =>
                match str::from_utf8(v) {
                    Ok(s) => write!(f, "Data({:?})", s),
                    Err(_) => write!(f, "Data({:?})", v.as_ref())
                },
            Value::Bulk(ref b) => {
                try!(write!(f, "Bulk("));
                try!(f.debug_list().entries(b).finish());
                write!(f, ")")
            },
            Value::Status(ref v) =>
                write!(f, "Status({:?})", v.as_ref()),
            Value::Error(ref v) =>
                write!(f, "Error({:?})", v.as_ref()),
            Value::Message(_) =>
                write!(f, "Message(..)"),
        }
    }
}

#[derive(Debug)]
pub struct ResponseBuffer {
    body: Vec<u8>,
    bytes_written: usize,
}

impl ResponseBuffer {
    pub fn new() -> ResponseBuffer {
        ResponseBuffer {
            body: Vec::new(),
            bytes_written: 0,
        }
    }

    pub fn push_value(&mut self, value: Value) {
        value.serialize_to(&mut self.body);
    }
}

impl Buf for ResponseBuffer {
    fn remaining(&self) -> usize {
        self.body.len() - self.bytes_written
    }

    fn advance(&mut self, cnt: usize) {
        self.bytes_written += cnt;
        // FIXME: poor mans vecdeque
        if self.bytes_written == self.body.len() {
            self.body.clear();
            self.bytes_written = 0;
        }
    }

    fn bytes(&self) -> &[u8] {
        &self.body[self.bytes_written..]
    }
}

#[derive(Debug)]
pub struct RequestBuffer {
    body: ByteTendril,
    bytes_read: u32,
}

impl RequestBuffer {
    pub fn new() -> RequestBuffer {
        RequestBuffer {
            body: ByteTendril::new(),
            bytes_read: 0,
        }
    }

    pub fn pop_value(&mut self) -> ProtocolResult<Value> {
        let mut parser = Parser::new(self.body.subtendril(0, self.bytes_read));
        let parsed = parser.parse();
        if parsed.is_ok() {
            self.body = parser.body;
            self.bytes_read = self.body.len32();
        }
        parsed
    }
}

impl MutBuf for RequestBuffer {
    fn remaining(&self) -> usize {
        cmp::min(4 * 1024, self.body.len32() - self.bytes_read) as usize
    }

    fn advance(&mut self, cnt: usize) {
        self.bytes_read += cnt as u32
    }

    fn mut_bytes(&mut self) -> &mut [u8] {
        let cur_len = self.body.len32();
        if cur_len - self.bytes_read < 4 * 1024 {
            unsafe { self.body.set_len(cmp::max(4 * 1024, cur_len * 2)); }
        }
        &mut self.body[self.bytes_read as usize..]
    }
}

/// The internal redis response parser.
struct Parser {
    body: ByteTendril,
}

impl Parser {
    fn new<T: Into<ByteTendril>>(body: T) -> Parser {
        Parser {
            body: body.into()
        }
    }

    /// parses a single value out of the stream.  If there are multiple
    /// values you can call this multiple times.
    fn parse(&mut self) -> ProtocolResult<Value> {
        match try!(self.read_byte()) {
            b'+' => self.parse_status(),
            b':' => self.parse_int(),
            b'$' => self.parse_data(),
            b'*' => self.parse_bulk(),
            b'-' => self.parse_error(),
            _ => Err("Invalid response when parsing value".into()),
        }
    }

    #[inline]
    fn read_byte(&mut self) -> ProtocolResult<u8> {
        if self.body.len() >= 1 {
            let byte = self.body[0];
            self.body.pop_front(1);
            return Ok(byte)
        } else {
            Err(ProtocolError::Incomplete)
        }
    }

    #[inline]
    fn read(&mut self, bytes: usize) -> ProtocolResult<ByteTendril> {
        if self.body.len() >= bytes {
            let (first_part, second_part) = Self::split_at(self.body.clone(), bytes);
            self.body = second_part;
            return Ok(first_part)
        } else {
            Err(ProtocolError::Incomplete)
        }
    }

    fn split_at(mut tendril: ByteTendril, position: usize) -> (ByteTendril, ByteTendril) {
        let offset = position as u32;
        let len = tendril.len32() - offset;
        let second_part = tendril.subtendril(offset, len);
        tendril.pop_back(len);
        (tendril, second_part)
    }

    fn read_line(&mut self) -> ProtocolResult<ByteTendril> {
        let nl_pos = match self.body.iter().position(|&b| b == b'\r') {
            Some(nl_pos) => nl_pos,
            None => return Err("Missing line separator".into()),
        };
        let (first_part, second_part) = Self::split_at(try!(self.read(nl_pos + 2)), nl_pos);
        if second_part.as_ref() != b"\r\n" {
            return Err("Invalid line separator".into())
        }

        Ok(first_part)
    }

    fn read_string_line(&mut self) -> ProtocolResult<StrTendril> {
        let line = try!(self.read_line());
        match str::from_utf8(line.as_ref()) {
            Ok(_) => Ok(unsafe { mem::transmute(line) }),
            Err(_) => Err("Expected valid string, got garbage".into())
        }
    }

    fn read_int_line(&mut self) -> ProtocolResult<i64> {
        let line = try!(self.read_line());
        let line_str = unsafe { str::from_utf8_unchecked(line.as_ref()) };
        match line_str.parse::<i64>() {
            Err(_) => Err("Expected integer, got garbage".into()),
            Ok(value) => Ok(value)
        }
    }

    fn parse_status(&mut self) -> ProtocolResult<Value> {
        let line = try!(self.read_string_line());
        Ok(Value::Status(line))
    }

    fn parse_int(&mut self) -> ProtocolResult<Value> {
        Ok(Value::Int(try!(self.read_int_line())))
    }

    fn parse_data(&mut self) -> ProtocolResult<Value> {
        let length = try!(self.read_int_line());
        if length < 0 {
            Ok(Value::Nil)
        } else {
            let length = length as usize;
            let (data, line_sep) = Self::split_at(try!(self.read(length + 2)), length);
            if line_sep.as_ref() != b"\r\n" {
                return Err("Invalid line separator".into())
            }
            Ok(Value::Data(data))
        }
    }

    fn parse_bulk(&mut self) -> ProtocolResult<Value> {
        let length = try!(self.read_int_line());
        if length < 0 {
            Ok(Value::Nil)
        } else {
            let mut rv = Vec::with_capacity(length as usize);
            for _ in 0..length {
                let value = try!(self.parse());
                rv.push(value);
            }
            Ok(Value::Bulk(rv))
        }
    }

    fn parse_error(&mut self) -> ProtocolResult<Value> {
        let line = try!(self.read_string_line());
        Ok(Value::Error(line))
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestBuffer, ProtocolResult, ProtocolError, Parser, Value};
    use std::io::Write;
    use mio::MutBuf;

    fn parse(slice: &[u8]) -> ProtocolResult<Value> {
        Parser::new(slice).parse()
    }

    macro_rules! assert_eq_repr {
        ($left:expr , $right:expr) => ({
            match (format!("{:?}", &$left), format!("{:?}", &$right)) {
                (left_val, right_val) => {
                    if !(left_val == right_val) {
                        panic!("repr assertion failed: `(debug(left) == debug(right))` \
                               (left: `{:?}`, right: `{:?}`)", left_val, right_val)
                    }
                }
            }
        })
    }

    #[test]
    fn parse_incomplete() {
        let r = parse(b"*2\r\n$3\r\nfoo");
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
    }

    #[test]
    fn parse_error() {
        let r = parse(b"-foo\r\n");
        assert_eq_repr!(r.unwrap(), Value::Error("foo".into()));

        let r = parse(b"-invalid line sep\r\r");
        assert!(if let ProtocolError::Invalid(_) = r.unwrap_err() {true} else {false});
    }

    #[test]
    fn parse_valid_bulk() {
        let r = parse(b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n");
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(
            r.unwrap(),
            Value::Bulk(vec![
                Value::Data(b"foo".as_ref().into()),
                Value::Data(b"barz".as_ref().into())
            ])
        );
    }

    #[test]
    fn parser_multiple2() {
        let mut parser = Parser::new(
            b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n".as_ref(),
        );
        for _ in 0..2 {
            let r = parser.parse();
            assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
            assert_eq_repr!(
                r.unwrap(),
                Value::Bulk(vec![
                    Value::Data(b"foo".as_ref().into()),
                    Value::Data(b"barz".as_ref().into())
                ])
            );
        }
        let r = parser.parse();
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
    }

    #[test]
    fn request_parse() {
        let mut req = RequestBuffer::new();
        req.mut_bytes().write_all(b"+OK\r").unwrap();
        req.advance(4);
        let r = req.pop_value();
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
        req.mut_bytes().write_all(b"\n:100\r\n").unwrap();
        req.advance(7);
        let r = req.pop_value();
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(r.unwrap(), Value::Status("OK".into()));
        let r = req.pop_value();
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(r.unwrap(), Value::Int(100));
    }
}<commit_msg>fix protocol array response<commit_after>use mio::{Buf, MutBuf};
use std::mem;
use std::io::Write;
use std::cmp;
use std::str;
use std::fmt;
use tendril;
use queue_backend::Message;

pub type ByteTendril = tendril::Tendril<tendril::fmt::Bytes, tendril::Atomic>;
pub type StrTendril = tendril::Tendril<tendril::fmt::UTF8, tendril::Atomic>;

#[derive(Eq, PartialEq, Debug)]
pub enum ProtocolError {
    Incomplete,
    Invalid(&'static str),
}

impl From<&'static str> for ProtocolError {
    fn from(from: &'static str) -> ProtocolError {
        ProtocolError::Invalid(from)
    }
}

pub type ProtocolResult<T> = Result<T, ProtocolError>;

#[derive(Clone)]
pub enum Value {
    Nil,
    Int(i64),
    Data(ByteTendril),
    Bulk(Vec<Value>),
    Status(StrTendril),
    Error(StrTendril),
    Message(Message),
}

impl Value {
    fn serialize_to(self, f: &mut Vec<u8>) {
        match self {
            Value::Nil =>
                write!(f, "$-1\r\n"),
            Value::Int(v) =>
                write!(f, ":{}\r\n", v),
            Value::Data(v) => {
                write!(f, "${}\r\n", v.len32()).unwrap();
                f.write_all(v.as_ref()).unwrap();
                write!(f, "\r\n")
            }
            Value::Bulk(b) => {
                write!(f, "*{}\r\n", b.len()).unwrap();
                for v in b {
                    v.serialize_to(f);
                }
                Ok(())
            },
            Value::Status(v) =>
                write!(f, "+{}\r\n", v.as_ref()),
            Value::Error(v) =>
                write!(f, "-{}\r\n", v.as_ref()),
            Value::Message(message) => {
                write!(f, "*2\r\n").unwrap();
                write!(f, ":{}\r\n${}\r\n", message.id(), message.len()).unwrap();
                f.write(message.body()).unwrap();
                write!(f, "\r\n")
            }
        }.unwrap()
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Value::Nil =>
                write!(f, "Nil"),
            Value::Int(v) =>
                write!(f, "Int({:?})", v),
            Value::Data(ref v) =>
                match str::from_utf8(v) {
                    Ok(s) => write!(f, "Data({:?})", s),
                    Err(_) => write!(f, "Data({:?})", v.as_ref())
                },
            Value::Bulk(ref b) => {
                try!(write!(f, "Bulk("));
                try!(f.debug_list().entries(b).finish());
                write!(f, ")")
            },
            Value::Status(ref v) =>
                write!(f, "Status({:?})", v.as_ref()),
            Value::Error(ref v) =>
                write!(f, "Error({:?})", v.as_ref()),
            Value::Message(_) =>
                write!(f, "Message(..)"),
        }
    }
}

#[derive(Debug)]
pub struct ResponseBuffer {
    body: Vec<u8>,
    bytes_written: usize,
}

impl ResponseBuffer {
    pub fn new() -> ResponseBuffer {
        ResponseBuffer {
            body: Vec::new(),
            bytes_written: 0,
        }
    }

    pub fn push_value(&mut self, value: Value) {
        value.serialize_to(&mut self.body);
    }
}

impl Buf for ResponseBuffer {
    fn remaining(&self) -> usize {
        self.body.len() - self.bytes_written
    }

    fn advance(&mut self, cnt: usize) {
        self.bytes_written += cnt;
        // FIXME: poor mans vecdeque
        if self.bytes_written == self.body.len() {
            self.body.clear();
            self.bytes_written = 0;
        }
    }

    fn bytes(&self) -> &[u8] {
        &self.body[self.bytes_written..]
    }
}

#[derive(Debug)]
pub struct RequestBuffer {
    body: ByteTendril,
    bytes_read: u32,
}

impl RequestBuffer {
    pub fn new() -> RequestBuffer {
        RequestBuffer {
            body: ByteTendril::new(),
            bytes_read: 0,
        }
    }

    pub fn pop_value(&mut self) -> ProtocolResult<Value> {
        let mut parser = Parser::new(self.body.subtendril(0, self.bytes_read));
        let parsed = parser.parse();
        if parsed.is_ok() {
            self.body = parser.body;
            self.bytes_read = self.body.len32();
        }
        parsed
    }
}

impl MutBuf for RequestBuffer {
    fn remaining(&self) -> usize {
        cmp::min(4 * 1024, self.body.len32() - self.bytes_read) as usize
    }

    fn advance(&mut self, cnt: usize) {
        self.bytes_read += cnt as u32
    }

    fn mut_bytes(&mut self) -> &mut [u8] {
        let cur_len = self.body.len32();
        if cur_len - self.bytes_read < 4 * 1024 {
            unsafe { self.body.set_len(cmp::max(4 * 1024, cur_len * 2)); }
        }
        &mut self.body[self.bytes_read as usize..]
    }
}

/// The internal redis response parser.
struct Parser {
    body: ByteTendril,
}

impl Parser {
    fn new<T: Into<ByteTendril>>(body: T) -> Parser {
        Parser {
            body: body.into()
        }
    }

    /// parses a single value out of the stream.  If there are multiple
    /// values you can call this multiple times.
    fn parse(&mut self) -> ProtocolResult<Value> {
        match try!(self.read_byte()) {
            b'+' => self.parse_status(),
            b':' => self.parse_int(),
            b'$' => self.parse_data(),
            b'*' => self.parse_bulk(),
            b'-' => self.parse_error(),
            _ => Err("Invalid response when parsing value".into()),
        }
    }

    #[inline]
    fn read_byte(&mut self) -> ProtocolResult<u8> {
        if self.body.len() >= 1 {
            let byte = self.body[0];
            self.body.pop_front(1);
            return Ok(byte)
        } else {
            Err(ProtocolError::Incomplete)
        }
    }

    #[inline]
    fn read(&mut self, bytes: usize) -> ProtocolResult<ByteTendril> {
        if self.body.len() >= bytes {
            let (first_part, second_part) = Self::split_at(self.body.clone(), bytes);
            self.body = second_part;
            return Ok(first_part)
        } else {
            Err(ProtocolError::Incomplete)
        }
    }

    fn split_at(mut tendril: ByteTendril, position: usize) -> (ByteTendril, ByteTendril) {
        let offset = position as u32;
        let len = tendril.len32() - offset;
        let second_part = tendril.subtendril(offset, len);
        tendril.pop_back(len);
        (tendril, second_part)
    }

    fn read_line(&mut self) -> ProtocolResult<ByteTendril> {
        let nl_pos = match self.body.iter().position(|&b| b == b'\r') {
            Some(nl_pos) => nl_pos,
            None => return Err("Missing line separator".into()),
        };
        let (first_part, second_part) = Self::split_at(try!(self.read(nl_pos + 2)), nl_pos);
        if second_part.as_ref() != b"\r\n" {
            return Err("Invalid line separator".into())
        }

        Ok(first_part)
    }

    fn read_string_line(&mut self) -> ProtocolResult<StrTendril> {
        let line = try!(self.read_line());
        match str::from_utf8(line.as_ref()) {
            Ok(_) => Ok(unsafe { mem::transmute(line) }),
            Err(_) => Err("Expected valid string, got garbage".into())
        }
    }

    fn read_int_line(&mut self) -> ProtocolResult<i64> {
        let line = try!(self.read_line());
        let line_str = unsafe { str::from_utf8_unchecked(line.as_ref()) };
        match line_str.parse::<i64>() {
            Err(_) => Err("Expected integer, got garbage".into()),
            Ok(value) => Ok(value)
        }
    }

    fn parse_status(&mut self) -> ProtocolResult<Value> {
        let line = try!(self.read_string_line());
        Ok(Value::Status(line))
    }

    fn parse_int(&mut self) -> ProtocolResult<Value> {
        Ok(Value::Int(try!(self.read_int_line())))
    }

    fn parse_data(&mut self) -> ProtocolResult<Value> {
        let length = try!(self.read_int_line());
        if length < 0 {
            Ok(Value::Nil)
        } else {
            let length = length as usize;
            let (data, line_sep) = Self::split_at(try!(self.read(length + 2)), length);
            if line_sep.as_ref() != b"\r\n" {
                return Err("Invalid line separator".into())
            }
            Ok(Value::Data(data))
        }
    }

    fn parse_bulk(&mut self) -> ProtocolResult<Value> {
        let length = try!(self.read_int_line());
        if length < 0 {
            Ok(Value::Nil)
        } else {
            let mut rv = Vec::with_capacity(length as usize);
            for _ in 0..length {
                let value = try!(self.parse());
                rv.push(value);
            }
            Ok(Value::Bulk(rv))
        }
    }

    fn parse_error(&mut self) -> ProtocolResult<Value> {
        let line = try!(self.read_string_line());
        Ok(Value::Error(line))
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestBuffer, ProtocolResult, ProtocolError, Parser, Value};
    use std::io::Write;
    use mio::MutBuf;

    fn parse(slice: &[u8]) -> ProtocolResult<Value> {
        Parser::new(slice).parse()
    }

    macro_rules! assert_eq_repr {
        ($left:expr , $right:expr) => ({
            match (format!("{:?}", &$left), format!("{:?}", &$right)) {
                (left_val, right_val) => {
                    if !(left_val == right_val) {
                        panic!("repr assertion failed: `(debug(left) == debug(right))` \
                               (left: `{:?}`, right: `{:?}`)", left_val, right_val)
                    }
                }
            }
        })
    }

    #[test]
    fn parse_incomplete() {
        let r = parse(b"*2\r\n$3\r\nfoo");
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
    }

    #[test]
    fn parse_error() {
        let r = parse(b"-foo\r\n");
        assert_eq_repr!(r.unwrap(), Value::Error("foo".into()));

        let r = parse(b"-invalid line sep\r\r");
        assert!(if let ProtocolError::Invalid(_) = r.unwrap_err() {true} else {false});
    }

    #[test]
    fn parse_valid_bulk() {
        let r = parse(b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n");
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(
            r.unwrap(),
            Value::Bulk(vec![
                Value::Data(b"foo".as_ref().into()),
                Value::Data(b"barz".as_ref().into())
            ])
        );
    }

    #[test]
    fn parser_multiple2() {
        let mut parser = Parser::new(
            b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n".as_ref(),
        );
        for _ in 0..2 {
            let r = parser.parse();
            assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
            assert_eq_repr!(
                r.unwrap(),
                Value::Bulk(vec![
                    Value::Data(b"foo".as_ref().into()),
                    Value::Data(b"barz".as_ref().into())
                ])
            );
        }
        let r = parser.parse();
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
    }

    #[test]
    fn request_parse() {
        let mut req = RequestBuffer::new();
        req.mut_bytes().write_all(b"+OK\r").unwrap();
        req.advance(4);
        let r = req.pop_value();
        assert_eq_repr!(r.unwrap_err(), ProtocolError::Incomplete);
        req.mut_bytes().write_all(b"\n:100\r\n").unwrap();
        req.advance(7);
        let r = req.pop_value();
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(r.unwrap(), Value::Status("OK".into()));
        let r = req.pop_value();
        assert!(r.is_ok(), "{:?} not ok", r.unwrap_err());
        assert_eq_repr!(r.unwrap(), Value::Int(100));
    }
}<|endoftext|>


<commit_before>service! {
    rpc AppendEntries(term: u64, leaderId: u64, prev_log_id: u64, prev_log_term: u64, entries: Vec<Vec<u8>>, leader_commit: u64) -> u64; //Err for not success
    rpc RequestVote(term: u64, candidate_id: u64, last_log_id: u64, last_log_term: u64) -> (u64, bool); // term, voteGranted
    rpc InstallSnapshot(term: u64, leader_id: u64, lasr_included_index: u64, last_included_term: u64, data: Vec<u8>, done: bool) -> u64;
}

//TODO: Use higher order macro to merge with rpc service! macro when possible to do this in Rust.
//Current major problem is inner repeated macro will be recognized as outer macro which breaks expand

#[macro_export]
macro_rules! raft {
    (
        $(
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) $(-> $out:ty)* $(| $error:ty)*;
        )*
    ) => {
        raft! {{
            $(
                $(#[$attr])*
                def $smt $fn_name( $( $arg : $in_ ),* ) $(-> $out)* $(| $error)*;
            )*
        }}
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ); // No return, no error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        raft! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> () | ();
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty; //return, no error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        raft! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> $out | ();
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) | $error:ty; //no return, error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        raft! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> () | $error;
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty | $error:ty; //return, error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        raft! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> $out | $error;
        }
    };
    (
        {} // all expanded
        $(
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident ( $( $arg:ident : $in_:ty ),* ) -> $out:ty | $error:ty;
        )*
    ) => {
        use std;
        use byteorder::{ByteOrder, LittleEndian};
        use bincode::{SizeLimit, serde as bincode};

        mod sm_args {
            $(
                #[derive(Serialize, Deserialize, Debug)]
                pub struct $fn_name {
                    $(pub $arg:$in_),*
                }
            )*
        }
        mod sm_returns {
            $(
                #[derive(Serialize, Deserialize, Debug)]
                pub enum $fn_name {
                    Result($out),
                    Error($error)
                }
            )*
        }
        pub trait StateMachine {
           $(
                $(#[$attr])*
                fn $fn_name(&self, $($arg:$in_),*) -> std::result::Result<$out, $error>;
           )*
           fn snapshot(&self) -> Vec<u8>;
//           fn dispatch(&self, fn_id: u64, &data: Vec<u8>) -> Option<Vec<u8>> {
//                match fn_id as usize {
//                    $(hash_ident!($fn_name) => {
//                        let decoded: sm_args::$fn_name = bincode::deserialize(&data).unwrap();
//                        let f_result = self.$fn_name($(decoded.$arg),*);
//                        let s_result = match f_result {
//                            Ok(v) => sm_returns::$fn_name::Result(v),
//                            Err(e) => sm_returns::$fn_name::Error(e)
//                        };
//                        Some(bincode::serialize(&s_result, SizeLimit::Infinite).unwrap())
//                    }),*
//                    _ => {
//                        println!("Undefined function id: {}", fn_id);
//                        None
//                    }
//                }
//           }
        }
    };
}

#[cfg(test)]
mod syntax_test {
    raft! {
        def qry get (key: u64) -> String | ();
        def cmd test(a: u32, b: u32) -> bool;
    }
}

<commit_msg>define user state machine<commit_after>trait RaftMsg {
    fn encode(&self) -> (usize, Vec<u8>);
}

trait UserStateMachine {
    fn snapshot(&self) -> Vec<u8>;
}

service! {
    rpc AppendEntries(term: u64, leaderId: u64, prev_log_id: u64, prev_log_term: u64, entries: Vec<Vec<u8>>, leader_commit: u64) -> u64; //Err for not success
    rpc RequestVote(term: u64, candidate_id: u64, last_log_id: u64, last_log_term: u64) -> (u64, bool); // term, voteGranted
    rpc InstallSnapshot(term: u64, leader_id: u64, lasr_included_index: u64, last_included_term: u64, data: Vec<u8>, done: bool) -> u64;
}

//TODO: Use higher order macro to merge with rpc service! macro when possible to do this in Rust.
//Current major problem is inner repeated macro will be recognized as outer macro which breaks expand

#[macro_export]
macro_rules! state_machine {
    (
        $(
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) $(-> $out:ty)* $(| $error:ty)*;
        )*
    ) => {
        state_machine! {{
            $(
                $(#[$attr])*
                def $smt $fn_name( $( $arg : $in_ ),* ) $(-> $out)* $(| $error)*;
            )*
        }}
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ); // No return, no error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        state_machine! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> () | ();
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty; //return, no error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        state_machine! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> $out | ();
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) | $error:ty; //no return, error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        state_machine! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> () | $error;
        }
    };
    (
        {
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty | $error:ty; //return, error

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        state_machine! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            def $smt $fn_name( $( $arg : $in_ ),* ) -> $out | $error;
        }
    };
    (
        {} // all expanded
        $(
            $(#[$attr:meta])*
            def $smt:ident $fn_name:ident ( $( $arg:ident : $in_:ty ),* ) -> $out:ty | $error:ty;
        )*
    ) => {
        use std;
        use byteorder::{ByteOrder, LittleEndian};
        use bincode::{SizeLimit, serde as bincode};

        mod sm_args {
            use super::*;
            $(
                #[derive(Serialize, Deserialize, Debug)]
                pub struct $fn_name {
                    $(pub $arg:$in_),*
                }
                impl $crate::raft::RaftMsg for $fn_name {
                    fn encode(&self) -> (usize, Vec<u8>) {
                        (
                            hash_ident!($fn_name),
                            bincode::serialize(&self, SizeLimit::Infinite).unwrap()
                        )
                    }
                }

            )*
        }
        mod sm_returns {
            $(
                #[derive(Serialize, Deserialize, Debug)]
                pub enum $fn_name {
                    Result($out),
                    Error($error)
                }
            )*
        }
        pub trait StateMachine: $crate::raft::UserStateMachine {
           $(
                $(#[$attr])*
                fn $fn_name(&self, $($arg:$in_),*) -> std::result::Result<$out, $error>;
           )*

//           fn dispatch(&self, fn_id: u64, &data: Vec<u8>) -> Option<Vec<u8>> {
//                match fn_id as usize {
//                    $(hash_ident!($fn_name) => {
//                        let decoded: sm_args::$fn_name = bincode::deserialize(&data).unwrap();
//                        let f_result = self.$fn_name($(decoded.$arg),*);
//                        let s_result = match f_result {
//                            Ok(v) => sm_returns::$fn_name::Result(v),
//                            Err(e) => sm_returns::$fn_name::Error(e)
//                        };
//                        Some(bincode::serialize(&s_result, SizeLimit::Infinite).unwrap())
//                    }),*
//                    _ => {
//                        println!("Undefined function id: {}", fn_id);
//                        None
//                    }
//                }
//           }
        }
    };
}

#[cfg(test)]
mod syntax_test {
    state_machine! {
        def qry get (key: u64) -> String | ();
        def cmd test(a: u32, b: u32) -> bool;
    }
}

<|endoftext|>