<commit_before>use rocket::request::{FromForm, FormItems};

#[derive(Debug)]
pub struct StatementStructResult {
    sql: String,
}
pub struct WhereStructResult {
    sql: String,
    values: Vec<String>,
}

// My Results
type QueryOpResult = Result<&'static str, String>;
type StatementResult = Result<StatementStructResult, String>;
type WhereResult = Result<WhereStructResult, String>;

// TODO: Move there later
pub struct Params {
    pub k: String,
    pub v: String,
}
// implement QueryString
pub struct QueryString {
    pub query: Vec<Params>,
}

impl<'f> FromForm<'f> for QueryString {
    type Error = ();

    fn from_form(items: &mut FormItems<'f>, _: bool) -> Result<QueryString, ()> {
        let mut qs: QueryString = QueryString { query: Vec::new() };
        for (k, v) in items {
            qs.query.push(Params {
                k: k.to_string(),
                v: v.url_decode().unwrap(),
            });
        }
        Ok(qs)
    }
}

pub fn query_operator(qop: &str) -> QueryOpResult {
    match &qop[..] {
        "$eq" => Ok("="),
        "$ne" => Ok("!="),
        "$gt" => Ok(">"),
        "$lt" => Ok("<"),
        "$gte" => Ok(">="),
        "$lte" => Ok("<="),
        "$in" => Ok("IN"),
        "$nin" => Ok("NOT IN"),
        "$notnull" => Ok("IS NOT NULL"),
        "$null" => Ok("IS NULL"),
        _ => Err(format!("operator {} not found", qop)),
    }
}

fn collect_params(v: &Vec<Params>, collect_values: bool) -> Result<Vec<String>, String> {
    let mut pid: i32 = 0;
    v.iter()
        .map(|param| {
            pid = pid + 1;
            let result = match param.v.contains(".") {
                true => {
                    let value_splited = param.v.split(".").collect::<Vec<&str>>();
                    let operator = match query_operator(value_splited[0]) {
                        Ok(op) => op,
                        Err(e) => return Err(e),
                    };
                    (operator, value_splited[1])
                }
                _ => ("=", param.v.as_str()),
            };
            if collect_values {
                Ok(format!("{}", result.1))
            } else {
                Ok(format!("{}{}${}", param.k, result.0, pid))
            }
        })
        .collect()
}

// generic function to process statements: _select, _groupby, _order, _count
fn process_statement<F>(
    qs: &QueryString,
    stm: &str,
    stm_default: &str,
    stm_fmt: &str,
    stm_fn: F,
) -> StatementResult
where
    F: Fn(&str) -> String,
{
    let mut statement_syntax: String = String::from(stm_default);
    let statement_params = qs.query.iter().find(|p| p.k == String::from(stm));
    let columns = match statement_params {
        Some(param) => stm_fn(&param.v),
        _ => return Err("".to_string()),
    };
    if columns != "" {
        statement_syntax = format!("{}", String::from(stm_fmt).replace("{}", &columns));
    }
    Ok(StatementStructResult { sql: statement_syntax })
}

// Get QueryString and return chunck WHERE statement with values
pub fn ext_where(qs: QueryString) -> WhereResult {
    let where_syntax: String;
    let ref iter_params = qs.query;
    let where_values = match collect_params(iter_params, true) {
        Ok(result) => result,
        Err(e) => return Err(e.to_string()),
    };
    let where_fields = match collect_params(iter_params, false) {
        Ok(result) => result,
        Err(e) => return Err(e.to_string()),
    };

    if where_fields.len() > 1 {
        where_syntax = format!("WHERE {}", where_fields.join(" AND "));
    } else {
        where_syntax = format!("WHERE {}", where_fields.join(" "));
    }

    Ok(WhereStructResult {
        sql: where_syntax,
        values: where_values,
    })
}

// Get QueryString and return SELECT statement
pub fn ext_select(qs: QueryString) -> StatementResult {
    Ok(
        process_statement(&qs, "_select", "SELECT * FROM", "SELECT {} FROM", |x| {
            String::from(x)
        }).unwrap(),
    )
}

// Get QueryString and return COUNT clause statement
pub fn ext_count(qs: QueryString) -> StatementResult {
    // FIXME: Need handle errors
    Ok(
        process_statement(&qs, "_count", "", "SELECT COUNT({}) FROM", |x| {
            let vs = x.split(",").collect::<Vec<&str>>();
            let result = if vs.len() > 1 { "" } else { x };
            String::from(result)
        }).unwrap(),
    )
}

// Get QueryString and return chunk ORDER BY statement
pub fn ext_order(qs: QueryString) -> StatementResult {
    Ok(
        process_statement(&qs, "_order", "", "ORDER BY {}", |x| {
            let vspl = &x.split(",").collect::<Vec<&str>>();
            let od: Vec<String> = vspl.iter()
                .map(|param| {
                    let mut s = String::new();
                    s.push_str(param); // omg
                    if s.starts_with("-") {
                        s.remove(0);
                        s.push_str(" DESC")
                    }
                    format!("{}", s)
                })
                .collect();
            format!("{}", od.join(","))
        }).unwrap(),
    )

}

// Get QueryString and return chunk GROUP BY statement
pub fn ext_groupby(qs: QueryString) -> StatementResult {
    Ok(
        process_statement(&qs, "_groupby", "", "GROUP BY {}", |x| String::from(x)).unwrap(),
    )
}

#[cfg(test)]
mod tests {
    use postgresql::commands::*;

    #[test]
    fn get_opetators() {
        use std::collections::HashMap;

        let mut test_cases = HashMap::new();
        test_cases.insert("$eq", "=");
        test_cases.insert("$ne", "!=");
        test_cases.insert("$gt", ">");
        test_cases.insert("$gte", ">=");
        test_cases.insert("$lt", "<");
        test_cases.insert("$lte", "<=");
        test_cases.insert("$in", "IN");
        test_cases.insert("$nin", "NOT IN");
        test_cases.insert("$notnull", "IS NOT NULL");
        test_cases.insert("$null", "IS NULL");

        for (op, expected) in &test_cases {
            match query_operator(&op.to_string()) {
                Ok(x) => assert_eq!(expected.to_string(), x),
                Err(e) => assert_eq!("", e),
            }
        }
    }

    #[test]
    fn get_opetators_error() {
        let invalid = "!tn".to_string();
        match query_operator(&invalid) {
            Ok(_) => println!("no result"),
            Err(e) => assert_eq!(format!("operator {} not found", invalid), e),
        }
    }

