<commit_before>extern crate ndarray;
extern crate fnv;

use self::fnv::FnvHashMap;
use ndarray_ext::NdArray;
use ops;
use std::collections::binary_heap::BinaryHeap;
use std::collections::hash_set::HashSet;
use std::mem;
use tensor::Tensor;

#[doc(hidden)]
#[inline]
/// Performs actual graph traversal and its evaluation
// TODO: loop-based rather than recursion (this would be difficult)
pub fn perform_eval(target: &Tensor, memo: &mut FnvHashMap<Tensor, NdArray>, train: bool)
{
    if memo.contains_key(target) {
        return;
    }
    let y = {
        let mut xs = vec![];
        {
            let ref inputs = target.borrow().inputs;
            // integrating loops below is impossible because of
            // "memo is already mutably borrowed"
            for input in inputs.iter() {
                perform_eval(input, memo, train);
            }
            for input in inputs.iter() {
                // unwrap is safe
                xs.push(memo.get(input).unwrap());
            }
        }

        target.borrow_mut().op.compute(xs.as_slice(), train)
    };

    // cache output
    memo.insert(target.clone(), y);
}

#[inline]
/// Makes mapping of {node => the node contributed to gradient or not}
fn contributed_to_grads(objective: &Tensor, variables: &[&Tensor]) -> FnvHashMap<Tensor, bool>
{
    fn rec(target: &Tensor, vars: &[&Tensor], memo: &mut FnvHashMap<Tensor, bool>)
    {
        if memo.contains_key(target) {
            return;
        }

        let mut contrib = false;

        if vars.contains(&target) {
            contrib = true;
        } else {
            for input in target.borrow().inputs.iter() {
                // recurse
                rec(input, vars, memo);
                // unwrap is always safe
                contrib |= *memo.get(input).unwrap();
            }
        }

        memo.insert(target.clone(), contrib);
    }

    let mut memo = FnvHashMap::default();
    rec(objective, variables, &mut memo);
    memo
}

#[test]
fn contributed_to_grads_test()
{
    // dummy graph
    let ref t = ::constant(::ndarray_ext::standard_normal(&[2, 3]));
    let ref v = ::variable(::ndarray_ext::standard_normal(&[2, 3]));
    let ref z = ::sigmoid_cross_entropy(&v, &t);
    let booleans = contributed_to_grads(z, &[v]);
    assert_eq!(booleans.len(), 3);
    assert!(!booleans.get(t).unwrap());
    assert!(booleans.get(v).unwrap());
    assert!(booleans.get(z).unwrap());
}

#[inline]
/// Returns symbolic gradient tensors.
///
/// This computes partial derivatives of `objective` with `variables` and returns the
/// gradients. This is achieved by building the subgraph between `objective` and
/// `variables` in reverse order from user's graph definition.
///
/// NOTE: Nodes that do not contribute to the gradient won't be included to avoid
/// unnecessary computation.
pub fn symbolic_gradients(
    objective: &Tensor,
    variables: &[&Tensor],
    initial_grad: Option<&Tensor>,
) -> Vec<Tensor>
{
    #[inline]
    fn maybe_reduce_grad(gys: &mut Vec<Tensor>)
    {
        if gys.len() < 2 {
            return;
        }
        let acc = {
            // values to refs
            let refs = gys.iter().map(|a| a).collect::<Vec<_>>();
            if refs.len() == 2 {
                // normal addition
                refs[0] + refs[1]
            } else {
                // For 3 or more gradients, AddN, i.e. inplace accumulation
                // is preferred for performance
                ops::add_n(refs.as_slice())
            }
        };
        mem::swap(&mut vec![acc], gys);
    }

    let initial_grad = initial_grad.map(|a| a.clone()).unwrap_or_else(
        || ops::scalar(1.),
    );

    // Mapping of {y => [gy]}
    let mut grads = FnvHashMap::default();

    // Mapping of {node => must visit or not (boolean)}
    let contrib = contributed_to_grads(objective, variables);

    // Prepare a heap with tensor's rank numbers for reversed
    // topological sort.
    let mut heap = BinaryHeap::new();
    heap.push(objective.clone());
    grads.insert(objective.clone(), vec![initial_grad]);

    // This prevents calling `grad()` twice or more
    let mut grad_done = HashSet::<Tensor>::new();

    // builds backward graph
    while let Some(target) = heap.pop() {
        let borrowed_target = target.borrow();

        // Vec<Tensor> to Vec<&Tensor>
        let xs = borrowed_target.inputs.iter().map(|a| a).collect::<Vec<_>>();

        // time to call `grad`
        let gxs = {
            if let Some(gys) = grads.get_mut(&target) {
                maybe_reduce_grad(gys);
                borrowed_target.op.grad(&gys[0], xs.as_slice(), &target)
            } else {
                unreachable!("Safe unwrapping is guaranteed by topological ordering")
            }
        };

        debug_assert_eq!(
            xs.len(),
            gxs.len(),
            "Wrong `grad` implementation of {}.",
            target
        );

        for (x, maybe_gx) in xs.into_iter().zip(gxs) {
            if !contrib.contains_key(x) {
                continue;
            }
            // cuts the backward path if gx is None.
            if let Some(gx) = maybe_gx {
                // memo gx
                if let Some(mut gys) = grads.remove(x) {
                    // gradient accumulation should be delayed here
                    gys.push(gx);
                    grads.insert(x.clone(), gys);
                } else {
                    grads.insert(x.clone(), vec![gx]);
                }
                // update heap
                if !x.is_source() && !grad_done.contains(x) {
                    grad_done.insert(x.clone());
                    heap.push(x.clone());
                }
            }
        }
    }

    variables
        .iter()
        .map(|v| {
            let mut gys = grads.remove(v).expect(
                "Input variable(s) didn't contributed to gradient computation",
            );
            maybe_reduce_grad(&mut gys);
            gys.remove(0)
        })
        .collect::<Vec<Tensor>>()
}

