<commit_before>// #![feature(phase)]
// #[phase(plugin, link)] extern crate log;

use racer::codecleaner::{code_chunks,CodeIndicesIter};
// use codecleaner::{code_chunks,CodeIndicesIter};
// mod codecleaner;

#[cfg(test)] use racer::testutils::{rejustify, slice};

pub struct StmtIndicesIter<'a> {
    src: &'a str,
    it: CodeIndicesIter<'a>,
    pos: usize,
    start: usize,
    end: usize,
    bracelevel: i32,
    parenlevel: i32,
    is_macro: bool,
    enddelim: u8
}

impl<'a> Iterator for StmtIndicesIter<'a> {
    type Item = (usize, usize);

    #[inline]
    fn next(&mut self) -> Option<(usize, usize)> {
        let semicolon: u8 = ";".as_bytes()[0];
        let hash: u8 = "#".as_bytes()[0];
        let openbrace: u8 = "{".as_bytes()[0];
        let closebrace: u8 = "}".as_bytes()[0];
        let openparen: u8 = "(".as_bytes()[0];
        let closeparen: u8 = ")".as_bytes()[0];
        let closesqbrace: u8 = "]".as_bytes()[0];
        let bang: u8 = "!".as_bytes()[0];
        let whitespace = " \t\r\n".as_bytes();

        let __u: u8 = "u".as_bytes()[0];
        let __s: u8 = "s".as_bytes()[0];
        let __e: u8 = "e".as_bytes()[0];

        let src_bytes = self.src.as_bytes();

        loop {

            // do we need the next chunk?
            if self.end <= self.pos {
                // get the next chunk of code
                match self.it.next() {
                    Some((start, end)) => {
                        self.end = end;
                        if self.start == self.pos {
                            self.start = start;
                        }
                        self.pos = start;
                    }
                    None => {
                        // no more chunks. finished
                        if self.start < self.end {
                            let start = self.start;
                            self.start = self.end;
                            return Some((start, self.end));
                        } else {
                            return None
                        }

                    }
                }
            }

            // if this is a new stmt block, skip the whitespace
            if self.pos == self.start {
                while self.pos < self.end {
                    if !whitespace.contains(&src_bytes[self.pos]) {
                        break;
                    } else {
                        self.pos += 1;
                    }
                }
                self.start = self.pos;
            }

            // if the statement starts with 'macro_rules!' then we're in a macro
            // We need to know this because a closeparen can terminate a macro
            if (self.end - self.pos) > 12 &&
               self.src.slice(self.pos, self.pos+12) == "macro_rules!" {
                self.is_macro = true;
            }

            // iterate through the chunk, looking for stmt end
            while self.pos < self.end {
                if src_bytes[self.pos] == openparen {

                    // macros can be terminated by closeparen if opened by one
                    if self.is_macro &&
                        self.bracelevel == 0 &&
                        self.parenlevel == 0 {
                        self.enddelim = closeparen;
                    }

                    // also macro invocations can too
                    if self.pos > 0 && src_bytes[self.pos-1] == bang &&
                        self.bracelevel == 0 &&
                        self.parenlevel == 0 {
                        self.enddelim = closeparen;
                    }

                    self.parenlevel += 1;

                } else if src_bytes[self.pos] == closeparen {
                    self.parenlevel -= 1;

                } else if src_bytes[self.pos] == openbrace {
                    // if we are top level and stmt is not a 'use' then
                    // closebrace finishes the stmt
                    if self.bracelevel == 0 &&
                        self.parenlevel == 0 &&
                       !(is_a_use_stmt(self.src, self.start, self.pos)) {
                           self.enddelim = closebrace;
                    }
                    self.bracelevel += 1;

                } else if src_bytes[self.pos] == closebrace {
                    self.bracelevel -= 1;
                    // have we reached the end of the scope?
                    if self.bracelevel < 0 {
                        return None;
                    }
                }

                // attribute   #[foo = bar]
                if self.bracelevel == 0 && self.start == self.pos &&
                    src_bytes[self.pos] == hash {
                    self.enddelim = closesqbrace;
                }

                if self.bracelevel == 0 && self.parenlevel == 0 &&
                   src_bytes[self.pos] == self.enddelim {
                    let start = self.start;
                    self.start = self.pos+1;
                    self.pos = self.pos+1;
                    self.enddelim = semicolon;
                    self.is_macro = false;
                    return Some((start, self.pos));
                }

                self.pos += 1;
            }

        }
    }
}

fn is_a_use_stmt(src: &str, start: usize, pos: usize) -> bool {
    let src_bytes = src.as_bytes();
    let whitespace = " {\t\r\n".as_bytes();
    (pos > 3 && src_bytes.slice(start, start+3) == "use".as_bytes() &&
     whitespace.contains(&src_bytes[start+3])) ||
        (pos > 7 && src_bytes.slice(start, start+7) == "pub use".as_bytes() &&
                      whitespace.contains(&src_bytes[start+7]))
}

pub fn iter_stmts<'a>(src: &'a str) -> StmtIndicesIter<'a> {
    let semicolon: u8 = ";".as_bytes()[0];
    StmtIndicesIter{src: src, it: code_chunks(src),
                    pos: 0, start: 0, end: 0, bracelevel: 0,
                    parenlevel: 0, enddelim: semicolon, is_macro: false}
}