    #[test]
    fn where_by_request_one_querystring() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "5".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id=$1".to_string());
        assert_eq!(result.values, vec!["5".to_string()])
    }

    #[test]
    fn where_by_request_n_querystring() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id=$1 AND name=$2".to_string());
        assert_eq!(result.values, vec!["5".to_string(), "goga".to_string()])
    }

    #[test]
    fn where_by_request_with_query_operators() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "$ne.5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id!=$1 AND name=$2".to_string());
        assert_eq!(result.values, vec!["5".to_string(), "goga".to_string()])
    }

    #[test]
    fn where_by_request_with_query_invalid_operators() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "$nt.5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_where(qs) {
            Err(e) => assert_eq!(e, "operator $nt not found"),
            Ok(_) => println!("no result"),
        }
    }

    #[test]
    fn select_by_request_one_column() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(qs).unwrap();
        assert_eq!(result.sql, "SELECT name FROM");
    }

    #[test]
    fn select_by_request_n_column() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(qs).unwrap();
        assert_eq!(result.sql, "SELECT name,age FROM");
    }

    #[test]
    fn select_by_request_n_empty_value() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(qs).unwrap();
        assert_eq!(result.sql, "SELECT * FROM");
    }

    #[test]
    fn count_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(qs).unwrap();
        assert_eq!(result.sql, "SELECT COUNT(name) FROM");
    }

    #[test]
    fn count_by_request_all_fields() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "*".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(qs).unwrap();
        assert_eq!(result.sql, "SELECT COUNT(*) FROM");
    }

    #[test]
    fn count_by_request_two_fields() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_count(qs) {
            Err(e) => assert_eq!(e, "could not use more than one column in count function"),
            Ok(s) => assert_eq!(s.sql, ""),
        }
    }

    #[test]
    fn count_by_request_empty_value() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(qs).unwrap();
        assert_eq!(result.sql, "")
    }

    #[test]
    fn order_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name")
    }

    #[test]
    fn order_by_request_desc() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "-name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name DESC")
    }

    #[test]
    fn order_by_request_empty_params() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "")
    }

    #[test]
    fn order_by_request_multiples_columns() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name,age")
    }

    #[test]
    fn order_by_request_multiples_columns_with_orders() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name,-age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name,age DESC")
    }

    #[test]
    fn groupby_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_groupby".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_groupby(qs).unwrap();
        assert_eq!(result.sql, "GROUP BY name")
    }

    #[test]
    fn groupby_by_request_empty_param() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: String::from("_groupby"),
            v: String::from(""),
        });

        let qs = QueryString { query: hm };
        let result = ext_groupby(qs).unwrap();
        assert_eq!(result.sql, "")
    }
}
<commit_msg>Use reference to QueryString<commit_after>use rocket::request::{FromForm, FormItems};

#[derive(Debug)]
pub struct StatementStructResult {
    sql: String,
}
pub struct WhereStructResult {
    sql: String,
    values: Vec<String>,
}

// My Results
type QueryOpResult = Result<&'static str, String>;
type StatementResult = Result<StatementStructResult, String>;
type WhereResult = Result<WhereStructResult, String>;

// TODO: Move there later
pub struct Params {
    pub k: String,
    pub v: String,
}
// implement QueryString
pub struct QueryString {
    pub query: Vec<Params>,
}

impl<'f> FromForm<'f> for QueryString {
    type Error = ();

    fn from_form(items: &mut FormItems<'f>, _: bool) -> Result<QueryString, ()> {
        let mut qs: QueryString = QueryString { query: Vec::new() };
        for (k, v) in items {
            qs.query.push(Params {
                k: k.to_string(),
                v: v.url_decode().unwrap(),
            });
        }
        Ok(qs)
    }
}

pub fn query_operator(qop: &str) -> QueryOpResult {
    match &qop[..] {
        "$eq" => Ok("="),
        "$ne" => Ok("!="),
        "$gt" => Ok(">"),
        "$lt" => Ok("<"),
        "$gte" => Ok(">="),
        "$lte" => Ok("<="),
        "$in" => Ok("IN"),
        "$nin" => Ok("NOT IN"),
        "$notnull" => Ok("IS NOT NULL"),
        "$null" => Ok("IS NULL"),
        _ => Err(format!("operator {} not found", qop)),
    }
}

fn collect_params(v: &Vec<Params>, collect_values: bool) -> Result<Vec<String>, String> {
    let mut pid: i32 = 0;
    v.iter()
        .map(|param| {
            pid = pid + 1;
            let result = match param.v.contains(".") {
                true => {
                    let value_splited = param.v.split(".").collect::<Vec<&str>>();
                    let operator = match query_operator(value_splited[0]) {
                        Ok(op) => op,
                        Err(e) => return Err(e),
                    };
                    (operator, value_splited[1])
                }
                _ => ("=", param.v.as_str()),
            };
            if collect_values {
                Ok(format!("{}", result.1))
            } else {
                Ok(format!("{}{}${}", param.k, result.0, pid))
            }
        })
        .collect()
}

// generic function to process statements: _select, _groupby, _order, _count
fn process_statement<F>(
    qs: &QueryString,
    stm: &str,
    stm_default: &str,
    stm_fmt: &str,
    stm_fn: F,
) -> StatementResult
where
    F: Fn(&str) -> String,
{
    let mut statement_syntax: String = String::from(stm_default);
    let statement_params = qs.query.iter().find(|p| p.k == String::from(stm));
    let columns = match statement_params {
        Some(param) => stm_fn(&param.v),
        _ => return Err("".to_string()),
    };
    if columns != "" {
        statement_syntax = format!("{}", String::from(stm_fmt).replace("{}", &columns));
    }
    Ok(StatementStructResult { sql: statement_syntax })
}

// Get QueryString and return chunck WHERE statement with values
pub fn ext_where(qs: &QueryString) -> WhereResult {
    let where_syntax: String;
    let ref iter_params = qs.query;
    let where_values = match collect_params(iter_params, true) {
        Ok(result) => result,
        Err(e) => return Err(e.to_string()),
    };
    let where_fields = match collect_params(iter_params, false) {
        Ok(result) => result,
        Err(e) => return Err(e.to_string()),
    };

    if where_fields.len() > 1 {
        where_syntax = format!("WHERE {}", where_fields.join(" AND "));
    } else {
        where_syntax = format!("WHERE {}", where_fields.join(" "));
    }

    Ok(WhereStructResult {
        sql: where_syntax,
        values: where_values,
    })
}

// Get QueryString and return SELECT statement
pub fn ext_select(qs: &QueryString) -> StatementResult {
    Ok(
        process_statement(qs, "_select", "SELECT * FROM", "SELECT {} FROM", |x| {
            String::from(x)
        }).unwrap(),
    )
}

// Get QueryString and return COUNT clause statement
pub fn ext_count(qs: &QueryString) -> StatementResult {
    // FIXME: Need handle errors
    Ok(
        process_statement(qs, "_count", "", "SELECT COUNT({}) FROM", |x| {
            let vs = x.split(",").collect::<Vec<&str>>();
            let result = if vs.len() > 1 { "" } else { x };
            String::from(result)
        }).unwrap(),
    )
}

// Get QueryString and return chunk ORDER BY statement
pub fn ext_order(qs: QueryString) -> StatementResult {
    Ok(
        process_statement(&qs, "_order", "", "ORDER BY {}", |x| {
            let vspl = &x.split(",").collect::<Vec<&str>>();
            let od: Vec<String> = vspl.iter()
                .map(|param| {
                    let mut s = String::new();
                    s.push_str(param); // omg
                    if s.starts_with("-") {
                        s.remove(0);
                        s.push_str(" DESC")
                    }
                    format!("{}", s)
                })
                .collect();
            format!("{}", od.join(","))
        }).unwrap(),
    )

}