#[test]
fn topological_ordering_on_reverse_mode()
{
    fn collect_parents_from(endpoint: &Tensor) -> HashSet<Tensor>
    {
        let mut collected = HashSet::new();
        endpoint.visit_once(&mut |arg| { collected.insert(arg.clone()); });
        collected
    }

    let ref x = ::constant(::ndarray_ext::standard_normal(&[4, 2]));
    let ref w = ::variable(::ndarray_ext::standard_normal(&[2, 3]));
    let ref b = ::variable(::ndarray_ext::zeros(&[4, 3]));
    let ref z = ::matmul(x, w) + b;
    let ref g = ::gradients(z, &[w], Some(&::ones(&[4, 3])))[0];

    // to vec
    let mut collected = collect_parents_from(g).into_iter().collect::<Vec<Tensor>>();

    // sort by rank
    collected.sort_by_key(|t| t.borrow().rank);

    // tensor to name
    let sorted_names = collected
        .into_iter()
        .map(|t| t.borrow().op.name().to_string())
        .collect::<Vec<String>>();

    // compare
    let a = sorted_names;
    let b = vec![
        "Constant".to_string(), // one or x
        "Constant".to_string(), // one or x
        "ReverseAxes".to_string(), // transpose for x
        "MatMul".to_string(),
    ]; // MatMulGrad

    assert_eq!(a, b);
}
<commit_msg>Fix topological_ordering_on_reverse_mode<commit_after>extern crate ndarray;
extern crate fnv;

use self::fnv::FnvHashMap;
use ndarray_ext::NdArray;
use ops;
use std::collections::binary_heap::BinaryHeap;
use std::collections::hash_set::HashSet;
use std::mem;
use tensor::Tensor;

#[doc(hidden)]
#[inline]
/// Performs actual graph traversal and its evaluation
// TODO: loop-based rather than recursion (this would be difficult)
pub fn perform_eval(target: &Tensor, memo: &mut FnvHashMap<Tensor, NdArray>, train: bool)
{
    if memo.contains_key(target) {
        return;
    }
    let y = {
        let mut xs = vec![];
        {
            let ref inputs = target.borrow().inputs;
            // integrating loops below is impossible because of
            // "memo is already mutably borrowed"
            for input in inputs.iter() {
                perform_eval(input, memo, train);
            }
            for input in inputs.iter() {
                // unwrap is safe
                xs.push(memo.get(input).unwrap());
            }
        }

        target.borrow_mut().op.compute(xs.as_slice(), train)
    };

    // cache output
    memo.insert(target.clone(), y);
}

#[inline]
/// Makes mapping of {node => the node contributed to gradient or not}
fn contributed_to_grads(objective: &Tensor, variables: &[&Tensor]) -> FnvHashMap<Tensor, bool>
{
    fn rec(target: &Tensor, vars: &[&Tensor], memo: &mut FnvHashMap<Tensor, bool>)
    {
        if memo.contains_key(target) {
            return;
        }

        let mut contrib = false;

        if vars.contains(&target) {
            contrib = true;
        } else {
            for input in target.borrow().inputs.iter() {
                // recurse
                rec(input, vars, memo);
                // unwrap is always safe
                contrib |= *memo.get(input).unwrap();
            }
        }

        memo.insert(target.clone(), contrib);
    }

    let mut memo = FnvHashMap::default();
    rec(objective, variables, &mut memo);
    memo
}

#[test]
fn contributed_to_grads_test()
{
    // dummy graph
    let ref t = ::constant(::ndarray_ext::standard_normal(&[2, 3]));
    let ref v = ::variable(::ndarray_ext::standard_normal(&[2, 3]));
    let ref z = ::sigmoid_cross_entropy(&v, &t);
    let booleans = contributed_to_grads(z, &[v]);
    assert_eq!(booleans.len(), 3);
    assert!(!booleans.get(t).unwrap());
    assert!(booleans.get(v).unwrap());
    assert!(booleans.get(z).unwrap());
}

#[inline]
/// Returns symbolic gradient tensors.
///
/// This computes partial derivatives of `objective` with `variables` and returns the
/// gradients. This is achieved by building the subgraph between `objective` and
/// `variables` in reverse order from user's graph definition.
///
/// NOTE: Nodes that do not contribute to the gradient won't be included to avoid
/// unnecessary computation.
pub fn symbolic_gradients(
    objective: &Tensor,
    variables: &[&Tensor],
    initial_grad: Option<&Tensor>,
) -> Vec<Tensor>
{
    #[inline]
    fn maybe_reduce_grad(gys: &mut Vec<Tensor>)
    {
        if gys.len() < 2 {
            return;
        }
        let acc = {
            // values to refs
            let refs = gys.iter().map(|a| a).collect::<Vec<_>>();
            if refs.len() == 2 {
                // normal addition
                refs[0] + refs[1]
            } else {
                // For 3 or more gradients, AddN, i.e. inplace accumulation
                // is preferred for performance
                ops::add_n(refs.as_slice())
            }
        };
        mem::swap(&mut vec![acc], gys);
    }

    let initial_grad = initial_grad.map(|a| a.clone()).unwrap_or_else(
        || ops::scalar(1.),
    );

    // Mapping of {y => [gy]}
    let mut grads = FnvHashMap::default();

    // Mapping of {node => must visit or not (boolean)}
    let contrib = contributed_to_grads(objective, variables);

    // Prepare a heap with tensor's rank numbers for reversed
    // topological sort.
    let mut heap = BinaryHeap::new();
    heap.push(objective.clone());
    grads.insert(objective.clone(), vec![initial_grad]);

    // This prevents calling `grad()` twice or more
    let mut grad_done = HashSet::<Tensor>::new();

    // builds backward graph
    while let Some(target) = heap.pop() {
        let borrowed_target = target.borrow();

        // Vec<Tensor> to Vec<&Tensor>
        let xs = borrowed_target.inputs.iter().map(|a| a).collect::<Vec<_>>();

        // time to call `grad`
        let gxs = {
            if let Some(gys) = grads.get_mut(&target) {
                maybe_reduce_grad(gys);
                borrowed_target.op.grad(&gys[0], xs.as_slice(), &target)
            } else {
                unreachable!("Safe unwrapping is guaranteed by topological ordering")
            }
        };

        debug_assert_eq!(
            xs.len(),
            gxs.len(),
            "Wrong `grad` implementation of {}.",
            target
        );

        for (x, maybe_gx) in xs.into_iter().zip(gxs) {
            if !contrib.contains_key(x) {
                continue;
            }
            // cuts the backward path if gx is None.
            if let Some(gx) = maybe_gx {
                // memo gx
                if let Some(mut gys) = grads.remove(x) {
                    // gradient accumulation should be delayed here
                    gys.push(gx);
                    grads.insert(x.clone(), gys);
                } else {
                    grads.insert(x.clone(), vec![gx]);
                }
                // update heap
                if !x.is_source() && !grad_done.contains(x) {
                    grad_done.insert(x.clone());
                    heap.push(x.clone());
                }
            }
        }
    }

    variables
        .iter()
        .map(|v| {
            let mut gys = grads.remove(v).expect(
                "Input variable(s) didn't contributed to gradient computation",
            );
            maybe_reduce_grad(&mut gys);
            gys.remove(0)
        })
        .collect::<Vec<Tensor>>()
}