#[test]
fn iterates_single_use_stmts() {
    let src = rejustify("
    use std::Foo; // a comment
    use std::Bar;
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("use std::Foo;", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("use std::Bar;", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_use_stmt_over_two_lines() {
    let src = rejustify("
    use std::{Foo,
              Bar}; // a comment
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("use std::{Foo,
          Bar};", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_use_stmt_without_the_prefix() {
    let src = rejustify("
    pub use {Foo,
              Bar}; // this is also legit apparently
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("pub use {Foo,
          Bar};", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_while_stmt() {
    let src = rejustify("
    while self.pos < 3 { }
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("while self.pos < 3 { }", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_lambda_arg() {
    let src = rejustify("
    myfn(|n|{});
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("myfn(|n|{});", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_macro() {
    let src = "
    mod foo;
    macro_rules! otry(
        ($e:expr) => (match $e { Some(e) => e, None => return })
    )
    mod bar;
    ";
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("mod foo;", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("macro_rules! otry(
        ($e:expr) => (match $e { Some(e) => e, None => return })
    )", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("mod bar;", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_macro_invocation() {
    let src = "
    mod foo;
    local_data_key!(local_stdout: Box<Writer + Send>)  // no ';'
    mod bar;
    ";
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("mod foo;", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("local_data_key!(local_stdout: Box<Writer + Send>)", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("mod bar;", slice(src.as_slice(), it.next().unwrap()));
}

// #[test]
// fn iterates_if_else_stmt() {
//     let src = rejustify("
//     if self.pos < 3 { } else { }
//     ");
//     let mut it = iter_stmts(src.as_slice());
//     assert_eq!("if self.pos < 3 { } else { }", slice(src.as_slice(), it.next().unwrap()));
// }

#[test]
fn iterates_inner_scope() {
    let src = "
    while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    }
    {
       b = foo;       // but not this
    }
    ";

    let scope = src.as_slice().slice_from(25);
    let mut it = iter_stmts(scope);

    assert_eq!("let a = 35;", slice(scope, it.next().unwrap()));
    assert_eq!("return a + 35;", slice(scope, it.next().unwrap()));
    assert_eq!(None, it.next());
}

#[test]
fn iterates_module_attribute() {
    let src = rejustify("
    #![license = \"BSD\"]
    #[test]
    ");
    let mut it = iter_stmts(src.as_slice());
    assert_eq!("#![license = \"BSD\"]", slice(src.as_slice(), it.next().unwrap()));
    assert_eq!("#[test]", slice(src.as_slice(), it.next().unwrap()));
}

#[test]
fn iterates_half_open_subscope_if_is_the_last_thing() {
    let src = "
    let something = 35;
    while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    ";

    let scope = src;
    let mut it = iter_stmts(scope);
    assert_eq!("let something = 35;", slice(scope, it.next().unwrap()));
    assert_eq!("while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    ", slice(scope, it.next().unwrap()));
}

// fn main() {
//     use std::io::BufferedReader;
//     use std::io::File;
//     use std::str;

//     let filetxt = BufferedReader::new(File::open(&Path::new("/usr/local/src/rust/src/libstd/io/stdio.rs"))).read_to_end().unwrap();
//     let src = str::from_utf8(filetxt.as_slice()).unwrap();

//     for (start,end) in iter_stmts(src) {
//         println!("BLOB |{}|",src.slice(start,end));
//     }

// }
<commit_msg>Clean codeiter<commit_after>// #![feature(phase)]
// #[phase(plugin, link)] extern crate log;

use racer::codecleaner::{code_chunks,CodeIndicesIter};
// use codecleaner::{code_chunks,CodeIndicesIter};
// mod codecleaner;

#[cfg(test)] use racer::testutils::{rejustify, slice};

pub struct StmtIndicesIter<'a> {
    src: &'a str,
    it: CodeIndicesIter<'a>,
    pos: usize,
    start: usize,
    end: usize,
    bracelevel: i32,
    parenlevel: i32,
    is_macro: bool,
    enddelim: u8
}

impl<'a> Iterator for StmtIndicesIter<'a> {
    type Item = (usize, usize);

    #[inline]
    fn next(&mut self) -> Option<(usize, usize)> {
        let semicolon: u8 = ";".as_bytes()[0];
        let hash: u8 = "#".as_bytes()[0];
        let openbrace: u8 = "{".as_bytes()[0];
        let closebrace: u8 = "}".as_bytes()[0];
        let openparen: u8 = "(".as_bytes()[0];
        let closeparen: u8 = ")".as_bytes()[0];
        let closesqbrace: u8 = "]".as_bytes()[0];
        let bang: u8 = "!".as_bytes()[0];
        let whitespace = " \t\r\n".as_bytes();

        let __u: u8 = "u".as_bytes()[0];
        let __s: u8 = "s".as_bytes()[0];
        let __e: u8 = "e".as_bytes()[0];

        let src_bytes = self.src.as_bytes();

        loop {

            // do we need the next chunk?
            if self.end <= self.pos {
                // get the next chunk of code
                match self.it.next() {
                    Some((start, end)) => {
                        self.end = end;
                        if self.start == self.pos {
                            self.start = start;
                        }
                        self.pos = start;
                    }
                    None => {
                        // no more chunks. finished
                        if self.start < self.end {
                            let start = self.start;
                            self.start = self.end;
                            return Some((start, self.end));
                        } else {
                            return None
                        }

                    }
                }
            }

            // if this is a new stmt block, skip the whitespace
            if self.pos == self.start {
                while self.pos < self.end {
                    if !whitespace.contains(&src_bytes[self.pos]) {
                        break;
                    } else {
                        self.pos += 1;
                    }
                }
                self.start = self.pos;
            }

            // if the statement starts with 'macro_rules!' then we're in a macro
            // We need to know this because a closeparen can terminate a macro
            if (self.end - self.pos) > 12 &&
                &self.src[self.pos..self.pos+12] == "macro_rules!" {
                self.is_macro = true;
            }

            // iterate through the chunk, looking for stmt end
            while self.pos < self.end {
                if src_bytes[self.pos] == openparen {

                    // macros can be terminated by closeparen if opened by one
                    if self.is_macro &&
                        self.bracelevel == 0 &&
                        self.parenlevel == 0 {
                        self.enddelim = closeparen;
                    }

                    // also macro invocations can too
                    if self.pos > 0 && src_bytes[self.pos-1] == bang &&
                        self.bracelevel == 0 &&
                        self.parenlevel == 0 {
                        self.enddelim = closeparen;
                    }

                    self.parenlevel += 1;

                } else if src_bytes[self.pos] == closeparen {
                    self.parenlevel -= 1;

                } else if src_bytes[self.pos] == openbrace {
                    // if we are top level and stmt is not a 'use' then
                    // closebrace finishes the stmt
                    if self.bracelevel == 0 &&
                        self.parenlevel == 0 &&
                       !(is_a_use_stmt(self.src, self.start, self.pos)) {
                           self.enddelim = closebrace;
                    }
                    self.bracelevel += 1;

                } else if src_bytes[self.pos] == closebrace {
                    self.bracelevel -= 1;
                    // have we reached the end of the scope?
                    if self.bracelevel < 0 {
                        return None;
                    }
                }

                // attribute   #[foo = bar]
                if self.bracelevel == 0 && self.start == self.pos &&
                    src_bytes[self.pos] == hash {
                    self.enddelim = closesqbrace;
                }

                if self.bracelevel == 0 && self.parenlevel == 0 &&
                   src_bytes[self.pos] == self.enddelim {
                    let start = self.start;
                    self.start = self.pos+1;
                    self.pos = self.pos+1;
                    self.enddelim = semicolon;
                    self.is_macro = false;
                    return Some((start, self.pos));
                }

                self.pos += 1;
            }

        }
    }
}

fn is_a_use_stmt(src: &str, start: usize, pos: usize) -> bool {
    let src_bytes = src.as_bytes();
    let whitespace = " {\t\r\n".as_bytes();
    (pos > 3 && &src_bytes[start..start+3] == "use".as_bytes() &&
     whitespace.contains(&src_bytes[start+3])) ||
        (pos > 7 && src_bytes.slice(start, start+7) == "pub use".as_bytes() &&
                      whitespace.contains(&src_bytes[start+7]))
}

pub fn iter_stmts<'a>(src: &'a str) -> StmtIndicesIter<'a> {
    let semicolon: u8 = ";".as_bytes()[0];
    StmtIndicesIter{src: src, it: code_chunks(src),
                    pos: 0, start: 0, end: 0, bracelevel: 0,
                    parenlevel: 0, enddelim: semicolon, is_macro: false}
}

#[test]
fn iterates_single_use_stmts() {
    let src = &rejustify("
    use std::Foo; // a comment
    use std::Bar;
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("use std::Foo;", slice(src, it.next().unwrap()));
    assert_eq!("use std::Bar;", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_use_stmt_over_two_lines() {
    let src = &rejustify("
    use std::{Foo,
              Bar}; // a comment
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("use std::{Foo,
          Bar};", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_use_stmt_without_the_prefix() {
    let src = &rejustify("
    pub use {Foo,
              Bar}; // this is also legit apparently
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("pub use {Foo,
          Bar};", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_while_stmt() {
    let src = &rejustify("
    while self.pos < 3 { }
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("while self.pos < 3 { }", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_lambda_arg() {
    let src = &rejustify("
    myfn(|n|{});
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("myfn(|n|{});", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_macro() {
    let src = "
    mod foo;
    macro_rules! otry(
        ($e:expr) => (match $e { Some(e) => e, None => return })
    )
    mod bar;
    ";
    let mut it = iter_stmts(src);
    assert_eq!("mod foo;", slice(src, it.next().unwrap()));
    assert_eq!("macro_rules! otry(
        ($e:expr) => (match $e { Some(e) => e, None => return })
    )", slice(src, it.next().unwrap()));
    assert_eq!("mod bar;", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_macro_invocation() {
    let src = "
    mod foo;
    local_data_key!(local_stdout: Box<Writer + Send>)  // no ';'
    mod bar;
    ";
    let mut it = iter_stmts(src);
    assert_eq!("mod foo;", slice(src, it.next().unwrap()));
    assert_eq!("local_data_key!(local_stdout: Box<Writer + Send>)", slice(src, it.next().unwrap()));
    assert_eq!("mod bar;", slice(src, it.next().unwrap()));
}

// #[test]
// fn iterates_if_else_stmt() {
//     let src = rejustify("
//     if self.pos < 3 { } else { }
//     ");
//     let mut it = iter_stmts(src.as_slice());
//     assert_eq!("if self.pos < 3 { } else { }", slice(src.as_slice(), it.next().unwrap()));
// }

#[test]
fn iterates_inner_scope() {
    let src = "
    while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    }
    {
       b = foo;       // but not this
    }
    ";

    let scope = &src[25..];
    let mut it = iter_stmts(scope);

    assert_eq!("let a = 35;", slice(scope, it.next().unwrap()));
    assert_eq!("return a + 35;", slice(scope, it.next().unwrap()));
    assert_eq!(None, it.next());
}

#[test]
fn iterates_module_attribute() {
    let src = &rejustify("
    #![license = \"BSD\"]
    #[test]
    ")[];
    let mut it = iter_stmts(src);
    assert_eq!("#![license = \"BSD\"]", slice(src, it.next().unwrap()));
    assert_eq!("#[test]", slice(src, it.next().unwrap()));
}

#[test]
fn iterates_half_open_subscope_if_is_the_last_thing() {
    let src = "
    let something = 35;
    while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    ";

    let scope = src;
    let mut it = iter_stmts(scope);
    assert_eq!("let something = 35;", slice(scope, it.next().unwrap()));
    assert_eq!("while self.pos < 3 {
       let a = 35;
       return a + 35;  // should iterate this
    ", slice(scope, it.next().unwrap()));
}

// fn main() {
//     use std::io::BufferedReader;
//     use std::io::File;
//     use std::str;

//     let filetxt = BufferedReader::new(File::open(&Path::new("/usr/local/src/rust/src/libstd/io/stdio.rs"))).read_to_end().unwrap();
//     let src = str::from_utf8(filetxt.as_slice()).unwrap();

//     for (start,end) in iter_stmts(src) {
//         println!("BLOB |{}|",src.slice(start,end));
//     }

// }
<|endoftext|>


<commit_before>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

pub struct Talent;

impl Talent {
  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// The `epoch` must be given as `I64` (UNIX time in seconds) and is
  /// the range in which batches are searched.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(epoch: i64, presented_talents: Vec<i32>) -> Vec<Filter> {
    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_start_at")
                                             .with_lte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_end_at")
                                             .with_gte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.len() > 0 { // preferred over !_.is_empty()
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inside a `Map`,
  /// and the `epoch` (defined as UNIX time in seconds) for batches,
  /// return a `Query` for ElasticSearch.
  ///
  /// `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map, epoch: i64) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(epoch,
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: &mut Client, default_indexes: &[&str], params: &Map) -> Vec<u32> {
    let epoch = match params.find(&["epoch"]) {
      Some(&Value::I64(epoch)) => epoch,
      _ => DateTime::timestamp(&UTC::now())
    };

    let indexes: Vec<&str> = match params.find(&["index"]) {
      Some(&Value::String(ref index)) => vec![&index[..]],
      _ => default_indexes.to_vec()
    };

    let result = es.search_query()
                   .with_indexes(&indexes)
                   .with_query(&Talent::search_filters(params, epoch))
                   .with_sort(&Talent::sorting_criteria())
                   .send();

    match result {
      Ok(result) => {
        let mut results = result.hits.hits.into_iter()
                                          .map(|hit| hit.source.unwrap()["id"]
                                                               .as_u64()
                                                               .unwrap() as u32)
                                          .collect::<Vec<u32>>();
        results.dedup();
        results
      },
      Err(err) => {
        println!("{:?}", err);
        vec![]
      }
    }
  }

  /// Return a `Sort` that makes values be sorted for `updated_at`, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }
}
<commit_msg>resources/user: add tests for Talent::search()<commit_after>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

pub struct Talent;

impl Talent {
  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// The `epoch` must be given as `I64` (UNIX time in seconds) and is
  /// the range in which batches are searched.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(epoch: i64, presented_talents: Vec<i32>) -> Vec<Filter> {
    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_start_at")
                                             .with_lte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_end_at")
                                             .with_gte(JsonVal::from(epoch))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.len() > 0 { // preferred over !_.is_empty()
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inside a `Map`,
  /// and the `epoch` (defined as UNIX time in seconds) for batches,
  /// return a `Query` for ElasticSearch.
  ///
  /// `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map, epoch: i64) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(epoch,
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: &mut Client, default_indexes: &[&str], params: &Map) -> Vec<u32> {
    let epoch = match params.find(&["epoch"]) {
      Some(&Value::I64(epoch)) => epoch,
      _ => DateTime::timestamp(&UTC::now())
    };

    let indexes: Vec<&str> = match params.find(&["index"]) {
      Some(&Value::String(ref index)) => vec![&index[..]],
      _ => default_indexes.to_vec()
    };

    let result = es.search_query()
                   .with_indexes(&indexes)
                   .with_query(&Talent::search_filters(params, epoch))
                   .with_sort(&Talent::sorting_criteria())
                   .send();

    match result {
      Ok(result) => {
        let mut results = result.hits.hits.into_iter()
                                          .map(|hit| hit.source.unwrap()["id"]
                                                               .as_u64()
                                                               .unwrap() as u32)
                                          .collect::<Vec<u32>>();
        results.dedup();
        results
      },
      Err(err) => {
        println!("{:?}", err);
        vec![]
      }
    }
  }

  /// Return a `Sort` that makes values be sorted for `updated_at`, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }
}

#[cfg(test)]
mod tests {
  use std::env;

  use chrono::UTC;
  use chrono::datetime::DateTime;

  use rs_es::Client;
  use rs_es::query::*;
  use rs_es::operations::bulk::Action;
  use rs_es::units::Duration as ESDuration;

  use params::*;

  use resources::user::Talent;
  use config::*;
  use rustc_serialize::json::*;

  #[derive(RustcEncodable, Debug)]
  pub struct TestUser {
    pub id:              u32,
    pub accepted:        bool,
    pub batch_start_at:  i64,
    pub batch_end_at:    i64,
    pub updated_at:      i64,
    pub work_roles:      Array,
    pub company_ids:     Array
  }

  pub fn make_client() -> Client {
    let config = Config::load_config("examples/tests.toml".to_owned());
    Client::new(&*config.es.host, config.es.port)
  }

  pub fn populate_es(mut client: &mut Client) {
    let users = vec![
      TestUser {
        id:              1,
        accepted:        true,
        batch_start_at:  1141141876, // 2006
        batch_end_at:    4580812259, // 2099
        updated_at:      DateTime::timestamp(&UTC::now()),
        work_roles:      vec![],
        company_ids:     vec![]
      },

      TestUser {
        id:              2,
        accepted:        false,
        batch_start_at:  1141141876, // 2006
        batch_end_at:    4580812259, // 2099
        updated_at:      DateTime::timestamp(&UTC::now()) + 10,
        work_roles:      vec![],
        company_ids:     vec![]
      },

      TestUser {
        id:              3,
        accepted:        true,
        batch_start_at:  1141141876, // 2006
        batch_end_at:    4580812259, // 2099
        updated_at:      DateTime::timestamp(&UTC::now()) + 20,
        work_roles:      vec!["Fullstack".to_json(), "DevOps".to_json()],
        company_ids:     vec![6.to_json()]
      }];

    for user in users {
      client.index("sample_index", "test_user")
             .with_doc(&user)
             .send()
             .unwrap();
    }
  }

  pub fn clean_es(mut client: &mut Client) {
    let mut scan = match client.search_query()
                               .with_indexes(&["sample_index"])
                               .with_query(&Query::build_match_all().build())
                               .scan(ESDuration::minutes(1)) {
                                   Ok(scan) => scan,
                                   Err(_)   => return
                               };

    loop {
      let     page = scan.scroll(&mut client).unwrap();
      let mut hits = page.hits.hits;

      if hits.len() == 0 {
        break;
      }

      let actions: Vec<Action> = hits.drain(..)
                                .map(|hit| {
                                    Action::delete(hit.id)
                                        .with_index("sample_index")
                                        .with_doc_type(hit.doc_type)
                                })
                                .collect();
      client.bulk(&actions).send().unwrap();
    }

    scan.close(&mut client).unwrap();
  }

  #[test]
  fn test_search() {
    let mut client = make_client();
    clean_es(&mut client);
    populate_es(&mut client);

    // no parameters are given
    {
      let results = Talent::search(&mut client, &["sample_index"], &Map::new());
      assert_eq!(vec![3, 1], results);
    }

    // a non existing index is given
    {
      let mut map = Map::new();
      map.assign("index", Value::String("lololol".to_owned())).unwrap();

      let results = Talent::search(&mut client, &["sample_index"], &map);
      assert!(results.is_empty());
    }

    // a date that doesn't match given indexes is given
    {
      let mut map = Map::new();
      map.assign("epoch", Value::I64(1141141870)).unwrap();

      let results = Talent::search(&mut client, &["sample_index"], &map);
      assert!(results.is_empty());
    }

    // a date that doesn't match given indexes is given
    {
      let mut map = Map::new();
      map.assign("epoch", Value::I64(1141141870)).unwrap();

      let results = Talent::search(&mut client, &["sample_index"], &map);
      assert!(results.is_empty());
    }

    // TODO
    // filtering for valid work roles
    /*{
      let mut map = Map::new();
      map.assign("work_roles[]", Value::String("Fullstack".to_owned())).unwrap();

      let results = Talent::search(&mut client, &["sample_index"], &map);
      assert_eq!(vec![3], results);
    }*/

    // filtering for valid work roles
    {
      let mut map = Map::new();
      map.assign("company_id", Value::String("6".into())).unwrap();

      let results = Talent::search(&mut client, &["sample_index"], &map);
      assert_eq!(vec![1], results);
    }
  }
}
<|endoftext|>


<commit_before>use game::simpleboard::SimpleBoard;
use game::board::{State, GoBoard, GoMove};
use search::evaluation::Evaluation;

pub fn minimax(board: &mut SimpleBoard, depth: i8, maximizing_player: bool) -> (f64, Option<GoMove>) {
    if depth == 0 {
        (board.evaluate(), None)
    }
    else {
        if maximizing_player {
            let player = State::Black;
            let mut best_value: f64 = -10000.0;
            // Loop over all possible moves
            let moves = board.move_list(player);
            let mut good_move = GoMove::Pass;
            for mv in moves {
                let mut new_board = board.clone();
                new_board.play_move(&mv);
                let (v, _) = minimax(&mut new_board, depth - 1, false);
                if v > best_value {
                    best_value = v;
                    good_move = mv.clone();
                }
                best_value = v.max(best_value);
            }
            (best_value, Some(good_move))
        }
        else {
            let player = State::White;
            let mut best_value: f64 = 10000.0;
            let moves = board.move_list(player);
            let mut good_move = GoMove::Pass;
            for mv in moves {
                let mut new_board = board.clone();
                new_board.play_move(&mv);
                let (v, _) = minimax(&mut new_board, depth - 1, true);
                if v < best_value {
                    best_value = v;
                    good_move = mv.clone();
                }
                best_value = v.min(best_value);
            }
            (best_value, Some(good_move))
        }
    }
}

#[cfg(test)]
mod tests {
    use game::simpleboard::SimpleBoard;
    use game::board::*;
    use super::*;

    #[test]
    fn three_space_life() {
        let mut game_board = SimpleBoard::new(6, 4);

        let black_x = vec![0, 1, 2, 3, 3];
        let black_y = vec![1, 1, 1, 1, 0];

        for (x, y) in black_x.iter().zip(black_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::Black};
            game_board.play_move(&mv);
        }

        let white_x = vec![0, 1, 2, 3, 4, 4, 4];
        let white_y = vec![2, 2, 2, 2, 2, 1, 0];

        for (x, y) in white_x.iter().zip(white_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::White};
            game_board.play_move(&mv);
        }

        let (_, mv) = minimax(&mut game_board, 8, true);

        assert_eq!(mv.unwrap(), GoMove::Move{x: 1, y: 0, player: State::Black});

    }
}
<commit_msg>Added another tsumego test<commit_after>use game::simpleboard::SimpleBoard;
use game::board::{State, GoBoard, GoMove};
use search::evaluation::Evaluation;

pub fn minimax(board: &mut SimpleBoard, depth: i8, maximizing_player: bool) -> (f64, Option<GoMove>) {
    if depth == 0 {
        (board.evaluate(), None)
    }
    else {
        if maximizing_player {
            let player = State::Black;
            let mut best_value: f64 = -10000.0;
            // Loop over all possible moves
            let moves = board.move_list(player);
            let mut good_move = GoMove::Pass;
            for mv in moves {
                let mut new_board = board.clone();
                new_board.play_move(&mv);
                let (v, _) = minimax(&mut new_board, depth - 1, false);
                if v > best_value {
                    best_value = v;
                    good_move = mv.clone();
                }
                best_value = v.max(best_value);
            }
            (best_value, Some(good_move))
        }
        else {
            let player = State::White;
            let mut best_value: f64 = 10000.0;
            let moves = board.move_list(player);
            let mut good_move = GoMove::Pass;
            for mv in moves {
                let mut new_board = board.clone();
                new_board.play_move(&mv);
                let (v, _) = minimax(&mut new_board, depth - 1, true);
                if v < best_value {
                    best_value = v;
                    good_move = mv.clone();
                }
                best_value = v.min(best_value);
            }
            (best_value, Some(good_move))
        }
    }
}

#[cfg(test)]
mod tests {
    use game::simpleboard::SimpleBoard;
    use game::board::*;
    use super::*;

    #[test]
    fn three_space_life() {
        let mut game_board = SimpleBoard::new(6, 4);

        let black_x = vec![0, 1, 2, 3, 3];
        let black_y = vec![1, 1, 1, 1, 0];

        for (x, y) in black_x.iter().zip(black_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::Black};
            game_board.play_move(&mv);
        }

        let white_x = vec![0, 1, 2, 3, 4, 4, 4];
        let white_y = vec![2, 2, 2, 2, 2, 1, 0];

        for (x, y) in white_x.iter().zip(white_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::White};
            game_board.play_move(&mv);
        }

        let (_, mv) = minimax(&mut game_board, 8, true);

        assert_eq!(mv.unwrap(), GoMove::Move{x: 1, y: 0, player: State::Black});

    }

    #[test]
    fn corner_kill() {
        let mut game_board = SimpleBoard::new(7, 5);

        let black_x = vec![0, 1, 2, 3, 4, 5, 5, 5];
        let black_y = vec![3, 3, 3, 2, 2, 2, 1, 0];

        for (x, y) in black_x.iter().zip(black_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::Black};
            game_board.play_move(&mv);
        }

        let white_x = vec![0, 1, 2, 3, 4, 1];
        let white_y = vec![2, 2, 2, 1, 1, 0];

        for (x, y) in white_x.iter().zip(white_y.iter()) {
            let mv = GoMove::Move{x: *x, y: *y, player: State::White};
            game_board.play_move(&mv);
        }

        let (_, mv) = minimax(&mut game_board, 7, true);

        assert_eq!(mv.unwrap(), GoMove::Move{x: 0, y: 0, player: State::Black});

    }

}
<|endoftext|>


<commit_before>use std::mem;

const SORT_THRESH: usize = 6;

/// Use the Quickselect algorithm to select the `k`th smallest
/// element from `data`.
///
/// Does not modify `data`. Allocates an additional vector for scratch space; see `qselect_inplace`
/// for an in-place version.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect<T: Ord>(data: &[T], k: usize) -> &T {
    let len = data.len();
    assert!(k < len, "Called qselect with k = {} and data length: {}", k, len);

    let mut refs: Vec<_> = data.iter().collect();
    *qselect_inplace(&mut refs, k)
}

/// Use the Quickselect algorithm to select the `k`th smallest element from `data`. As part of the
/// algorithm, `k` is moved to its final sorted position and the rest of the array is (at least) partially
/// sorted.
///
/// For a version that uses extra space but does not reorder the slice, use `qselect`.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect_inplace<T: Ord>(data: &mut [T], k: usize) -> &mut T {
    let len = data.len();

    assert!(k < len, "Called qselect_inplace with k = {} and data length: {}", k, len);

    if len < SORT_THRESH {
        data.sort();
        return &mut data[k];
    }

    let pivot_idx = partition(data);

    if k == pivot_idx {
        &mut data[pivot_idx]
    } else if k < pivot_idx {
        qselect_inplace(&mut data[..pivot_idx], k)
    } else {
        qselect_inplace(&mut data[pivot_idx + 1..], k - pivot_idx - 1)
    }
}

fn partition<T: Ord>(data: &mut [T]) -> usize {
    let len = data.len();

    let pivot_idx = {
        let first = (&data[0], 0);
        let mid = (&data[len / 2], len / 2);
        let last = (&data[len - 1], len - 1);

        median_of_3(&first, &mid, &last).1
    };

    data.swap(pivot_idx, len - 1);

    let mut curr = 0;

    for i in 0 .. len - 1 {
        if data[i] < data[len - 1] {
            data.swap(i, curr);
            curr += 1;
        }
    }

    data.swap(curr, len - 1);

    curr
}

/// Of the three values passed, return the median.
pub fn median_of_3<T: Ord>(mut x: T, mut y: T, mut z: T) -> T {
    in_order(&mut x, &mut y);
    in_order(&mut x, &mut z);
    in_order(&mut y, &mut z);

    y
}

/// If `x > y`, swap `x` and `y`.
#[inline]
pub fn in_order<T: Ord>(x: &mut T, y: &mut T) {
    if x > y {
        mem::swap(x, y);
    }
}

#[test]
fn test_qsel() {
    macro_rules! test_qsel (
        ($($elem:expr),+; $k:expr; $expect:expr) => (
            let mut data = [$($elem),+];
            let selected = qselect_inplace(&mut data, $k);
            assert_eq!(*selected, $expect);
        )
    );

    test_qsel!(1, 2, 3, 4, 5; 3; 4);
    test_qsel!(5, 4, 3, 2, 1; 1; 2);
    test_qsel!(2, 2, 3, 3, 4; 1; 2);
}

#[test]
fn test_median() {
    macro_rules! test_median {
        ($x:expr, $y:expr, $z:expr; $expected:expr) => (
            let x = $x;
            let y = $y;
            let z = $z;
            let result = median_of_3(&x, &y, &z);
            assert_eq!(*result, $expected);
        )
    }

    test_median!(1, 2, 3; 2);
    test_median!(1, 3, 2; 2);
    test_median!(2, 1, 3; 2);
    test_median!(2, 3, 1; 2);
    test_median!(3, 1, 2; 2);
    test_median!(3, 2, 1; 2);
}
<commit_msg>Add variants of qselect functions which take ordering functions<commit_after>use std::cmp::Ordering;
use std::mem;

// The threshold below which qselect_inplace_by() should just sort the slice.
const SORT_THRESH: usize = 6;

/// Use the Quickselect algorithm to select the `k`th smallest
/// element from `data`.
///
/// Does not modify `data`. Allocates an additional vector for scratch space; see `qselect_inplace`
/// for an in-place version.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect<T: Ord>(data: &[T], k: usize) -> &T {
    qselect_by(data, k, <T as Ord>::cmp)
}

/// Use the Quickselect algorithm, given an ordering function, to select the `k`th
/// smallest element from `data`.
///
/// Does not modify `data`. Allocates an additional vector for scratch space; see `qselect_inplace_by`
/// for an in-place version.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect_by<T, F: FnMut(&T, &T) -> Ordering>(data: &[T], k: usize, mut ord_fn: F) -> &T {
    let len = data.len();
    assert!(k < len, "Called qselect with k = {} and data length: {}", k, len);

    let mut refs: Vec<_> = data.iter().collect();
    *qselect_inplace_by(&mut refs, k, |l, r| ord_fn(l, r))
}

/// Use the Quickselect algorithm to select the `k`th smallest element from `data`. As part of the
/// algorithm, `k` is moved to its final sorted position and the rest of the array is (at least) partially
/// sorted.
///
/// For a version that uses extra space but does not reorder the slice, use `qselect`.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect_inplace<T: Ord>(data: &mut [T], k: usize) -> &mut T {
    qselect_inplace_by(data, k, <T as Ord>::cmp)
}

/// Use the Quickselect algorithm, given an ordering function,
/// to select the `k`th smallest element from `data`. As part of the
/// algorithm, `k` is moved to its final sorted position and the rest of the array is (at least) partially
/// sorted.
///
/// For a version that uses extra space but does not reorder the slice, use `qselect_by`.
///
/// ##Panics
/// If `k` is greater than `data.len()`.
pub fn qselect_inplace_by<T, F: FnMut(&T, &T) -> Ordering>(data: &mut [T], k: usize, mut ord_fn: F) -> &mut T {
    let len = data.len();

    assert!(k < len, "Called qselect_inplace with k = {} and data length: {}", k, len);

    if len < SORT_THRESH {
        data.sort_by(&mut ord_fn);
        return &mut data[k];
    }

    let pivot_idx = partition_by(data, &mut ord_fn);

    if k == pivot_idx {
        &mut data[pivot_idx]
    } else if k < pivot_idx {
        qselect_inplace_by(&mut data[..pivot_idx], k, ord_fn)
    } else {
        qselect_inplace_by(&mut data[pivot_idx + 1..], k - pivot_idx - 1, ord_fn)
    }
}

/// Given an ordering function, pick an arbitrary pivot and partition the slice, returning the pivot.
fn partition_by<T, F: FnMut(&T, &T) -> Ordering>(data: &mut [T], mut ord_fn: F) -> usize {
    let len = data.len();

    let pivot_idx = {
        let first = (&data[0], 0);
        let mid = (&data[len / 2], len / 2);
        let last = (&data[len - 1], len - 1);

        median_of_3_by(&first, &mid, &last, |left, right| ord_fn(left.0, right.0)).1
    };

    data.swap(pivot_idx, len - 1);

    let mut curr = 0;

    for i in 0 .. len - 1 {
        if ord_fn(&data[i], &data[len - 1]) == Ordering::Less {
            data.swap(i, curr);
            curr += 1;
        }
    }

    data.swap(curr, len - 1);

    curr
}

/// Of the three values passed, return the median.
pub fn median_of_3<T: Ord>(x: T, y: T, z: T) -> T {
    median_of_3_by(x, y, z, <T as Ord>::cmp)
}

/// Given an ordering function, of the three values passed, return the median.
pub fn median_of_3_by<T, F: FnMut(&T, &T) -> Ordering>(mut x: T, mut y: T, mut z: T, mut ord_fn: F) -> T {
    in_order_by(&mut x, &mut y, &mut ord_fn);
    in_order_by(&mut x, &mut z, &mut ord_fn);
    in_order_by(&mut y, &mut z, &mut ord_fn);

    y
}

/// If `x > y`, swap `x` and `y`.
#[inline]
pub fn in_order<T: Ord>(x: &mut T, y: &mut T) {
    if x > y {
        mem::swap(x, y);
    }
}

/// Given an ordering function, if `x > y`, swap `x` and `y`.
#[inline]
pub fn in_order_by<T, F: FnMut(&T, &T) -> Ordering>(x: &mut T, y: &mut T, mut ord_fn: F) {
    if ord_fn(x, y) == Ordering::Greater {
        mem::swap(x, y);
    }
}

#[test]
fn test_qsel() {
    macro_rules! test_qsel (
        ($($elem:expr),+; $k:expr; $expect:expr) => (
            let mut data = [$($elem),+];
            let selected = qselect_inplace(&mut data, $k);
            assert_eq!(*selected, $expect);
        )
    );

    test_qsel!(1, 2, 3, 4, 5; 3; 4);
    test_qsel!(5, 4, 3, 2, 1; 1; 2);
    test_qsel!(2, 2, 3, 3, 4; 1; 2);
}

#[test]
fn test_median() {
    macro_rules! test_median {
        ($x:expr, $y:expr, $z:expr; $expected:expr) => (
            let x = $x;
            let y = $y;
            let z = $z;
            let result = median_of_3(&x, &y, &z);
            assert_eq!(*result, $expected);
        )
    }

    test_median!(1, 2, 3; 2);
    test_median!(1, 3, 2; 2);
    test_median!(2, 1, 3; 2);
    test_median!(2, 3, 1; 2);
    test_median!(3, 1, 2; 2);
    test_median!(3, 2, 1; 2);
}
<|endoftext|>


<commit_before>use ruma_identifiers::RoomVersionId;

use crate::{Error, Result};

pub enum RoomDisposition {
    /// A room version that has a stable specification.
    Stable,
    /// A room version that is not yet fully specified.
    #[allow(dead_code)]
    Unstable,
}

pub enum EventFormatVersion {
    /// $id:server event id format
    V1,
    /// MSC1659-style $hash event id format: introduced for room v3
    V2,
    /// MSC1884-style $hash format: introduced for room v4
    V3,
}

pub enum StateResolutionVersion {
    /// State resolution for rooms at version 1.
    V1,
    /// State resolution for room at version 2 or later.
    V2,
}

#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomVersion {
    /// The version this room is set to.
    pub version: RoomVersionId,
    /// The stability of this room.
    pub disposition: RoomDisposition,
    /// The format of the EventId.
    pub event_format: EventFormatVersion,
    /// Which state resolution algorithm is used.
    pub state_res: StateResolutionVersion,
    // FIXME: not sure what this one means?
    pub enforce_key_validity: bool,

    /// `m.room.aliases` had special auth rules and redaction rules
    /// before room version 6.
    ///
    /// before MSC2261/MSC2432,
    pub special_case_aliases_auth: bool,
    /// Strictly enforce canonical json, do not allow:
    /// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
    /// * Floats
    /// * NaN, Infinity, -Infinity
    pub strict_canonicaljson: bool,
    /// Verify notifications key while checking m.room.power_levels.
    ///
    /// bool: MSC2209: Check 'notifications'
    pub limit_notifications_power_levels: bool,
    /// Extra rules when verifying redaction events.
    pub extra_redaction_checks: bool,
}

impl RoomVersion {
    pub fn new(version: &RoomVersionId) -> Result<Self> {
        Ok(match version {
            RoomVersionId::Version1 => Self::version_1(),
            RoomVersionId::Version2 => Self::version_2(),
            RoomVersionId::Version3 => Self::version_3(),
            RoomVersionId::Version4 => Self::version_4(),
            RoomVersionId::Version5 => Self::version_5(),
            RoomVersionId::Version6 => Self::version_6(),
            ver => return Err(Error::Unsupported(format!("found version `{}`", ver.as_str()))),
        })
    }

    pub fn version_1() -> Self {
        Self {
            version: RoomVersionId::Version1,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V1,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_2() -> Self {
        Self {
            version: RoomVersionId::Version2,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_3() -> Self {
        Self {
            version: RoomVersionId::Version3,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V2,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_4() -> Self {
        Self {
            version: RoomVersionId::Version4,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_5() -> Self {
        Self {
            version: RoomVersionId::Version5,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_6() -> Self {
        Self {
            version: RoomVersionId::Version6,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
        }
    }
}
<commit_msg>state-res: Make some room_version enums non_exhaustive<commit_after>use ruma_identifiers::RoomVersionId;

use crate::{Error, Result};

#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum RoomDisposition {
    /// A room version that has a stable specification.
    Stable,
    /// A room version that is not yet fully specified.
    #[allow(dead_code)]
    Unstable,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum EventFormatVersion {
    /// $id:server event id format
    V1,
    /// MSC1659-style $hash event id format: introduced for room v3
    V2,
    /// MSC1884-style $hash format: introduced for room v4
    V3,
}

#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum StateResolutionVersion {
    /// State resolution for rooms at version 1.
    V1,
    /// State resolution for room at version 2 or later.
    V2,
}

#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomVersion {
    /// The version this room is set to.
    pub version: RoomVersionId,
    /// The stability of this room.
    pub disposition: RoomDisposition,
    /// The format of the EventId.
    pub event_format: EventFormatVersion,
    /// Which state resolution algorithm is used.
    pub state_res: StateResolutionVersion,
    // FIXME: not sure what this one means?
    pub enforce_key_validity: bool,

    /// `m.room.aliases` had special auth rules and redaction rules
    /// before room version 6.
    ///
    /// before MSC2261/MSC2432,
    pub special_case_aliases_auth: bool,
    /// Strictly enforce canonical json, do not allow:
    /// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
    /// * Floats
    /// * NaN, Infinity, -Infinity
    pub strict_canonicaljson: bool,
    /// Verify notifications key while checking m.room.power_levels.
    ///
    /// bool: MSC2209: Check 'notifications'
    pub limit_notifications_power_levels: bool,
    /// Extra rules when verifying redaction events.
    pub extra_redaction_checks: bool,
}

impl RoomVersion {
    pub fn new(version: &RoomVersionId) -> Result<Self> {
        Ok(match version {
            RoomVersionId::Version1 => Self::version_1(),
            RoomVersionId::Version2 => Self::version_2(),
            RoomVersionId::Version3 => Self::version_3(),
            RoomVersionId::Version4 => Self::version_4(),
            RoomVersionId::Version5 => Self::version_5(),
            RoomVersionId::Version6 => Self::version_6(),
            ver => return Err(Error::Unsupported(format!("found version `{}`", ver.as_str()))),
        })
    }

    pub fn version_1() -> Self {
        Self {
            version: RoomVersionId::Version1,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V1,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_2() -> Self {
        Self {
            version: RoomVersionId::Version2,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V1,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: false,
        }
    }

    pub fn version_3() -> Self {
        Self {
            version: RoomVersionId::Version3,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V2,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_4() -> Self {
        Self {
            version: RoomVersionId::Version4,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: false,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_5() -> Self {
        Self {
            version: RoomVersionId::Version5,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: true,
            strict_canonicaljson: false,
            limit_notifications_power_levels: false,
            extra_redaction_checks: true,
        }
    }

    pub fn version_6() -> Self {
        Self {
            version: RoomVersionId::Version6,
            disposition: RoomDisposition::Stable,
            event_format: EventFormatVersion::V3,
            state_res: StateResolutionVersion::V2,
            enforce_key_validity: true,
            special_case_aliases_auth: false,
            strict_canonicaljson: true,
            limit_notifications_power_levels: true,
            extra_redaction_checks: true,
        }
    }
}
<|endoftext|>


<commit_before>#[feature(libc)]
extern crate libc;

// use super::*;
use session::callbacks;
use libc::types::common::c95::c_void;

pub struct sp_session_config;
pub struct sp_session;

#[repr(C)]
pub struct session_config {
    api_version: libc::c_int,
    cache_location: *mut libc::c_char,
    settings_location: *mut libc::c_char,
    application_key: *mut c_void,
    application_key_size: usize,
    user_agent: *mut libc::c_char,
    callbacks: *mut libc::c_char,
    userdata: *mut c_void,
    compress_playlists: bool,
    dont_save_metadata_for_playlists: bool,
    initially_unload_playlists: bool,
    device_id: *mut libc::c_char,
    proxy: *mut libc::c_char,
    proxy_username: *mut libc::c_char,
    proxy_password: *mut libc::c_char,
    ca_certs_filename: *mut libc::c_char,
    tracefile: *mut libc::c_char,
}

#[link(name = "spotify")]
extern {
    fn sp_session_create(config: session_config , session: *mut sp_session)
                            -> ::error::Error;
}

//pub fn session_create<'a>(config: session::Config, session: *mut session)
//                            -> Result<::error::Error, &'a str> {
//    unsafe {
//        //Ok(sp_session_create(config, session))
//    }
//    Err(())
//}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_session_create() {
        /*
        let spconfig: sp_session_config = {
            api_version = 12,
            cache_location = "tmp",
            settings_location = "tmp",
            application_key = "",
            application_key_size = 0, // Set in main()
            user_agent = "spotify-jukebox-example",
            callbacks = 0,
            0,
        };
        */
    }

}
<commit_msg>A proper wrapper for session_create (incomplete)<commit_after>#[feature(libc)]
extern crate libc;

// use super::*;
use session::callbacks;
use libc::types::common::c95::c_void;
use std::ffi::CString;
use std::ptr;

pub struct sp_session_config;
pub struct sp_session;

#[repr(C)]
pub struct session_config {
    api_version: libc::c_int,
    cache_location: *const libc::c_char,
    settings_location: *const libc::c_char,
    application_key: *const c_void,
    application_key_size: libc::c_int,
    user_agent: *const libc::c_char,
    callbacks: *const libc::c_char,
    userdata: *mut c_void,
    compress_playlists: bool,
    dont_save_metadata_for_playlists: bool,
    initially_unload_playlists: bool,
    device_id: *const libc::c_char,
    proxy: *const libc::c_char,
    proxy_username: *const libc::c_char,
    proxy_password: *const libc::c_char,
    ca_certs_filename: *const libc::c_char,
    tracefile: *const libc::c_char,
}

#[link(name = "spotify")]
extern {
    fn sp_session_create(config: session_config,
                         session: *mut sp_session)
                         -> ::error::Error;
}

pub struct Config<'a> {
    api_version: isize,
    cache_location: &'a[u8],
    settings_location: &'a[u8],
    application_key: &'a str,
    user_agent: &'a str,
    callbacks: &'a sp_session,
    userdata: &'a str,
    compress_playlists: bool,
    dont_save_metadata_for_playlists: bool,
    initially_unload_playlists: bool,
    device_id: &'a str,
    proxy: &'a str,
    proxy_username: &'a str,
    proxy_password: &'a str,
    ca_certs_filename: &'a str,
    tracefile: &'a str,
}

pub fn session_create<'a>(config: Config<'a>,
                          session: *mut sp_session)
                          -> Result<::error::Error, &'a str> {
    let c_config_ref = session_config {
        api_version:        config.api_version as libc::c_int,
        cache_location:     CString::new(config.cache_location).unwrap().as_ptr(),
        settings_location:  CString::new(config.settings_location).unwrap().as_ptr(),
        application_key:    CString::new(config.application_key).unwrap().as_ptr() as *mut c_void,
        application_key_size: config.application_key.len() as libc::c_int,
        user_agent:         CString::new(config.user_agent).unwrap().as_ptr(),
        callbacks:          0isize as *const i8,
        userdata:           0isize as *mut c_void,
        compress_playlists: config.compress_playlists,
        dont_save_metadata_for_playlists: config.dont_save_metadata_for_playlists,
        initially_unload_playlists: config.initially_unload_playlists,
        device_id:          CString::new(config.device_id).unwrap().as_ptr(),
        proxy:              CString::new(config.proxy).unwrap().as_ptr(),
        proxy_username:     CString::new(config.proxy_username).unwrap().as_ptr(),
        proxy_password:     CString::new(config.proxy_password).unwrap().as_ptr(),
        ca_certs_filename:  CString::new(config.ca_certs_filename).unwrap().as_ptr(),
        tracefile:          CString::new(config.tracefile).unwrap().as_ptr(),
    };

    let c_error_ref = unsafe {
        sp_session_create(c_config_ref, session)
    };

    match c_error_ref {
        _ => Ok(c_error_ref),
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_session_create() {
        /*
        let spconfig: sp_session_config = {
            api_version = 12,
            cache_location = "tmp",
            settings_location = "tmp",
            application_key = "",
            application_key_size = 0, // Set in main()
            user_agent = "spotify-jukebox-example",
            callbacks = 0,
            0,
        };
        */
    }

}
<|endoftext|>


<commit_before>use jni::JNIEnv;
use jni::objects::JClass;
use jni::sys::{jint, jlong};
use libc::getloadavg;

#[no_mangle]
pub extern "system" fn Java_com_komanov_nativeaccess_JniHelper_getloadavg(_env: JNIEnv,
                                                                          _class: JClass,
                                                                          input: *mut f64,
                                                                          num: jint)
                                                                          -> jint {
    unsafe {
        return getloadavg(input, num);
    }
}
<commit_msg>Remove unused usage<commit_after>use jni::JNIEnv;
use jni::objects::JClass;
use jni::sys::jint;
use libc::getloadavg;

#[no_mangle]
pub extern "system" fn Java_com_komanov_nativeaccess_JniHelper_getloadavg(_env: JNIEnv,
                                                                          _class: JClass,
                                                                          input: *mut f64,
                                                                          num: jint)
                                                                          -> jint {
    unsafe {
        return getloadavg(input, num);
    }
}
<|endoftext|>


<commit_before>use std::error::{self, Error};
use std::fmt;

use cairo;
use cssparser::BasicParseError;
use glib;
use glib::error::ErrorDomain;
use glib::translate::*;
use glib_sys;
use libc;

use attributes::Attribute;
use parsers::ParseError;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// The value could not be parsed
    Parse(ParseError),

    // The value could be parsed, but is invalid
    Value(String),
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    attr: Attribute,
    err: ValueErrorKind,
}

impl NodeError {
    pub fn parse_error(attr: Attribute, error: ParseError) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error),
        }
    }

    pub fn value_error(attr: Attribute, description: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Value(description.to_string()),
        }
    }

    pub fn attribute_error(attr: Attribute, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ValueErrorKind::Parse(ref n) => write!(
                f,
                "error parsing value for attribute \"{}\": {}",
                self.attr.to_string(),
                n.display
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value for attribute \"{}\": {}",
                self.attr.to_string(),
                s
            ),
        }
    }
}

impl From<ParseError> for ValueErrorKind {
    fn from(pe: ParseError) -> ValueErrorKind {
        ValueErrorKind::Parse(pe)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        ValueErrorKind::from(ParseError::from(e))
    }
}

#[derive(Clone)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    // Could not parse data: URL
    BadDataUrl,
    Cairo(cairo::Status),
    EmptyData,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::BadDataUrl
            | LoadingError::EmptyData
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

extern "C" {
    fn rsvg_error_quark() -> glib_sys::GQuark;
}

pub fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) {
    unsafe {
        // this is RSVG_ERROR_FAILED, the only error code available in RsvgError
        assert!(code == 0);

        glib_sys::g_set_error_literal(
            err,
            rsvg_error_quark(),
            code as libc::c_int,
            msg.to_glib_none().0,
        );
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}

/// Used as a generic error to translate to glib::Error
///
/// This type implements `glib::error::ErrorDomain`, so it can be used
/// to obtain the error code while calling `glib::Error::new()`.  Unfortunately
/// the public librsvg API does not have detailed error codes yet, so we use
/// this single value as the only possible error code to return.
#[derive(Copy, Clone)]
pub struct RsvgError;

// Keep in sync with rsvg.h:RsvgError
const RSVG_ERROR_FAILED: i32 = 0;

impl ErrorDomain for RsvgError {
    fn domain() -> glib::Quark {
        from_glib(unsafe { rsvg_error_quark() })
    }

    fn code(self) -> i32 {
        RSVG_ERROR_FAILED
    }

    fn from(code: i32) -> Option<Self> {
        match code {
            // We don't have enough information from glib error codes
            _ => Some(RsvgError),
        }
    }
}
<commit_msg>AttributeResultExt: helper trait to convert Result<O, E> to Result<O, NodeError><commit_after>use std::error::{self, Error};
use std::fmt;

use cairo;
use cssparser::BasicParseError;
use glib;
use glib::error::ErrorDomain;
use glib::translate::*;
use glib_sys;
use libc;

use attributes::Attribute;
use parsers::ParseError;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// The value could not be parsed
    Parse(ParseError),

    // The value could be parsed, but is invalid
    Value(String),
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    attr: Attribute,
    err: ValueErrorKind,
}

impl NodeError {
    pub fn parse_error(attr: Attribute, error: ParseError) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error),
        }
    }

    pub fn value_error(attr: Attribute, description: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Value(description.to_string()),
        }
    }

    pub fn attribute_error(attr: Attribute, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.err {
            ValueErrorKind::Parse(ref n) => write!(
                f,
                "error parsing value for attribute \"{}\": {}",
                self.attr.to_string(),
                n.display
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value for attribute \"{}\": {}",
                self.attr.to_string(),
                s
            ),
        }
    }
}

impl From<ParseError> for ValueErrorKind {
    fn from(pe: ParseError) -> ValueErrorKind {
        ValueErrorKind::Parse(pe)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        ValueErrorKind::from(ParseError::from(e))
    }
}

#[derive(Clone)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
///
/// A `NodeError` requires an `Attribute` to which the error refers,
/// plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their
/// own kind of error, instead of `ValueErrorKind`.  If that particular error
/// type has an `impl From<FooError> for ValueErrorKind`, then this
/// trait helps assign attribute values in `set_atts()` methods as follows:
///
/// ```ignore
/// use error::AttributeResultExt;
///
/// // fn parse_foo(...) -> Result<Foo, FooError>
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
///
/// self.foo = parse_foo(value).attribute(Attribute::Foo)?;
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `NodeError` with the provided `attr`.
pub trait AttributeResultExt<O, E> {
    fn attribute(self, attr: Attribute) -> Result<O, NodeError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O, E> for Result<O, E> {
    fn attribute(self, attr: Attribute) -> Result<O, NodeError> {
        self.map_err(|e| e.into())
            .map_err(|e| NodeError::attribute_error(attr, e))
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    // Could not parse data: URL
    BadDataUrl,
    Cairo(cairo::Status),
    EmptyData,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::BadDataUrl
            | LoadingError::EmptyData
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

extern "C" {
    fn rsvg_error_quark() -> glib_sys::GQuark;
}

pub fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) {
    unsafe {
        // this is RSVG_ERROR_FAILED, the only error code available in RsvgError
        assert!(code == 0);

        glib_sys::g_set_error_literal(
            err,
            rsvg_error_quark(),
            code as libc::c_int,
            msg.to_glib_none().0,
        );
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}

/// Used as a generic error to translate to glib::Error
///
/// This type implements `glib::error::ErrorDomain`, so it can be used
/// to obtain the error code while calling `glib::Error::new()`.  Unfortunately
/// the public librsvg API does not have detailed error codes yet, so we use
/// this single value as the only possible error code to return.
#[derive(Copy, Clone)]
pub struct RsvgError;

// Keep in sync with rsvg.h:RsvgError
const RSVG_ERROR_FAILED: i32 = 0;

impl ErrorDomain for RsvgError {
    fn domain() -> glib::Quark {
        from_glib(unsafe { rsvg_error_quark() })
    }

    fn code(self) -> i32 {
        RSVG_ERROR_FAILED
    }

    fn from(code: i32) -> Option<Self> {
        match code {
            // We don't have enough information from glib error codes
            _ => Some(RsvgError),
        }
    }
}
<|endoftext|>


<commit_before>use futures::executor::block_on_stream;
use futures::stream::{self, StreamExt};

#[test]
fn panic_in_the_middle_of_the_stream() {
    let stream = stream::iter(vec![Some(10), None, Some(11)]);

    // panic on second element
    let stream_panicking = stream.map(|o| o.unwrap());
    let mut iter = block_on_stream(stream_panicking.catch_unwind());

    assert_eq!(10, iter.next().unwrap().ok().unwrap());
    assert!(iter.next().unwrap().is_err());
    assert!(iter.next().is_none());
}

#[test]
fn no_panic() {
    let stream = stream::iter(vec![10, 11, 12]);

    let mut iter = block_on_stream(stream.catch_unwind());

    assert_eq!(10, iter.next().unwrap().ok().unwrap());
    assert_eq!(11, iter.next().unwrap().ok().unwrap());
    assert_eq!(12, iter.next().unwrap().ok().unwrap());
    assert!(iter.next().is_none());
}
<commit_msg>futures: tests/stream_catch_unwind.rs: Don't break w/o "executor"<commit_after>#[cfg(feature = "executor")]
#[test]
fn panic_in_the_middle_of_the_stream() {
    use futures::executor::block_on_stream;
    use futures::stream::{self, StreamExt};

    let stream = stream::iter(vec![Some(10), None, Some(11)]);

    // panic on second element
    let stream_panicking = stream.map(|o| o.unwrap());
    let mut iter = block_on_stream(stream_panicking.catch_unwind());

    assert_eq!(10, iter.next().unwrap().ok().unwrap());
    assert!(iter.next().unwrap().is_err());
    assert!(iter.next().is_none());
}

#[cfg(feature = "executor")]
#[test]
fn no_panic() {
    use futures::executor::block_on_stream;
    use futures::stream::{self, StreamExt};

    let stream = stream::iter(vec![10, 11, 12]);

    let mut iter = block_on_stream(stream.catch_unwind());

    assert_eq!(10, iter.next().unwrap().ok().unwrap());
    assert_eq!(11, iter.next().unwrap().ok().unwrap());
    assert_eq!(12, iter.next().unwrap().ok().unwrap());
    assert!(iter.next().is_none());
}
<|endoftext|>