// Get QueryString and return chunk GROUP BY statement
pub fn ext_groupby(qs: &QueryString) -> StatementResult {
    Ok(
        process_statement(qs, "_groupby", "", "GROUP BY {}", |x| String::from(x)).unwrap(),
    )
}

#[cfg(test)]
mod tests {
    use postgresql::commands::*;

    #[test]
    fn get_opetators() {
        use std::collections::HashMap;

        let mut test_cases = HashMap::new();
        test_cases.insert("$eq", "=");
        test_cases.insert("$ne", "!=");
        test_cases.insert("$gt", ">");
        test_cases.insert("$gte", ">=");
        test_cases.insert("$lt", "<");
        test_cases.insert("$lte", "<=");
        test_cases.insert("$in", "IN");
        test_cases.insert("$nin", "NOT IN");
        test_cases.insert("$notnull", "IS NOT NULL");
        test_cases.insert("$null", "IS NULL");

        for (op, expected) in &test_cases {
            match query_operator(&op.to_string()) {
                Ok(x) => assert_eq!(expected.to_string(), x),
                Err(e) => assert_eq!("", e),
            }
        }
    }

    #[test]
    fn get_opetators_error() {
        let invalid = "!tn".to_string();
        match query_operator(&invalid) {
            Ok(_) => println!("no result"),
            Err(e) => assert_eq!(format!("operator {} not found", invalid), e),
        }
    }

    #[test]
    fn where_by_request_one_querystring() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "5".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(&qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id=$1".to_string());
        assert_eq!(result.values, vec!["5".to_string()])
    }

    #[test]
    fn where_by_request_n_querystring() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(&qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id=$1 AND name=$2".to_string());
        assert_eq!(result.values, vec!["5".to_string(), "goga".to_string()])
    }

    #[test]
    fn where_by_request_with_query_operators() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "$ne.5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_where(&qs).unwrap();
        assert_eq!(result.sql, "WHERE user_id!=$1 AND name=$2".to_string());
        assert_eq!(result.values, vec!["5".to_string(), "goga".to_string()])
    }

    #[test]
    fn where_by_request_with_query_invalid_operators() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "$nt.5".to_string(),
        });
        hm.push(Params {
            k: "name".to_string(),
            v: "goga".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_where(&qs) {
            Err(e) => assert_eq!(e, "operator $nt not found"),
            Ok(_) => println!("no result"),
        }
    }

    #[test]
    fn select_by_request_one_column() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(&qs).unwrap();
        assert_eq!(result.sql, "SELECT name FROM");
    }

    #[test]
    fn select_by_request_n_column() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(&qs).unwrap();
        assert_eq!(result.sql, "SELECT name,age FROM");
    }

    #[test]
    fn select_by_request_n_empty_value() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_select".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_select(&qs).unwrap();
        assert_eq!(result.sql, "SELECT * FROM");
    }

    #[test]
    fn count_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(&qs).unwrap();
        assert_eq!(result.sql, "SELECT COUNT(name) FROM");
    }

    #[test]
    fn count_by_request_all_fields() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "*".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(&qs).unwrap();
        assert_eq!(result.sql, "SELECT COUNT(*) FROM");
    }

    #[test]
    fn count_by_request_two_fields() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_count(&qs) {
            Err(e) => assert_eq!(e, "could not use more than one column in count function"),
            Ok(s) => assert_eq!(s.sql, ""),
        }
    }

    #[test]
    fn count_by_request_empty_value() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_count".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_count(&qs).unwrap();
        assert_eq!(result.sql, "")
    }

    #[test]
    fn order_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(&qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name")
    }

    #[test]
    fn order_by_request_desc() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "-name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(&qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name DESC")
    }

    #[test]
    fn order_by_request_empty_params() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(qs).unwrap();
        assert_eq!(result.sql, "")
    }

    #[test]
    fn order_by_request_multiples_columns() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name,age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(&qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name,age")
    }

    #[test]
    fn order_by_request_multiples_columns_with_orders() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_order".to_string(),
            v: "name,-age".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_order(&qs).unwrap();
        assert_eq!(result.sql, "ORDER BY name,age DESC")
    }

    #[test]
    fn groupby_by_request() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "_groupby".to_string(),
            v: "name".to_string(),
        });

        let qs = QueryString { query: hm };
        let result = ext_groupby(&qs).unwrap();
        assert_eq!(result.sql, "GROUP BY name")
    }

    #[test]
    fn groupby_by_request_empty_param() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: String::from("_groupby"),
            v: String::from(""),
        });

        let qs = QueryString { query: hm };
        let result = ext_groupby(&qs).unwrap();
        assert_eq!(result.sql, "")
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2018, Cloudflare, Inc.
// Copyright (C) 2018, Alessandro Ghedini
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::cmp;

use std::time::Duration;
use std::time::Instant;

use std::collections::BTreeMap;

use crate::frame;
use crate::ranges;

// Loss Recovery
const PACKET_THRESHOLD: u64 = 3;

const GRANULARITY: Duration = Duration::from_millis(1);

const INITIAL_RTT: Duration = Duration::from_millis(100);

// Congestion Control
const MAX_DATAGRAM_SIZE: usize = 1460;

const INITIAL_WINDOW: usize = 10 * MAX_DATAGRAM_SIZE;
const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;

const PERSISTENT_CONGESTION_THRESHOLD: u32 = 2;

#[derive(Debug)]
pub struct Sent {
    pkt_num: u64,

    pub frames: Vec<frame::Frame>,

    time: Instant,

    size: usize,

    ack_eliciting: bool,

    is_crypto: bool,
}

impl Sent {
    pub fn new(pkt_num: u64, frames: Vec<frame::Frame>, sent_bytes: usize,
               ack_eliciting: bool, is_crypto: bool, now: Instant) -> Sent {
        let sent_bytes = if ack_eliciting { sent_bytes } else { 0 };

        Sent {
            pkt_num,
            frames,
            time: now,
            size: sent_bytes,
            ack_eliciting,
            is_crypto,
        }
    }
}

pub struct InFlight {
    pub sent: BTreeMap<u64, Sent>,
    pub lost: Vec<frame::Frame>,
    pub acked: Vec<frame::Frame>,
}

impl InFlight {
    pub fn retransmit_unacked_crypto(&mut self) -> usize {
        let mut unacked_bytes = 0;

        for p in &mut self.sent.values_mut() {
            p.frames.retain(|f|
                match f {
                    frame::Frame::Crypto { .. } => true,
                    _ => false,
                });

            unacked_bytes += p.size;

            self.lost.append(&mut p.frames);
        }

        self.sent.clear();

        unacked_bytes
    }
}

impl Default for InFlight {
    fn default() -> InFlight {
        InFlight {
            sent: BTreeMap::new(),
            lost: Vec::new(),
            acked: Vec::new(),
        }
    }
}

pub struct Recovery {
    loss_detection_timer: Option<Instant>,

    crypto_count: u32,

    pto_count: u32,

    time_of_last_sent_ack_eliciting_packet: Instant,

    time_of_last_sent_crypto_packet: Instant,

    largest_sent_pkt: u64,

    largest_acked_pkt: u64,