#[test]
fn topological_ordering_on_reverse_mode()
{
    fn collect_parents_from(endpoint: &Tensor) -> HashSet<Tensor>
    {
        let mut collected = HashSet::new();
        endpoint.visit_once(&mut |arg| { collected.insert(arg.clone()); });
        collected
    }

    let ref x = ::constant(::ndarray_ext::standard_normal(&[4, 2]));
    let ref w = ::variable(::ndarray_ext::standard_normal(&[2, 3]));
    let ref b = ::variable(::ndarray_ext::zeros(&[4, 3]));
    let ref z = ::matmul(x, w) + b;
    let ref g = ::gradients(z, &[w], Some(&::ones(&[4, 3])))[0];

    // to vec
    let mut collected = collect_parents_from(g).into_iter().collect::<Vec<Tensor>>();

    // sort by rank
    collected.sort_by_key(|t| t.borrow().rank);

    // tensor to name
    let sorted_names = collected
        .into_iter()
        .map(|t| t.borrow().op.name().to_string())
        .collect::<Vec<String>>();

    // compare
    let a = sorted_names;
    let b = vec![
        "Constant".to_string(), // one or x
        "Constant".to_string(), // one or x
        "MatMul".to_string(),
    ]; // MatMulGrad

    assert_eq!(a, b);
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod std;
extern mod extra;

use extra::test::{TestOpts, run_tests_console, TestDesc, TestDescAndFn, DynTestFn, DynTestName};
use extra::getopts::{getopts, reqopt};
use std::{os, str};
use std::cell::Cell;
use std::os::list_dir_path;
use std::rt::io::Reader;
use std::rt::io::process::{Process, ProcessConfig, Ignored, CreatePipe};

#[deriving(Clone)]
struct Config {
    source_dir: ~str,
    filter: Option<~str>
}

fn main() {
    let args = os::args();
    let config = parse_config(args);
    let opts = test_options(config.clone());
    let tests = find_tests(config);
    if !run_tests_console(&opts, tests) {
        os::set_exit_status(1);
    }
}

fn parse_config(args: ~[~str]) -> Config {
    let args = args.tail();
    let opts = ~[reqopt("source-dir")];
    let matches = match getopts(args, opts) {
      Ok(m) => m,
      Err(f) => fail!(f.to_err_msg())
    };

    Config {
        source_dir: matches.opt_str("source-dir").unwrap(),
        filter: if matches.free.is_empty() {
            None
        } else {
            Some((*matches.free.head()).clone())
        }
    }
}

fn test_options(config: Config) -> TestOpts {
    TestOpts {
        filter: config.filter,
        run_ignored: false,
        run_tests: true,
        run_benchmarks: false,
        ratchet_metrics: None,
        ratchet_noise_percent: None,
        save_metrics: None,
        test_shard: None,
        logfile: None
    }
}

fn find_tests(config: Config) -> ~[TestDescAndFn] {
    let mut files = list_dir_path(&Path::new(config.source_dir));
    files.retain(|file| file.extension_str() == Some("html") );
    return files.map(|file| make_test(file.display().to_str()) );
}

fn make_test(file: ~str) -> TestDescAndFn {
    let f = Cell::new(file.clone());
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(file),
            ignore: false,
            should_fail: false
        },
        testfn: DynTestFn(|| { run_test(f.take()) })
    }
}

fn run_test(file: ~str) {
    let path = os::make_absolute(&Path::new(file));
    // FIXME (#1094): not the right way to transform a path
    let infile = ~"file://" + path.display().to_str();
    let create_pipe = CreatePipe(true, false); // rustc #10228

    let config = ProcessConfig {
        program: "./servo",
        args: [~"-z", infile.clone()],
        env: None,
        cwd: None,
        io: [Ignored, create_pipe, Ignored]
    };

    let mut prc = Process::new(config).unwrap();
    let stdout = prc.io[1].get_mut_ref();
    let mut output = ~[];
    loop {
        let byte = stdout.read_byte();
        match byte {
            Some(byte) => {
                print!("{}", byte as char);
                output.push(byte);
            }
            None => break
        }
    }

    let out = str::from_utf8(output);
    let lines: ~[&str] = out.split_iter('\n').collect();
    for &line in lines.iter() {
        if line.contains("TEST-UNEXPECTED-FAIL") {
            fail!(line);
        }
    }
}
<commit_msg>contenttest: Print stderr<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern mod std;
extern mod extra;

use extra::test::{TestOpts, run_tests_console, TestDesc, TestDescAndFn, DynTestFn, DynTestName};
use extra::getopts::{getopts, reqopt};
use std::{os, str};
use std::cell::Cell;
use std::os::list_dir_path;
use std::rt::io::Reader;
use std::rt::io::process::{Process, ProcessConfig, Ignored, CreatePipe, InheritFd};

#[deriving(Clone)]
struct Config {
    source_dir: ~str,
    filter: Option<~str>
}

fn main() {
    let args = os::args();
    let config = parse_config(args);
    let opts = test_options(config.clone());
    let tests = find_tests(config);
    if !run_tests_console(&opts, tests) {
        os::set_exit_status(1);
    }
}

fn parse_config(args: ~[~str]) -> Config {
    let args = args.tail();
    let opts = ~[reqopt("source-dir")];
    let matches = match getopts(args, opts) {
      Ok(m) => m,
      Err(f) => fail!(f.to_err_msg())
    };

    Config {
        source_dir: matches.opt_str("source-dir").unwrap(),
        filter: if matches.free.is_empty() {
            None
        } else {
            Some((*matches.free.head()).clone())
        }
    }
}