    latest_rtt: Duration,

    smoothed_rtt: Duration,

    rttvar: Duration,

    min_rtt: Duration,

    pub max_ack_delay: Duration,

    loss_time: Option<Instant>,

    bytes_in_flight: usize,

    cwnd: usize,

    recovery_start_time: Option<Instant>,

    ssthresh: usize,

    pub probes: usize,
}

impl Recovery {
    pub fn on_packet_sent(&mut self, pkt: Sent, flight: &mut InFlight,
                          now: Instant, trace_id: &str) {
        let pkt_num = pkt.pkt_num;
        let ack_eliciting = pkt.ack_eliciting;
        let is_crypto = pkt.is_crypto;
        let sent_bytes = pkt.size;

        self.largest_sent_pkt = pkt_num;

        flight.sent.insert(pkt_num, pkt);

        if ack_eliciting {
            if is_crypto {
                self.time_of_last_sent_crypto_packet = now;
            }

            self.time_of_last_sent_ack_eliciting_packet = now;

            // OnPacketSentCC
            self.bytes_in_flight += sent_bytes;

            self.set_loss_detection_timer(flight);
        }

        trace!("{} {:?}", trace_id, self);
    }

    pub fn on_ack_received(&mut self, ranges: &ranges::RangeSet, ack_delay: u64,
                           flight: &mut InFlight, now: Instant, trace_id: &str) {
        let largest_acked = ranges.largest().unwrap();

        if let Some(pkt) = flight.sent.get(&largest_acked) {
            if pkt.ack_eliciting {
                let ack_delay = Duration::from_micros(ack_delay);
                self.update_rtt(pkt.time.elapsed(), ack_delay);
            }
        }

        for pn in ranges.flatten().rev() {
            trace!("{} packet acked {}", trace_id, pn);
            self.on_packet_acked(pn, flight);
        }

        self.crypto_count = 0;
        self.pto_count = 0;

        self.detect_lost_packets(largest_acked, flight, now, trace_id);
        self.set_loss_detection_timer(flight);

        trace!("{} {:?}", trace_id, self);
    }

    pub fn on_loss_detection_timer(&mut self,
                                   in_flight: &mut InFlight,
                                   hs_flight: &mut InFlight,
                                   flight: &mut InFlight,
                                   now: Instant, trace_id: &str) {
        // TODO: avoid looping over every packet
        if in_flight.sent.values().any(|p| p.is_crypto) ||
           hs_flight.sent.values().any(|p| p.is_crypto) {
            self.crypto_count += 1;

            self.bytes_in_flight -= in_flight.retransmit_unacked_crypto();
            self.bytes_in_flight -= hs_flight.retransmit_unacked_crypto();
        } else if self.loss_time.is_some() {
            let largest_acked = self.largest_acked_pkt;
            self.detect_lost_packets(largest_acked, flight, now, trace_id);
        } else {
            self.pto_count += 1;
            self.probes = 2;
        }

        self.set_loss_detection_timer(flight);

        trace!("{} {:?}", trace_id, self);
    }

    pub fn loss_detection_timer(&self) -> Option<Instant> {
        self.loss_detection_timer
    }

    pub fn cwnd(&self) -> usize {
        // Ignore cwnd when sending probe packets.
        if self.probes > 0 {
            return std::usize::MAX;
        }

        if self.bytes_in_flight > self.cwnd {
            return 0;
        }

        self.cwnd - self.bytes_in_flight
    }

    fn update_rtt(&mut self, latest_rtt: Duration, ack_delay: Duration) {
        let zero = Duration::new(0, 0);

        let ack_delay = cmp::min(self.max_ack_delay, ack_delay);

        self.min_rtt = cmp::min(self.min_rtt, self.latest_rtt);

        if latest_rtt - self.min_rtt > ack_delay {
            self.latest_rtt = latest_rtt - ack_delay;
        }

        if self.smoothed_rtt == zero {
            self.rttvar = self.latest_rtt / 2;

            self.smoothed_rtt = self.latest_rtt;
        } else {
            let rttvar_sample = sub_abs(self.smoothed_rtt, self.latest_rtt);

            self.rttvar = (self.rttvar * 3 + rttvar_sample) / 4;

            self.smoothed_rtt = (self.smoothed_rtt * 7 + self.latest_rtt) / 8;
        }
    }

    fn set_loss_detection_timer(&mut self, flight: &InFlight) {
        let zero = Duration::new(0, 0);

        if self.bytes_in_flight == 0 {
            self.loss_detection_timer = None;
            return;
        }

        // TODO: avoid looping over every packet
        if flight.sent.values().any(|p| p.is_crypto) {
            // Crypto retransmission timer.
            let mut timeout = if self.smoothed_rtt == zero {
                INITIAL_RTT * 2
            } else {
                self.smoothed_rtt * 2
            };

            timeout = cmp::max(timeout, GRANULARITY);
            timeout *= 2_u32.pow(self.crypto_count);

            self.loss_detection_timer =
                Some(self.time_of_last_sent_crypto_packet + timeout);

            return;
        }

        if self.loss_time.is_some() {
            // Time threshold loss detection.
            self.loss_detection_timer = self.loss_time;
            return;
        }

        // PTO timer.
        let mut timeout = self.smoothed_rtt +
                          (self.rttvar * 4) +
                          self.max_ack_delay;

        timeout = cmp::max(timeout, GRANULARITY);
        timeout *= 2_u32.pow(self.pto_count);

        self.loss_detection_timer =
            Some(self.time_of_last_sent_ack_eliciting_packet + timeout);
    }

    fn detect_lost_packets(&mut self, largest_acked: u64, flight: &mut InFlight,
                           now: Instant, trace_id: &str) {
        let mut lost_pkt: Vec<u64> = Vec::new();

        let loss_delay = (cmp::max(self.latest_rtt, self.smoothed_rtt) * 9) / 8;

        let lost_send_time = now - loss_delay;

        let lost_pkt_num = largest_acked.checked_sub(PACKET_THRESHOLD)
                                        .unwrap_or(0);

        self.loss_time = None;

        // TODO: avoid looping over every packet
        for unacked in flight.sent.values().filter(|p| p.pkt_num <= largest_acked) {
            if unacked.time <= lost_send_time || unacked.pkt_num <= lost_pkt_num {
                if unacked.ack_eliciting {
                    trace!("{} packet lost {}", trace_id, unacked.pkt_num);
                }

                lost_pkt.push(unacked.pkt_num);
            } else if self.loss_time.is_none() {
                self.loss_time = Some(unacked.time + loss_delay);
            } else {
                let loss_time = self.loss_time.unwrap();
                self.loss_time =
                    Some(cmp::min(loss_time, unacked.time + loss_delay));
            }
        }

        if !lost_pkt.is_empty() {
            self.on_packets_lost(lost_pkt, flight, now);
        }
    }

    fn in_recovery(&self, sent_time: Instant) -> bool {
        match self.recovery_start_time {
            Some(recovery_start_time) => sent_time <= recovery_start_time,

            None => false,
        }
    }

    fn on_packet_acked(&mut self, pkt_num: u64, flight: &mut InFlight) {
        if let Some(mut p) = flight.sent.remove(&pkt_num) {
            flight.acked.append(&mut p.frames);

            if p.ack_eliciting {
                // OnPacketAckedCC
                self.bytes_in_flight -= p.size;

                if self.in_recovery(p.time) {
                    return;
                }

                if self.cwnd < self.ssthresh {
                    self.cwnd += p.size;
                } else {
                    self.cwnd += (MAX_DATAGRAM_SIZE * p.size) / self.cwnd;
                }
            }
        }
    }

    fn on_packets_lost(&mut self, lost_pkt: Vec<u64>, flight: &mut InFlight,
                       now: Instant) {
        let mut largest_lost_packet = 0;
        let mut largest_lost_packet_sent_time = now;

        for lost in lost_pkt {
            let mut p = flight.sent.remove(&lost).unwrap();

            if !p.ack_eliciting {
                continue;
            }

            self.bytes_in_flight -= p.size;
            flight.lost.append(&mut p.frames);

            if lost > largest_lost_packet {
                largest_lost_packet = lost;
                largest_lost_packet_sent_time = p.time;
            }
        }

        // CongestionEvent
        if !self.in_recovery(largest_lost_packet_sent_time) {
            self.recovery_start_time = Some(now);

            self.cwnd /= 2;
            self.cwnd = cmp::max(self.cwnd, MINIMUM_WINDOW);
            self.ssthresh = self.cwnd;

            if self.pto_count > PERSISTENT_CONGESTION_THRESHOLD {
                self.cwnd = MINIMUM_WINDOW;
            }
        }
    }
}

impl Default for Recovery {
    fn default() -> Recovery {
        let now = Instant::now();

        Recovery {
            loss_detection_timer: None,

            crypto_count: 0,

            pto_count: 0,

            time_of_last_sent_crypto_packet: now,

            time_of_last_sent_ack_eliciting_packet: now,

            largest_sent_pkt: 0,

            largest_acked_pkt: 0,

            latest_rtt: Duration::new(0, 0),

            smoothed_rtt: Duration::new(0, 0),

            min_rtt: Duration::from_secs(std::u64::MAX),

            rttvar: Duration::new(0, 0),

            max_ack_delay: Duration::from_millis(25),

            loss_time: None,

            bytes_in_flight: 0,

            cwnd: INITIAL_WINDOW,

            recovery_start_time: None,

            ssthresh: std::usize::MAX,

            probes: 0,
        }
    }
}

impl std::fmt::Debug for Recovery {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.loss_detection_timer {
            Some(v) => {
                let now = Instant::now();

                if v > now {
                    let d = v.duration_since(now);
                    write!(f, "timer={:?} ", d)?;
                } else {
                    write!(f, "timer=exp ")?;
                }
            },

            None => {
                write!(f, "timer=none ")?;
            },
        };

        write!(f, "inflight={} ", self.bytes_in_flight)?;
        write!(f, "cwnd={} ", self.cwnd)?;
        write!(f, "latest_rtt={:?} ", self.latest_rtt)?;
        write!(f, "srtt={:?} ", self.smoothed_rtt)?;
        write!(f, "min_rtt={:?} ", self.min_rtt)?;
        write!(f, "rttvar={:?} ", self.rttvar)?;
        write!(f, "probes={} ", self.probes)?;

        Ok(())
    }
}

fn sub_abs(lhs: Duration, rhs: Duration) -> Duration {
    if lhs > rhs {
        lhs - rhs
    } else {
        rhs - lhs
    }
}
<commit_msg>recovery: retransmit crypto data from 1-RTT pkt num space<commit_after>// Copyright (C) 2018, Cloudflare, Inc.
// Copyright (C) 2018, Alessandro Ghedini
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::cmp;

use std::time::Duration;
use std::time::Instant;

use std::collections::BTreeMap;

use crate::frame;
use crate::ranges;

// Loss Recovery
const PACKET_THRESHOLD: u64 = 3;

const GRANULARITY: Duration = Duration::from_millis(1);

const INITIAL_RTT: Duration = Duration::from_millis(100);

// Congestion Control
const MAX_DATAGRAM_SIZE: usize = 1460;

const INITIAL_WINDOW: usize = 10 * MAX_DATAGRAM_SIZE;
const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE;

const PERSISTENT_CONGESTION_THRESHOLD: u32 = 2;

#[derive(Debug)]
pub struct Sent {
    pkt_num: u64,

    pub frames: Vec<frame::Frame>,

    time: Instant,

    size: usize,

    ack_eliciting: bool,

    is_crypto: bool,
}

impl Sent {
    pub fn new(pkt_num: u64, frames: Vec<frame::Frame>, sent_bytes: usize,
               ack_eliciting: bool, is_crypto: bool, now: Instant) -> Sent {
        let sent_bytes = if ack_eliciting { sent_bytes } else { 0 };

        Sent {
            pkt_num,
            frames,
            time: now,
            size: sent_bytes,
            ack_eliciting,
            is_crypto,
        }
    }
}

pub struct InFlight {
    pub sent: BTreeMap<u64, Sent>,
    pub lost: Vec<frame::Frame>,
    pub acked: Vec<frame::Frame>,
}

impl InFlight {
    pub fn retransmit_unacked_crypto(&mut self) -> usize {
        let mut unacked_bytes = 0;

        for p in &mut self.sent.values_mut().filter(|p| p.is_crypto) {
            p.frames.retain(|f|
                match f {
                    frame::Frame::Crypto { .. } => true,

                    _ => false,
                });

            unacked_bytes += p.size;

            self.lost.append(&mut p.frames);
        }

        self.sent.clear();

        unacked_bytes
    }
}

impl Default for InFlight {
    fn default() -> InFlight {
        InFlight {
            sent: BTreeMap::new(),
            lost: Vec::new(),
            acked: Vec::new(),
        }
    }
}

pub struct Recovery {
    loss_detection_timer: Option<Instant>,

    crypto_count: u32,

    pto_count: u32,

    time_of_last_sent_ack_eliciting_packet: Instant,

    time_of_last_sent_crypto_packet: Instant,

    largest_sent_pkt: u64,

    largest_acked_pkt: u64,

    latest_rtt: Duration,

    smoothed_rtt: Duration,

    rttvar: Duration,

    min_rtt: Duration,

    pub max_ack_delay: Duration,

    loss_time: Option<Instant>,

    bytes_in_flight: usize,

    cwnd: usize,

    recovery_start_time: Option<Instant>,

    ssthresh: usize,

    pub probes: usize,
}

impl Recovery {
    pub fn on_packet_sent(&mut self, pkt: Sent, flight: &mut InFlight,
                          now: Instant, trace_id: &str) {
        let pkt_num = pkt.pkt_num;
        let ack_eliciting = pkt.ack_eliciting;
        let is_crypto = pkt.is_crypto;
        let sent_bytes = pkt.size;

        self.largest_sent_pkt = pkt_num;

        flight.sent.insert(pkt_num, pkt);

        if ack_eliciting {
            if is_crypto {
                self.time_of_last_sent_crypto_packet = now;
            }

            self.time_of_last_sent_ack_eliciting_packet = now;

            // OnPacketSentCC
            self.bytes_in_flight += sent_bytes;

            self.set_loss_detection_timer(flight);
        }

        trace!("{} {:?}", trace_id, self);
    }