fn test_options(config: Config) -> TestOpts {
    TestOpts {
        filter: config.filter,
        run_ignored: false,
        run_tests: true,
        run_benchmarks: false,
        ratchet_metrics: None,
        ratchet_noise_percent: None,
        save_metrics: None,
        test_shard: None,
        logfile: None
    }
}

fn find_tests(config: Config) -> ~[TestDescAndFn] {
    let mut files = list_dir_path(&Path::new(config.source_dir));
    files.retain(|file| file.extension_str() == Some("html") );
    return files.map(|file| make_test(file.display().to_str()) );
}

fn make_test(file: ~str) -> TestDescAndFn {
    let f = Cell::new(file.clone());
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(file),
            ignore: false,
            should_fail: false
        },
        testfn: DynTestFn(|| { run_test(f.take()) })
    }
}

fn run_test(file: ~str) {
    let path = os::make_absolute(&Path::new(file));
    // FIXME (#1094): not the right way to transform a path
    let infile = ~"file://" + path.display().to_str();
    let stdout = CreatePipe(true, false); // rustc #10228
    let stderr = InheritFd(2);

    let config = ProcessConfig {
        program: "./servo",
        args: [~"-z", infile.clone()],
        env: None,
        cwd: None,
        io: [Ignored, stdout, stderr]
    };

    let mut prc = Process::new(config).unwrap();
    let stdout = prc.io[1].get_mut_ref();
    let mut output = ~[];
    loop {
        let byte = stdout.read_byte();
        match byte {
            Some(byte) => {
                print!("{}", byte as char);
                output.push(byte);
            }
            None => break
        }
    }

    let out = str::from_utf8(output);
    let lines: ~[&str] = out.split_iter('\n').collect();
    for &line in lines.iter() {
        if line.contains("TEST-UNEXPECTED-FAIL") {
            fail!(line);
        }
    }
}
<|endoftext|>


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

#[derive(Debug)]
pub struct StatementStructResult {
    pub sql: String,
}
pub struct WhereStructResult {
    pub sql: String,
    pub 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
#[derive(Debug)]
pub struct Params {
    pub k: String,
    pub v: String,
}
// implement QueryString
#[derive(Debug)]
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),
        _ => String::from(""),
    };
    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 {
    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(","))
    })
}

// 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_without_params() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "name".to_string(),
            v: "toby".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_order(&qs) {
            Ok(result) => assert_eq!(result.sql, ""),
            Err(e) => assert_eq!("error on params", e),
        };
    }

    #[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>Add workaround to rust-postgres limitation I can not search some Int column with String values<commit_after>use rocket::request::{FromForm, FormItems};

#[derive(Debug)]
pub struct StatementStructResult {
    pub sql: String,
}
pub struct WhereStructResult {
    pub sql: String,
    pub 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
#[derive(Debug)]
pub struct Params {
    pub k: String,
    pub v: String,
}
// implement QueryString
#[derive(Debug)]
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()
        .filter(|param| !param.k.starts_with("_"))
        .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 {
                // biggest workaround ever!
                let commons = ["pk", "id"];
                if commons.iter().any(|c| param.k.contains(c)) {
                    Ok(format!("{}::text{}${}", param.k, result.0, pid))
                } 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),
        _ => String::from(""),
    };
    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 {
    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(","))
    })
}