    pub fn on_ack_received(&mut self, ranges: &ranges::RangeSet, ack_delay: u64,
                           flight: &mut InFlight, now: Instant, trace_id: &str) {
        let largest_acked = ranges.largest().unwrap();

        if let Some(pkt) = flight.sent.get(&largest_acked) {
            if pkt.ack_eliciting {
                let ack_delay = Duration::from_micros(ack_delay);
                self.update_rtt(pkt.time.elapsed(), ack_delay);
            }
        }

        for pn in ranges.flatten().rev() {
            trace!("{} packet acked {}", trace_id, pn);
            self.on_packet_acked(pn, flight);
        }

        self.crypto_count = 0;
        self.pto_count = 0;

        self.detect_lost_packets(largest_acked, flight, now, trace_id);
        self.set_loss_detection_timer(flight);

        trace!("{} {:?}", trace_id, self);
    }

    pub fn on_loss_detection_timer(&mut self,
                                   in_flight: &mut InFlight,
                                   hs_flight: &mut InFlight,
                                   flight: &mut InFlight,
                                   now: Instant, trace_id: &str) {
        // TODO: avoid looping over every packet
        if in_flight.sent.values().any(|p| p.is_crypto) ||
           hs_flight.sent.values().any(|p| p.is_crypto) ||
           flight.sent.values().any(|p| p.is_crypto) {
            self.crypto_count += 1;

            self.bytes_in_flight -= in_flight.retransmit_unacked_crypto();
            self.bytes_in_flight -= hs_flight.retransmit_unacked_crypto();
            self.bytes_in_flight -= flight.retransmit_unacked_crypto();
        } else if self.loss_time.is_some() {
            let largest_acked = self.largest_acked_pkt;
            self.detect_lost_packets(largest_acked, flight, now, trace_id);
        } else {
            self.pto_count += 1;
            self.probes = 2;
        }

        self.set_loss_detection_timer(flight);

        trace!("{} {:?}", trace_id, self);
    }

    pub fn loss_detection_timer(&self) -> Option<Instant> {
        self.loss_detection_timer
    }

    pub fn cwnd(&self) -> usize {
        // Ignore cwnd when sending probe packets.
        if self.probes > 0 {
            return std::usize::MAX;
        }

        if self.bytes_in_flight > self.cwnd {
            return 0;
        }

        self.cwnd - self.bytes_in_flight
    }

    fn update_rtt(&mut self, latest_rtt: Duration, ack_delay: Duration) {
        let zero = Duration::new(0, 0);

        let ack_delay = cmp::min(self.max_ack_delay, ack_delay);

        self.min_rtt = cmp::min(self.min_rtt, self.latest_rtt);

        if latest_rtt - self.min_rtt > ack_delay {
            self.latest_rtt = latest_rtt - ack_delay;
        }

        if self.smoothed_rtt == zero {
            self.rttvar = self.latest_rtt / 2;

            self.smoothed_rtt = self.latest_rtt;
        } else {
            let rttvar_sample = sub_abs(self.smoothed_rtt, self.latest_rtt);

            self.rttvar = (self.rttvar * 3 + rttvar_sample) / 4;

            self.smoothed_rtt = (self.smoothed_rtt * 7 + self.latest_rtt) / 8;
        }
    }

    fn set_loss_detection_timer(&mut self, flight: &InFlight) {
        let zero = Duration::new(0, 0);

        if self.bytes_in_flight == 0 {
            self.loss_detection_timer = None;
            return;
        }

        // TODO: avoid looping over every packet
        if flight.sent.values().any(|p| p.is_crypto) {
            // Crypto retransmission timer.
            let mut timeout = if self.smoothed_rtt == zero {
                INITIAL_RTT * 2
            } else {
                self.smoothed_rtt * 2
            };

            timeout = cmp::max(timeout, GRANULARITY);
            timeout *= 2_u32.pow(self.crypto_count);

            self.loss_detection_timer =
                Some(self.time_of_last_sent_crypto_packet + timeout);

            return;
        }

        if self.loss_time.is_some() {
            // Time threshold loss detection.
            self.loss_detection_timer = self.loss_time;
            return;
        }

        // PTO timer.
        let mut timeout = self.smoothed_rtt +
                          (self.rttvar * 4) +
                          self.max_ack_delay;

        timeout = cmp::max(timeout, GRANULARITY);
        timeout *= 2_u32.pow(self.pto_count);

        self.loss_detection_timer =
            Some(self.time_of_last_sent_ack_eliciting_packet + timeout);
    }

    fn detect_lost_packets(&mut self, largest_acked: u64, flight: &mut InFlight,
                           now: Instant, trace_id: &str) {
        let mut lost_pkt: Vec<u64> = Vec::new();

        let loss_delay = (cmp::max(self.latest_rtt, self.smoothed_rtt) * 9) / 8;

        let lost_send_time = now - loss_delay;

        let lost_pkt_num = largest_acked.checked_sub(PACKET_THRESHOLD)
                                        .unwrap_or(0);

        self.loss_time = None;

        // TODO: avoid looping over every packet
        for unacked in flight.sent.values().filter(|p| p.pkt_num <= largest_acked) {
            if unacked.time <= lost_send_time || unacked.pkt_num <= lost_pkt_num {
                if unacked.ack_eliciting {
                    trace!("{} packet lost {}", trace_id, unacked.pkt_num);
                }

                lost_pkt.push(unacked.pkt_num);
            } else if self.loss_time.is_none() {
                self.loss_time = Some(unacked.time + loss_delay);
            } else {
                let loss_time = self.loss_time.unwrap();
                self.loss_time =
                    Some(cmp::min(loss_time, unacked.time + loss_delay));
            }
        }

        if !lost_pkt.is_empty() {
            self.on_packets_lost(lost_pkt, flight, now);
        }
    }

    fn in_recovery(&self, sent_time: Instant) -> bool {
        match self.recovery_start_time {
            Some(recovery_start_time) => sent_time <= recovery_start_time,

            None => false,
        }
    }

    fn on_packet_acked(&mut self, pkt_num: u64, flight: &mut InFlight) {
        if let Some(mut p) = flight.sent.remove(&pkt_num) {
            flight.acked.append(&mut p.frames);

            if p.ack_eliciting {
                // OnPacketAckedCC
                self.bytes_in_flight -= p.size;

                if self.in_recovery(p.time) {
                    return;
                }

                if self.cwnd < self.ssthresh {
                    self.cwnd += p.size;
                } else {
                    self.cwnd += (MAX_DATAGRAM_SIZE * p.size) / self.cwnd;
                }
            }
        }
    }