// 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_with_orderby() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "user_id".to_string(),
            v: "5".to_string(),
        });
        hm.push(Params {
            k: "_order".to_string(),
            v: "user_id".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_without_params() {
        let mut hm = Vec::new();
        hm.push(Params {
            k: "name".to_string(),
            v: "toby".to_string(),
        });

        let qs = QueryString { query: hm };
        match ext_order(&qs) {
            Ok(result) => assert_eq!(result.sql, ""),
            Err(e) => assert_eq!("error on params", e),
        };
    }

    #[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>mod backend;
mod schema;
mod models;
pub mod show_karma;

use std::collections::HashMap;

use regex::Regex;

use ::bot::{Bot, Node, RoomEvent};
use self::backend::Backend;

pub struct UpvoteTracker {
    vote_db: Backend,
}

impl UpvoteTracker {
    pub fn new() -> Self {
        Self {
            vote_db: Backend::new()
        }
    }
}

struct VoteCount {
    up: i32,
    down: i32,
}

impl<'a> Node<'a> for UpvoteTracker {
    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        let event = event.raw_event;
        if event.type_ == "m.room.message" &&
            event.content["msgtype"] == "m.text" {
            let up_re = Regex::new(r"([^ ]+|\(.+?\))\+\+").unwrap();
            let dn_re = Regex::new(r"([^ ]+|\(.+?\))--").unwrap();

            let body = event.content["body"].as_str().unwrap();
            //println!("found karma for: {}", ent);

            let mut votes: HashMap<String, VoteCount> = HashMap::new();

            for cap in up_re.captures_iter(body) {
                let ent = cap[1].trim_matches(|c| c == '(' || c == ')').to_string();
                let e = votes.entry(ent).or_insert(VoteCount{up: 0, down: 0});
                (*e).up += 1;
            }

            for cap in dn_re.captures_iter(body) {
                let ent = cap[1].trim_matches(|c| c == '(' || c == ')').to_string();
                let e = votes.entry(ent).or_insert(VoteCount{up: 0, down: 0});
                (*e).down += 1;
            }

            for (k, v) in votes.iter() {
                println!("{} up: {} down: {}", &k, v.up, v.down);
                self.vote_db.vote(&event.sender, k, v.up, v.down);
            }
        }
    }
}
<commit_msg>Fix up/down voting regex to be more robust<commit_after>mod backend;
mod schema;
mod models;
pub mod show_karma;

use std::collections::HashMap;

use regex::Regex;

use ::bot::{Bot, Node, RoomEvent};
use self::backend::Backend;

pub struct UpvoteTracker {
    vote_db: Backend,
}

impl UpvoteTracker {
    pub fn new() -> Self {
        Self {
            vote_db: Backend::new()
        }
    }
}

struct VoteCount {
    up: i32,
    down: i32,
}

impl<'a> Node<'a> for UpvoteTracker {
    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        let event = event.raw_event;
        if event.type_ == "m.room.message" &&
            event.content["msgtype"] == "m.text" {
            let body = event.content["body"].as_str().unwrap();

            let mut votes: HashMap<String, VoteCount> = HashMap::new();

            let re = Regex::new(r"([^ ]+|\(.+?\))(\+\+|--)").unwrap();

            for cap in re.captures_iter(body) {
                let ent = cap[1].trim_matches(|c| c == '(' || c == ')').to_string();
                let e = votes.entry(ent).or_insert(VoteCount{up: 0, down: 0});

                if &cap[2] == "++" {
                    (*e).up += 1;
                }

                if &cap[2] == "--" {
                    (*e).down += 1;
                }
            }

            for (k, v) in votes.iter() {
                println!("{} up: {} down: {}", &k, v.up, v.down);
                self.vote_db.vote(&event.sender, k, v.up, v.down);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Describe type of device

///  cairo_device_type_t is used to describe the type of a given device. The devices types are also known as "backends" within cairo.
///
/// The device type can be queried with cairo_device_get_type()
///
/// The various cairo_device_t functions can be used with devices of any type, but some backends also provide type-specific functions that must only be called with a device of the appropriate type. These functions have names that begin with cairo_type_device such as cairo_xcb_device_debug_cap_xrender_version().
///
/// The behavior of calling a type-specific function with a device of the wrong type is undefined.
///
/// New entries may be added in future versions.
///
/// Since 1.10
#[repr(i32)]
pub enum DeviceType {
  /// The device is invalid, since 1.10
  Invalid = -1,
  /// The device is of type Direct Render Manager, since 1.10
  DRM = 0,
  /// The device is of type OpenGL, since 1.10
  GL = 1,
  /// The device is of type script, since 1.10
  Script = 2,
  /// The device is of type xcb, since 1.10
  XCB = 3,
  /// The device is of type xlib, since 1.10
  XLib = 4,
  /// The device is of type XML, since 1.10
  XML = 5,
  /// The device is of type cogl, since 1.12
  COGL = 6,
  /// The device is of type win32, since 1.12
  Win32 = 7
}

<commit_msg>Replaced Cairo '_t' types with Rust-Cairo types<commit_after>//! Describe type of device

/// surface::device_type::DeviceType is used to describe the type of a given device. The devices types are also known as "backends" within cairo.
///
/// The device type can be queried with cairo_device_get_type()
///
/// The various surface::Device functions can be used with devices of any type, but some backends also provide type-specific functions that must only be called with a device of the appropriate type. These functions have names that begin with cairo_type_device such as cairo_xcb_device_debug_cap_xrender_version().
///
/// The behavior of calling a type-specific function with a device of the wrong type is undefined.
///
/// New entries may be added in future versions.
///
/// Since 1.10
#[repr(i32)]
pub enum DeviceType {
  /// The device is invalid, since 1.10
  Invalid = -1,
  /// The device is of type Direct Render Manager, since 1.10
  DRM = 0,
  /// The device is of type OpenGL, since 1.10
  GL = 1,
  /// The device is of type script, since 1.10
  Script = 2,
  /// The device is of type xcb, since 1.10
  XCB = 3,
  /// The device is of type xlib, since 1.10
  XLib = 4,
  /// The device is of type XML, since 1.10
  XML = 5,
  /// The device is of type cogl, since 1.12
  COGL = 6,
  /// The device is of type win32, since 1.12
  Win32 = 7
}

<|endoftext|>


<commit_before>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.to_string())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, SyntaxDefinition>::new();

    let mut writer = XmlWriter::new(Vec::<u8>::new());
    let mut state = ParseState::default();

    for event in XmlReader::from(html_string.as_str()) {
        let event = match event {
            Ok(event) => event,
            Err(_) => continue
        };

        let parse_event = ParseEvent::from(&event);
        state = ParseState::next(state, parse_event);

        let language = match state.clone() {
            ParseState::InCodeBlock(language) => language,
            _ => {
                assert!(writer.write(event).is_ok());
                continue;
            }
        };

        let unescaped_content = match event.element().unescaped_content() {
            Ok(content) => content.into_owned(),
            Err(_) => {
                assert!(writer.write(event.clone()).is_ok());
                continue;
            }
        };

        let content_to_highlight = match str::from_utf8(&unescaped_content) {
            Ok(utf8_str) => utf8_str,
            Err(_) => {
                assert!(writer.write(event.clone()).is_ok());
                continue;
            }
        };

        let syntax_key = language.clone();
        let syntax_definition = syntax_definitions.entry(syntax_key).or_insert({
            match ss.find_syntax_by_token(&language) {
                Some(valid_syntax) => valid_syntax.clone(),
                None => {
                    assert!(writer.write(event.clone()).is_ok());
                    continue;
                }
            }
        });

        let highlighted =
            highlighted_snippet_for_string(content_to_highlight, syntax_definition, &theme);

        let text = Element::new(highlighted);
        assert!(writer.write(Event::Text(text)).is_ok());
    }

    String::from_utf8(writer.into_inner()).unwrap_or(html_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::MaybeStartBlock(lang.to_string()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.to_string()));

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string())
                       .next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.to_string()));
    }
}
<commit_msg>Postpone cloning ParseState data until necessary<commit_after>//! Provide syntax highlighting via [Syntect].
//!
//! [Syntect]: https://docs.rs/syntect/1.0.0/syntect/

// Standard library
use std::default::Default;
use std::collections::HashMap;
use std::str;

// Third party
use quick_xml::{XmlReader, XmlWriter, Element, Event};
use syntect::html::highlighted_snippet_for_string;
use syntect::highlighting::Theme;
use syntect::parsing::{SyntaxDefinition, SyntaxSet};

/// A `Language` is a `String` representing a code highlighting language.
type Language = String;

/// Define XML parsing states of interest for highlighting.
///
/// We use these in conjunction with `ParseEvent`s to drive a state machine
/// which tracks whether a stream of `quick_xml::Event`s indicates the presence
/// of a code block to be highlighted.
#[derive(Clone, Debug, PartialEq)]
enum ParseState {
    /// Not in a code block at all.
    NotInBlock,
    /// Possibly the start of a highlight block, with the language as payload.
    MaybeStartBlock(Language),
    /// The start of the code block has been confirmed; highlight next text node.
    WillStartCodeBlock(Language),
    /// In a code block; use the content for highlighting.
    InCodeBlock(Language),
}

/// Define XML parsing events of interest for highlighting.
///
/// We don't care at all about any other items for this specific scenario; we
/// use these in conjunction with the `ParseState` to drive the state  machine
/// described there.
#[derive(Debug)]
enum ParseEvent {
    // Starting a `<pre>`; optionally specifying a language.
    StartPre(Option<Language>),
    // Starting a `<code>`.
    StartCode,
    // Ending a `<code>`.
    EndCode,
    Text,
    Whitespace,
    Other,
}

impl ParseState {
    /// Get the next `ParseState` given current `ParseState` and a `ParseEvent`.
    fn next(self, event: ParseEvent) -> ParseState {
        use self::ParseState::*;
        use self::ParseEvent::*;

        match (self, event) {
            (NotInBlock, StartPre(Some(language))) => MaybeStartBlock(language),
            (MaybeStartBlock(language), Whitespace) => MaybeStartBlock(language),
            (MaybeStartBlock(language), StartCode) => WillStartCodeBlock(language),
            (WillStartCodeBlock(language), Text) |
            (WillStartCodeBlock(language), Whitespace) => InCodeBlock(language),
            (InCodeBlock(_), EndCode) => NotInBlock,
            (_, _) => NotInBlock,
        }
    }
}

impl Default for ParseState {
    fn default() -> ParseState {
        ParseState::NotInBlock
    }
}

impl<'e> From<&'e Event> for ParseEvent {
    fn from(event: &'e Event) -> ParseEvent {
        // TODO @1.15: remove `'static`.
        const PRE: &'static [u8] = b"pre";
        const CODE: &'static [u8] = b"code";
        const CLASS: &'static [u8] = b"class";
        const WHITE_SPACE: &'static [u8] = b"";

        match *event {
            Event::Start(ref element) => {
                match element.name() {
                    PRE => {
                        let maybe_class_attr = element.attributes()
                            .map(|attr| attr.unwrap())
                            .filter(|&(attr, _value)| attr == CLASS)
                            .next();

                        if let Some((_attr, value)) = maybe_class_attr {
                            match str::from_utf8(value) {
                                Ok(lang) => ParseEvent::StartPre(Some(lang.to_string())),
                                Err(_) => ParseEvent::StartPre(None),
                            }
                        } else {
                            ParseEvent::StartPre(None)
                        }
                    }
                    CODE => ParseEvent::StartCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::End(ref element) => {
                match element.name() {
                    CODE => ParseEvent::EndCode,
                    _ => ParseEvent::Other,
                }
            }
            Event::Text(ref element) => {
                match element.name() {
                    WHITE_SPACE => ParseEvent::Whitespace,
                    _ => ParseEvent::Text,
                }
            }
            _ => ParseEvent::Other,
        }
    }
}

/// Highlight all code blocks in a block of HTML.
///
/// Assumes that the blocks to be highlighted are in the following basic format:
///
/// ```html
/// <pre class="rust">
///   <code>
///     pub fn syntax_highlight(html_string: String) -> String {
///         // implementation details
///     }
///   </code>
/// </pre>
/// ```
///
/// The `class` attribute value from the `pre` tag defines the language used to
/// highlight the code.
///
/// Note that any `html_string` will do; if it cannot be parsed as XML, it will
/// simply be returned unchanged; and if there are no code blocks to highlight,
/// it will also be returned unchanged.
pub fn syntax_highlight(html_string: String, theme: &Theme) -> String {
    let ss = SyntaxSet::load_defaults_nonewlines();
    let mut syntax_definitions = HashMap::<Language, SyntaxDefinition>::new();

    let mut writer = XmlWriter::new(Vec::<u8>::new());
    let mut state = ParseState::default();

    for event in XmlReader::from(html_string.as_str()) {
        let event = match event {
            Ok(event) => event,
            Err(_) => continue
        };

        let parse_event = ParseEvent::from(&event);
        state = ParseState::next(state, parse_event);

        let language = match state {
            ParseState::InCodeBlock(ref language) => language.clone(),
            _ => {
                assert!(writer.write(event).is_ok());
                continue;
            }
        };

        let unescaped_content = match event.element().unescaped_content() {
            Ok(content) => content.into_owned(),
            Err(_) => {
                assert!(writer.write(event.clone()).is_ok());
                continue;
            }
        };

        let content_to_highlight = match str::from_utf8(&unescaped_content) {
            Ok(utf8_str) => utf8_str,
            Err(_) => {
                assert!(writer.write(event.clone()).is_ok());
                continue;
            }
        };

        let syntax_key = language.clone();
        let syntax_definition = syntax_definitions.entry(syntax_key).or_insert({
            match ss.find_syntax_by_token(&language) {
                Some(valid_syntax) => valid_syntax.clone(),
                None => {
                    assert!(writer.write(event.clone()).is_ok());
                    continue;
                }
            }
        });

        let highlighted =
            highlighted_snippet_for_string(content_to_highlight, syntax_definition, &theme);

        let text = Element::new(highlighted);
        assert!(writer.write(Event::Text(text)).is_ok());
    }

    String::from_utf8(writer.into_inner()).unwrap_or(html_string)
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_parse_state() {
        use super::ParseState;
        use super::ParseEvent;

        let lang = "rust";

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::MaybeStartBlock(lang.to_string()));

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::NotInBlock.next(ParseEvent::StartCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::StartCode),
                   ParseState::WillStartCodeBlock(lang.to_string()));

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::EndCode),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string())
                       .next(ParseEvent::StartPre(Some(lang.to_string()))),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::MaybeStartBlock(lang.to_string()).next(ParseEvent::Other),
                   ParseState::NotInBlock);

        assert_eq!(ParseState::WillStartCodeBlock(lang.to_string()).next(ParseEvent::Text),
                   ParseState::InCodeBlock(lang.to_string()));
    }
}
<|endoftext|>


<commit_before>use aes::{unpad_inplace, Aes128, AesError, BLOCK_SIZE, MODE};
use errors::*;
use rand;
use rand::Rng;
use serialize::from_base64;
use xor::XOR;

use aes::random_block;

struct Server17 {
    key: Vec<u8>,
}

impl Server17 {
    fn new() -> Self {
        Self {
            key: random_block(),
        }
    }