    fn on_packets_lost(&mut self, lost_pkt: Vec<u64>, flight: &mut InFlight,
                       now: Instant) {
        let mut largest_lost_packet = 0;
        let mut largest_lost_packet_sent_time = now;

        for lost in lost_pkt {
            let mut p = flight.sent.remove(&lost).unwrap();

            if !p.ack_eliciting {
                continue;
            }

            self.bytes_in_flight -= p.size;
            flight.lost.append(&mut p.frames);

            if lost > largest_lost_packet {
                largest_lost_packet = lost;
                largest_lost_packet_sent_time = p.time;
            }
        }

        // CongestionEvent
        if !self.in_recovery(largest_lost_packet_sent_time) {
            self.recovery_start_time = Some(now);

            self.cwnd /= 2;
            self.cwnd = cmp::max(self.cwnd, MINIMUM_WINDOW);
            self.ssthresh = self.cwnd;

            if self.pto_count > PERSISTENT_CONGESTION_THRESHOLD {
                self.cwnd = MINIMUM_WINDOW;
            }
        }
    }
}

impl Default for Recovery {
    fn default() -> Recovery {
        let now = Instant::now();

        Recovery {
            loss_detection_timer: None,

            crypto_count: 0,

            pto_count: 0,

            time_of_last_sent_crypto_packet: now,

            time_of_last_sent_ack_eliciting_packet: now,

            largest_sent_pkt: 0,

            largest_acked_pkt: 0,

            latest_rtt: Duration::new(0, 0),

            smoothed_rtt: Duration::new(0, 0),

            min_rtt: Duration::from_secs(std::u64::MAX),

            rttvar: Duration::new(0, 0),

            max_ack_delay: Duration::from_millis(25),

            loss_time: None,

            bytes_in_flight: 0,

            cwnd: INITIAL_WINDOW,

            recovery_start_time: None,

            ssthresh: std::usize::MAX,

            probes: 0,
        }
    }
}

impl std::fmt::Debug for Recovery {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.loss_detection_timer {
            Some(v) => {
                let now = Instant::now();

                if v > now {
                    let d = v.duration_since(now);
                    write!(f, "timer={:?} ", d)?;
                } else {
                    write!(f, "timer=exp ")?;
                }
            },

            None => {
                write!(f, "timer=none ")?;
            },
        };

        write!(f, "inflight={} ", self.bytes_in_flight)?;
        write!(f, "cwnd={} ", self.cwnd)?;
        write!(f, "latest_rtt={:?} ", self.latest_rtt)?;
        write!(f, "srtt={:?} ", self.smoothed_rtt)?;
        write!(f, "min_rtt={:?} ", self.min_rtt)?;
        write!(f, "rttvar={:?} ", self.rttvar)?;
        write!(f, "probes={} ", self.probes)?;

        Ok(())
    }
}

fn sub_abs(lhs: Duration, rhs: Duration) -> Duration {
    if lhs > rhs {
        lhs - rhs
    } else {
        rhs - lhs
    }
}
<|endoftext|>


<commit_before>use super::{CloneCell, Color, Event, Place, Point, Rect, Renderer, Widget, Window};
use super::callback::Click;
use super::cell::CheckSet;

use std::cell::Cell;
use std::sync::Arc;

pub struct Label {
    pub rect: Cell<Rect>,
    pub text: CloneCell<String>,
    pub bg: Color,
    pub fg: Color,
    pub text_offset: Point,
    click_callback: Option<Arc<Fn(&Label, Point)>>,
    pressed: Cell<bool>,
}

impl Label {
    pub fn new() -> Self {
        Label {
            rect: Cell::new(Rect::default()),
            text: CloneCell::new(String::new()),
            bg: Color::rgb(237, 233, 227),
            fg: Color::rgb(0, 0, 0),
            text_offset: Point::default(),
            click_callback: None,
            pressed: Cell::new(false),
        }
    }

    pub fn place(self, window: &mut Window) -> Arc<Self> {
        let arc = Arc::new(self);

        window.widgets.push(arc.clone());

        arc
    }

    pub fn text(self, text: &str) -> Self {
        self.text.set(text.to_owned());
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Label {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Place for Label {
    fn rect(&self) -> &Cell<Rect> {
        &self.rect
    }
}

impl Widget for Label {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();
        renderer.rect(rect, self.bg);

        let text = self.text.borrow();

        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, self.fg);
                }
                point.x += 8;
            }
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.rect.get();
                if rect.contains(point) {
                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            *redraw = true;
                        }
                    }
                } else {
                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        focused
    }
}
<commit_msg>Update label.rs with `WidgetCore`<commit_after>use super::{CloneCell, Color, Event, Place, Point, Rect, Renderer, Widget, WidgetCore, Window};
use super::callback::Click;
use super::cell::CheckSet;

use std::cell::Cell;
use std::sync::Arc;

pub struct Label {
    pub core: WidgetCore,
    pub text: CloneCell<String>,
    pub text_offset: Point,
    click_callback: Option<Arc<Fn(&Label, Point)>>,
    pressed: Cell<bool>,
}

impl Label {
    pub fn new() -> Self {
        Label {
            core: WidgetCore::new(Color::rgb(237, 233, 227), Color::black()),
            text: CloneCell::new(String::new()),
            text_offset: Point::default(),
            click_callback: None,
            pressed: Cell::new(false),
        }
    }

    pub fn place(self, window: &mut Window) -> Arc<Self> {
        let arc = Arc::new(self);

        window.widgets.push(arc.clone());

        arc
    }

    pub fn text(self, text: &str) -> Self {
        self.text.set(text.to_owned());
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Label {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Place for Label {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }
}

impl Widget for Label {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();
        renderer.rect(rect, self.core.bg);

        let text = self.text.borrow();

        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, self.core.fg);
                }
                point.x += 8;
            }
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.core.rect.get();
                if rect.contains(point) {
                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            *redraw = true;
                        }
                    }
                } else {
                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        focused
    }
}
<|endoftext|>


<commit_before>/// Brainfuck language interpreter
/// As specified here: http://www.muppetlabs.com/~breadbox/bf/
#[macro_use]
extern crate clap;

use std::process;
use std::path::{Path};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::collections::VecDeque;
use std::thread;
use std::time::Duration;

use clap::{Arg, App};

macro_rules! exit_with_error(
    ($($arg:tt)*) => { {
        writeln!(&mut ::std::io::stderr(), $($arg)*)
            .expect("Failed while printing to stderr");
        process::exit(1);
    } }
);

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

fn main() {
    let args = App::new(crate_name!())
        .version(crate_version!())
        .version_short("v")
        .author(crate_authors!())
        .about("Brainfuck interpreter companion to the brain language compiler")
        .arg(Arg::with_name("input-file")
            .help("The brainfuck file to process. Should contain brainfuck instructions")
            .value_name("file")
            .takes_value(true)
            .required(true)
        )
        .arg(Arg::with_name("debug-enabled")
            .short("D")
            .long("debug")
            .help("Enables debug mode which outputs debugging information to stderr")
        )
        .arg(Arg::with_name("delay")
            .long("delay")
            .takes_value(true)
            .help("Delays execution of each instruction by this amount in ms")
        )
        .get_matches();

    let source_path = Path::new(args.value_of("input-file").unwrap());
    if !source_path.exists() || !source_path.is_file() {
        exit_with_error!("Not a valid file: '{}'", source_path.display());
    }

    let debug_mode = args.is_present("debug-enabled");

    let delay: u64 = if let Some(delay_str) = args.value_of("delay") {
        delay_str.parse().unwrap_or_else(|e: std::num::ParseIntError| exit_with_error!("Invalid delay: {}", e))
    } else {
        0
    };

    let f = File::open(source_path).unwrap_or_else(|e| {
        exit_with_error!("Could not open source file: {}", e);
    });

    let program = f.bytes().map(
        |c| c.expect("Fatal: Could not read char") as char
    ).collect::<Vec<char>>();

    interpret(program, debug_mode, delay);
}