    fn get_session_token(&self) -> Result<(Vec<u8>, Vec<u8>), Error> {
        let inputs = [
            "MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc=",
            "MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIHB1bXBpbic=",
            "MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw==",
            "MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg==",
            "MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl",
            "MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA==",
            "MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw==",
            "MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8=",
            "MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g=",
            "MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93",
        ];
        let mut rng = rand::thread_rng();
        let input_index = rng.gen_range(0, 10);
        let input = from_base64(inputs[input_index]).unwrap();
        let iv = random_block();
        let ciphertext = input.encrypt(&self.key, Some(&iv), MODE::CBC)?;
        Ok((ciphertext, iv))
    }

    fn is_padding_valid(&self, iv: &[u8], ciphertext: &[u8]) -> Result<bool, Error> {
        //match ciphertext.decrypt(&self.key, Some(iv), MODE::CBC) {
        //    Err(aes::Error(aes::ErrorKind::InvalidPadding, _)) => Ok(false),
        //    Err(e) => Err(e.into()),
        //    _ => Ok(true),
        //}
        if let Err(error) = ciphertext.decrypt(&self.key, Some(iv), MODE::CBC) {
            if let Some(&AesError::InvalidPadding) = error.downcast_ref::<AesError>() {
                Ok(false)
            } else {
                Err(error)
            }
        } else {
            Ok(true)
        }
    }

    fn verify_solution(&self, cleartext: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<(), Error> {
        compare_eq(
            &ciphertext.decrypt(&self.key, Some(iv), MODE::CBC)?[..],
            cleartext,
        )
    }
}

pub fn run() -> Result<(), Error> {
    let server = Server17::new();
    let (ciphertext, iv) = server.get_session_token()?;
    let mut cleartext = vec![0; ciphertext.len()];
    let mut prev = iv.clone();
    for (block_index, block) in ciphertext.chunks(BLOCK_SIZE).enumerate() {
        let block_offset = block_index * BLOCK_SIZE;
        for i in (0..BLOCK_SIZE).rev() {
            let padding = (BLOCK_SIZE - i) as u8;
            prev[i + 1..].xor_inplace(&[(padding - 1) ^ padding]);
            for u in 0u8..=255 {
                prev[i] ^= u;
                if server.is_padding_valid(&prev, block)?
                    && (i < BLOCK_SIZE - 1 || {
                        prev[i - 1] ^= 1;
                        let result = server.is_padding_valid(&prev, block)?;
                        prev[i - 1] ^= 1;
                        result
                    }) {
                    cleartext[block_offset + i] = padding ^ u;
                    break;
                }
                prev[i] ^= u;
            }
        }
        prev = block.to_vec();
    }
    unpad_inplace(&mut cleartext, BLOCK_SIZE as u8)?;
    server.verify_solution(&cleartext, &iv, &ciphertext)
}
<commit_msg>More comments<commit_after>use aes::{unpad_inplace, Aes128, AesError, BLOCK_SIZE, MODE};
use errors::*;
use rand;
use rand::Rng;
use serialize::from_base64;
use xor::XOR;

use aes::random_block;

struct Server17 {
    key: Vec<u8>,
}

impl Server17 {
    fn new() -> Self {
        Self {
            key: random_block(),
        }
    }

    fn get_session_token(&self) -> Result<(Vec<u8>, Vec<u8>), Error> {
        let inputs = [
            "MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc=",
            "MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIHB1bXBpbic=",
            "MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw==",
            "MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg==",
            "MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl",
            "MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA==",
            "MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw==",
            "MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8=",
            "MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g=",
            "MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93",
        ];
        let mut rng = rand::thread_rng();
        let input_index = rng.gen_range(0, inputs.len());
        let input = from_base64(inputs[input_index]).unwrap();
        let iv = random_block();
        let ciphertext = input.encrypt(&self.key, Some(&iv), MODE::CBC)?;
        Ok((iv, ciphertext))
    }

    fn is_padding_valid(&self, iv: &[u8], ciphertext: &[u8]) -> Result<bool, Error> {
        if let Err(error) = ciphertext.decrypt(&self.key, Some(iv), MODE::CBC) {
            if let Some(&AesError::InvalidPadding) = error.downcast_ref::<AesError>() {
                Ok(false)
            } else {
                Err(error)
            }
        } else {
            Ok(true)
        }
    }

    fn verify_solution(&self, cleartext: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<(), Error> {
        compare_eq(
            &ciphertext.decrypt(&self.key, Some(iv), MODE::CBC)?[..],
            cleartext,
        )
    }
}

pub fn run() -> Result<(), Error> {
    let server = Server17::new();
    let (iv, ciphertext) = server.get_session_token()?;
    let mut cleartext = vec![0; ciphertext.len()];
    let mut prev = iv.clone();
    for (block_index, block) in ciphertext.chunks(BLOCK_SIZE).enumerate() {
        let block_offset = block_index * BLOCK_SIZE;
        for i in (0..BLOCK_SIZE).rev() {
            let padding = (BLOCK_SIZE - i) as u8;
            prev[i + 1..].xor_inplace(&[(padding - 1) ^ padding]);
            for u in 0u8..=255 {
                prev[i] ^= u;
                if server.is_padding_valid(&prev, block)?
                    && (i < BLOCK_SIZE - 1 || {
                        // The last byte of the block requires a special treatment because the padding could
                        // accidentally be valid if we have for example flipped the last byte to the value 2
                        // and the second to last byte of the cleartext also happens to be a 2. We therefore
                        // change the second to last byte and test the padding again. If it is still
                        // valid, we can be sure that the last byte has been flipped to the value 1.
                        prev[i - 1] ^= 1;
                        let result = server.is_padding_valid(&prev, block)?;
                        prev[i - 1] ^= 1;
                        result
                    }) {
                    cleartext[block_offset + i] = padding ^ u;
                    break;
                }
                prev[i] ^= u;
            }
        }
        prev = block.to_vec();
    }
    unpad_inplace(&mut cleartext, BLOCK_SIZE as u8)?;
    server.verify_solution(&cleartext, &iv, &ciphertext)
}
<|endoftext|>


<commit_before>use std::io;
use std::mem;

use httparse;

use tracker::errors::{ErrorKind, Result};
use util::{aread, IOR};

pub struct Reader {
    data: Vec<u8>,
    idx: usize,
    state: ReadState,
}

pub enum ReadRes {
    None,
    Done(Vec<u8>),
    Redirect(String),
}

enum ReadState {
    Header,
    Body,
}

impl Reader {
    pub fn new() -> Reader {
        Reader {
            data: vec![0; 75],
            idx: 0,
            state: ReadState::Header,
        }
    }