fn interpret(program: Vec<char>, debug: bool, delay: u64) {
    let mut buffer: VecDeque<u8> = VecDeque::new();
    // Make sure there is at least one cell to begin with
    buffer.push_back(0u8);

    // p is the position "pointer" in the buffer
    let mut p: usize = 0;
    // i is the instruction index in the program
    let mut i: usize = 0;

    loop {
        if i >= program.len() {
            break;
        }
        let c = program[i];
        i += 1;

        match c {
            '>' => {
                p += 1;
                if p >= buffer.len() {
                    buffer.push_back(0u8);
                }
            },
            '<' => {
                if p == 0 {
                    buffer.push_front(0u8);
                }
                else {
                    p -= 1;
                }
            },
            '+' => buffer[p] = buffer[p].wrapping_add(1),
            '-' => buffer[p] = buffer[p].wrapping_sub(1),
            '.' => print!("{}", buffer[p] as char),
            ',' => {
                let chr = io::stdin().bytes().next();
                if chr.is_none() {
                    break;
                }
                buffer[p] = chr.unwrap().expect("Could not read input");
            },
            '[' => {
                if buffer[p] == 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            ']' => {
                if buffer[p] != 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            _ => continue,
        }

        thread::sleep(Duration::from_millis(delay));

        if debug {
            println_stderr!("{{lastInstructionIndex: {}, lastInstruction: \"{}\", currentPointer: {}, memory: \"{}\"}}", i-1, c, p,
                buffer.iter().fold(String::new(), |acc, v| format!("{} {}", acc, v)));
        }
    }
}

/// Finds the matching '[' or ']' for the given position within the program
/// panics if a match is not found
fn find_matching(program: &Vec<char>, start: usize) -> usize {
    let direction: isize = match program[start] {
        '[' => 1,
        ']' => -1,
        _ => unreachable!(),
    };

    let mut count = direction;
    let mut current = start;
    loop {
        if (direction < 0 && current == 0) || (direction > 0 && current >= program.len() - 1) {
            panic!("Could not find matching parenthesis for instruction {}", start);
        }
        current = (current as isize + direction) as usize;
        let c = program[current];

        count = match c {
            '[' => count + 1,
            ']' => count - 1,
            _ => count,
        };

        if count == 0 {
            break;
        }
    }

    current
}
<commit_msg>Making sure deug information is outputted before the delay<commit_after>/// Brainfuck language interpreter
/// As specified here: http://www.muppetlabs.com/~breadbox/bf/
#[macro_use]
extern crate clap;

use std::process;
use std::path::{Path};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::collections::VecDeque;
use std::thread;
use std::time::Duration;

use clap::{Arg, App};

macro_rules! exit_with_error(
    ($($arg:tt)*) => { {
        writeln!(&mut ::std::io::stderr(), $($arg)*)
            .expect("Failed while printing to stderr");
        process::exit(1);
    } }
);

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

fn main() {
    let args = App::new(crate_name!())
        .version(crate_version!())
        .version_short("v")
        .author(crate_authors!())
        .about("Brainfuck interpreter companion to the brain language compiler")
        .arg(Arg::with_name("input-file")
            .help("The brainfuck file to process. Should contain brainfuck instructions")
            .value_name("file")
            .takes_value(true)
            .required(true)
        )
        .arg(Arg::with_name("debug-enabled")
            .short("D")
            .long("debug")
            .help("Enables debug mode which outputs debugging information to stderr")
        )
        .arg(Arg::with_name("delay")
            .long("delay")
            .takes_value(true)
            .help("Delays execution of each instruction by this amount in ms")
        )
        .get_matches();

    let source_path = Path::new(args.value_of("input-file").unwrap());
    if !source_path.exists() || !source_path.is_file() {
        exit_with_error!("Not a valid file: '{}'", source_path.display());
    }

    let debug_mode = args.is_present("debug-enabled");

    let delay: u64 = if let Some(delay_str) = args.value_of("delay") {
        delay_str.parse().unwrap_or_else(|e: std::num::ParseIntError| exit_with_error!("Invalid delay: {}", e))
    } else {
        0
    };

    let f = File::open(source_path).unwrap_or_else(|e| {
        exit_with_error!("Could not open source file: {}", e);
    });

    let program = f.bytes().map(
        |c| c.expect("Fatal: Could not read char") as char
    ).collect::<Vec<char>>();

    interpret(program, debug_mode, delay);
}

fn interpret(program: Vec<char>, debug: bool, delay: u64) {
    let mut buffer: VecDeque<u8> = VecDeque::new();
    // Make sure there is at least one cell to begin with
    buffer.push_back(0u8);

    // p is the position "pointer" in the buffer
    let mut p: usize = 0;
    // i is the instruction index in the program
    let mut i: usize = 0;

    loop {
        if i >= program.len() {
            break;
        }
        let c = program[i];
        i += 1;

        match c {
            '>' => {
                p += 1;
                if p >= buffer.len() {
                    buffer.push_back(0u8);
                }
            },
            '<' => {
                if p == 0 {
                    buffer.push_front(0u8);
                }
                else {
                    p -= 1;
                }
            },
            '+' => buffer[p] = buffer[p].wrapping_add(1),
            '-' => buffer[p] = buffer[p].wrapping_sub(1),
            '.' => print!("{}", buffer[p] as char),
            ',' => {
                let chr = io::stdin().bytes().next();
                if chr.is_none() {
                    break;
                }
                buffer[p] = chr.unwrap().expect("Could not read input");
            },
            '[' => {
                if buffer[p] == 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            ']' => {
                if buffer[p] != 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            _ => continue,
        }

        if debug {
            println_stderr!("{{lastInstructionIndex: {}, lastInstruction: \"{}\", currentPointer: {}, memory: \"{}\"}}", i-1, c, p,
                buffer.iter().fold(String::new(), |acc, v| format!("{} {}", acc, v)));
        }

        thread::sleep(Duration::from_millis(delay));
    }
}

/// Finds the matching '[' or ']' for the given position within the program
/// panics if a match is not found
fn find_matching(program: &Vec<char>, start: usize) -> usize {
    let direction: isize = match program[start] {
        '[' => 1,
        ']' => -1,
        _ => unreachable!(),
    };

    let mut count = direction;
    let mut current = start;
    loop {
        if (direction < 0 && current == 0) || (direction > 0 && current >= program.len() - 1) {
            panic!("Could not find matching parenthesis for instruction {}", start);
        }
        current = (current as isize + direction) as usize;
        let c = program[current];

        count = match c {
            '[' => count + 1,
            ']' => count - 1,
            _ => count,
        };

        if count == 0 {
            break;
        }
    }

    current
}
<|endoftext|>