    pub fn readable<R: io::Read>(&mut self, conn: &mut R) -> Result<ReadRes> {
        loop {
            match aread(&mut self.data[self.idx..], conn) {
                IOR::Complete => {
                    self.idx = self.data.len();
                    let new_len = (self.idx as f32 * 1.5) as usize;
                    self.data.resize(new_len, 0u8);
                }
                IOR::Incomplete(a) => {
                    self.idx += a;
                    let mut header_done = None;
                    match self.state {
                        ReadState::Header => {
                            let mut headers = [httparse::EMPTY_HEADER; 16];
                            let mut resp = httparse::Response::new(&mut headers);
                            match resp.parse(&self.data[..self.idx]) {
                                Ok(httparse::Status::Complete(i)) => {
                                    // Redirect handling
                                    let redirect_codes = [301, 302, 303, 307, 308];
                                    if resp
                                        .code
                                        .as_ref()
                                        .map(|c| redirect_codes.contains(c))
                                        .unwrap_or(false)
                                    {
                                        let loc = resp
                                            .headers
                                            .iter()
                                            .find(|h| h.name == "Location")
                                            .and_then(|h| String::from_utf8(h.value.to_vec()).ok());
                                        if loc.is_none() {
                                            return Err(ErrorKind::InvalidResponse(
                                                "malformed HTTP",
                                            )
                                            .into());
                                        }
                                        return Ok(ReadRes::Redirect(loc.unwrap()));
                                    }
                                    header_done = Some(i);
                                }
                                Ok(httparse::Status::Partial) => {}
                                Err(_) => {
                                    return Err(ErrorKind::InvalidResponse("malformed HTTP").into());
                                }
                            }
                        }
                        ReadState::Body => {}
                    }
                    if let Some(i) = header_done {
                        let body = self.data.split_off(i);
                        self.idx -= self.data.len();
                        self.data = body;
                        self.state = ReadState::Body;
                    }
                }
                IOR::Blocked => return Ok(ReadRes::None),
                IOR::EOF => match self.state {
                    ReadState::Body => {
                        let mut data = mem::replace(&mut self.data, Vec::with_capacity(0));
                        data.truncate(self.idx);
                        return Ok(ReadRes::Done(data));
                    }
                    _ => return Err(ErrorKind::EOF.into()),
                },
                IOR::Err(_) => return Err(ErrorKind::IO.into()),
            }
        }
    }
}
<commit_msg>Ensure tracker response is always fully processed<commit_after>use std::io;
use std::mem;

use httparse;

use tracker::errors::{ErrorKind, Result};
use util::{aread, IOR};

pub struct Reader {
    data: Vec<u8>,
    idx: usize,
    state: ReadState,
}

pub enum ReadRes {
    None,
    Done(Vec<u8>),
    Redirect(String),
}

enum ReadState {
    Header,
    Body,
}

impl Reader {
    pub fn new() -> Reader {
        Reader {
            data: vec![0; 75],
            idx: 0,
            state: ReadState::Header,
        }
    }

    pub fn readable<R: io::Read>(&mut self, conn: &mut R) -> Result<ReadRes> {
        loop {
            match aread(&mut self.data[self.idx..], conn) {
                IOR::Complete => {
                    self.idx = self.data.len();
                    let new_len = (self.idx as f32 * 1.5) as usize;
                    self.data.resize(new_len, 0u8);
                    if let Some(result) = self.process_data()? {
                        return Ok(result);
                    }
                }
                IOR::Incomplete(a) => {
                    self.idx += a;
                    if let Some(result) = self.process_data()? {
                        return Ok(result);
                    }
                }
                IOR::Blocked => return Ok(ReadRes::None),
                IOR::EOF => match self.state {
                    ReadState::Body => {
                        let mut data = mem::replace(&mut self.data, Vec::with_capacity(0));
                        data.truncate(self.idx);
                        return Ok(ReadRes::Done(data));
                    }
                    _ => return Err(ErrorKind::EOF.into()),
                },
                IOR::Err(_) => return Err(ErrorKind::IO.into()),
            }
        }
    }

    fn process_data(&mut self) -> Result<Option<ReadRes>> {
        let mut header_done = None;
        match self.state {
            ReadState::Header => {
                let mut headers = [httparse::EMPTY_HEADER; 16];
                let mut resp = httparse::Response::new(&mut headers);
                match resp.parse(&self.data[..self.idx]) {
                    Ok(httparse::Status::Complete(i)) => {
                        // Redirect handling
                        let redirect_codes = [301, 302, 303, 307, 308];
                        if resp
                            .code
                            .as_ref()
                            .map(|c| redirect_codes.contains(c))
                            .unwrap_or(false)
                        {
                            let loc = resp
                                .headers
                                .iter()
                                .find(|h| h.name == "Location")
                                .and_then(|h| String::from_utf8(h.value.to_vec()).ok());
                            if loc.is_none() {
                                return Err(ErrorKind::InvalidResponse(
                                    "malformed HTTP",
                                )
                                .into());
                            }
                            return Ok(Some(ReadRes::Redirect(loc.unwrap())));
                        }
                        header_done = Some(i);
                    }
                    Ok(httparse::Status::Partial) => {}
                    Err(_) => {
                        return Err(ErrorKind::InvalidResponse("malformed HTTP").into());
                    }
                }
            }
            ReadState::Body => {}
        }
        if let Some(i) = header_done {
            let body = self.data.split_off(i);
            self.idx -= self.data.len();
            self.data = body;
            self.state = ReadState::Body;
        }
        Ok(None)
    }
}
<|endoftext|>


<commit_before>#![feature(staged_api)]
#![feature(test)]
#![unstable(feature = "test", issue = "0")]

extern crate test;

pub use test::*;

pub fn custom_api() {
}
<commit_msg>Auto merge of #7738 - JohnTitor:issue-none, r=ehuss<commit_after>#![feature(staged_api)]
#![feature(test)]
#![unstable(feature = "test", issue = "none")]

extern crate test;

pub use test::*;

pub fn custom_api() {
}
<|endoftext|>