<commit_before>#![crate_id = "stem#0.1.0"]
#![crate_type = "lib"]

use std::ascii;
use std::ascii::Ascii;
use std::vec::Vec;

/// Member b is a vector of std::ascii::Ascii holding a word to be stemmed.
/// The letters are in b[0], b[1] ... ending at b[z->k]. Member k is readjusted
/// downwards as the stemming progresses. Zero termination is not in fact used
/// in the algorithm.
///
/// Note that only lower case sequences are stemmed. Forcing to lower case
/// should be done before get(...) is called.
///
///
/// Typical usage is:
///
///     let b = ~"pencils";
///     let res = stem::get(b);
///         /- stem the 7 characters of b[0] to b[6]. The result, res,
///            will be "pencil" (the 's' is removed). -/
///
pub struct Stemmer {
    b: Vec<ascii::Ascii>,
    k: uint,
    j: uint,
}

impl Stemmer {
    pub fn new(word: &str) -> Result<Stemmer, ~str> {
        if !word.is_ascii() {
            Err("Only support English words with ASCII characters".to_owned())
        } else {
            let b = unsafe { word.to_ascii_nocheck().to_lower() };
            let k = b.len();
            Ok(Stemmer {
                b: b,
                k: k,
                j: 0,
            })
        }
    }

    /// stem.is_consonant(i) is true <=> stem[i] is a consonant
    pub fn is_consonant(&self, i: uint) -> bool {
        match self.b.get(i).to_char() {
            'a' | 'e' | 'i' | 'o' | 'u' => false,
            'y' => if i == 0 {
                true
            } else {
                !self.is_consonant(i - 1)
            },
            _ => true,
        }
    }

    /// stem.measure() measures the number of consonant sequences in [0, j).
    /// if c is a consonant sequence and v a vowel sequence, and <..> indicates
    /// arbitrary presence,
    ///
    ///    <c><v>       gives 0
    ///    <c>vc<v>     gives 1
    ///    <c>vcvc<v>   gives 2
    ///    <c>vcvcvc<v> gives 3
    ///    ....
    ///
    pub fn measure(&self) -> uint {
        let mut n = 0u;
        let mut i = 0u;
        let j = self.j;
        loop {
            if i >= j { return n }
            if !self.is_consonant(i) { break }
            i += 1;
        }
        i += 1;
        loop {
            loop {
                if i >= j { return n }
                if self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
            n += 1;
            loop {
                if i >= j { return n }
                if !self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
        }
    }

    /// stem.has_vowel() is TRUE <=> [0, j-1) contains a vowel
    pub fn has_vowel(&self) -> bool {
        for i in range(0, self.j) {
            if !self.is_consonant(i) {
                return true;
            }
        }
        return false;
    }

    /// stem.double_consonant(i) is TRUE <=> i,(i-1) contain a double consonant.
    pub fn double_consonant(&self, i: uint) -> bool {
        if i < 1 {
            false
        } else if self.b.get(i) != self.b.get(i - 1) {
            false
        } else {
            self.is_consonant(i)
        }
    }

    /// cvc(z, i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant
    /// and also if the second c is not w,x or y. this is used when trying to
    /// restore an e at the end of a short word. e.g.
    ///
    ///    cav(e), lov(e), hop(e), crim(e), but
    ///    snow, box, tray.
    ///
    pub fn cvc(&self, i: uint) -> bool {
        if i < 2 || !self.is_consonant(i) || self.is_consonant(i - 1)
            || !self.is_consonant(i - 2) { return false }
        match self.b.get(i).to_char() {
            'w' | 'x' | 'y' => false,
            _ => true,
        }
    }

    /// stem.ends(s) is true <=> [0, k) ends with the string s.
    pub fn ends(&mut self, s: &str) -> bool {
        let len = s.len();
        let k = self.k;
        if s[len - 1] != self.b.get(k-1).to_byte() { return false } /* tiny speed-up */
        if len > k { return false }
        let mut iter = s.bytes();
        for ac in self.b.slice(k - len, k).iter() {
            if ac.to_byte() != iter.next().unwrap() { return false }
        }
        self.j = k - len;
        return true;
    }

    /// stem.setto(s) sets [j,k) to the characters in the string s,
    /// readjusting k.
    fn set_to(&mut self, s: &str) {
        let length = s.len();
        let j = self.j;
        for i in range(0, length) {
            *self.b.get_mut(j + i) = s[i].to_ascii();
        }
        self.k = j + length;
    }

    /// self.replace(s) is used further down.
    fn r(&mut self, s: &str) {
        if self.measure() > 0 {
            self.set_to(s);
        }
    }

    /// stem.step1ab() gets rid of plurals and -ed or -ing. e.g.
    ///
    ///     caresses  ->  caress
    ///     ponies    ->  poni
    ///     ties      ->  ti
    ///     caress    ->  caress
    ///     cats      ->  cat
    ///
    ///     feed      ->  feed
    ///     agreed    ->  agree
    ///     disabled  ->  disable
    ///
    ///     matting   ->  mat
    ///     mating    ->  mate
    ///     meeting   ->  meet
    ///     milling   ->  mill
    ///     messing   ->  mess
    ///
    ///     meetings  ->  meet
    ///
    pub fn step1ab(&mut self) {
        if self.b.get(self.k - 1).to_char() == 's' {
            if self.ends("sses") {
                self.k -= 2;
            } else if self.ends("ies") {
                self.set_to("i");
            } else if self.b.get(self.k - 2).to_char() != 's' {
                self.k -= 1;
            }
        }
        if self.ends("eed") {
            if self.measure() > 0 { self.k -= 1 }
        } else if (self.ends("ed") || self.ends("ing")) && self.has_vowel() {
            self.k = self.j;
            if self.ends("at") {
                self.set_to("ate");
            } else if self.ends("bl") {
                self.set_to("ble");
            } else if self.ends("iz") {
                self.set_to("ize");
            } else if self.double_consonant(self.k - 1) {
                self.k -= 1;
                match self.b.get(self.k - 1).to_char() {
                    'l' | 's' | 'z' => self.k += 1,
                    _ => (),
                }
            } else if self.measure() == 1 && self.cvc(self.k - 1) {
                self.set_to("e");
            }
        }
    }

    /// stem.step1c() turns terminal y to i when there is another vowel in the stem.
    pub fn step1c(&mut self) {
       if self.ends("y") && self.has_vowel() {
           *self.b.get_mut(self.k-1) = 'i'.to_ascii();
        }
    }

    /// stem.step2() maps double suffices to single ones. so -ization ( = -ize
    /// plus -ation) maps to -ize etc. note that the string before the suffix
    /// must give m(z) > 0.
    pub fn step2(&mut self) {
        match self.b.get(self.k-2).to_char() {
            'a' => {
                if self.ends("ational") { self.r("ate"); return }
                if self.ends("tional") { self.r("tion"); return }
            },
            'c' => {
                if self.ends("enci") { self.r("ence"); return }
                if self.ends("anci") { self.r("ance"); return }
            },
            'e' => if self.ends("izer") { self.r("ize"); return },
            'l' => {
                if self.ends("bli") { self.r("ble"); return } /*-DEPARTURE-*/

             /* To match the published algorithm, replace this line with
                'l' => {
                    if self.ends("abli") { self.r("able"); return } */

                 if self.ends("alli") { self.r("al"); return }
                 if self.ends("entli") { self.r("ent"); return }
                 if self.ends("eli") { self.r("e"); return }
                 if self.ends("ousli") { self.r("ous"); return }
            },
            'o' => {
                if self.ends("ization") { self.r("ize"); return }
                if self.ends("ation") { self.r("ate"); return }
                if self.ends("ator") { self.r("ate"); return }
            },
            's' => {
                if self.ends("alism") { self.r("al"); return }
                if self.ends("iveness") { self.r("ive"); return }
                if self.ends("fulness") { self.r("ful"); return }
                if self.ends("ousness") { self.r("ous"); return }
            },
            't' => {
                if self.ends("aliti") { self.r("al"); return }
                if self.ends("iviti") { self.r("ive"); return }
                if self.ends("biliti") { self.r("ble"); return }
            },
            'g' => if self.ends("logi") { self.r("log"); return }, /*-DEPARTURE-*/
             /* To match the published algorithm, delete this line */
            _ => (),
        }
    }

    /// stem.step3() deals with -ic-, -full, -ness etc. similar strategy to step2.
    pub fn step3(&mut self) {
        match self.b.get(self.k-1).to_char() {
            'e' => {
                if self.ends("icate") { self.r("ic"); return }
                if self.ends("ative") { self.r(""); return }
                if self.ends("alize") { self.r("al"); return }
            },
            'i' => if self.ends("iciti") { self.r("ic"); return },
            'l' => {
                if self.ends("ical") { self.r("ic"); return }
                if self.ends("ful") { self.r(""); return }
            },
            's' => if self.ends("ness") { self.r(""); return },
            _ => (),
        }
    }

    /// stem.step4() takes off -ant, -ence etc., in context <c>vcvc<v>.
    pub fn step4(&mut self) {
        match self.b.get(self.k-2).to_char() {
            'a' => {
                if self.ends("al") {}
                else { return }
            }
            'c' => {
                if self.ends("ance") {}
                else if self.ends("ence") {}
                else { return }
            },
            'e' => {
                if self.ends("er") {}
                else { return }
            }
            'i' => {
                if self.ends("ic") {}
                else { return }
            },
            'l' => {
                if self.ends("able") {}
                else if self.ends("ible") {}
                else { return }
            },
            'n' => {
                if self.ends("ant") {}
                else if self.ends("ement") {}
                else if self.ends("ment") {}
                else if self.ends("ent") {}
                else { return }
            },
            'o' => {
                if self.ends("ion")
                    && (self.b.get(self.j-1).to_char() == 's' || self.b.get(self.j-1).to_char() == 't') {}
                else if self.ends("ou") {}
                else { return }
                /* takes care of -ous */
            },
            's' => {
                if self.ends("ism") {}
                else { return }
            },
            't' => {
                if self.ends("ate") {}
                else if self.ends("iti") {}
                else { return }
            },
            'u' => {
                if self.ends("ous") {}
                else { return }
            },
            'v' => {
                if self.ends("ive") {}
                else { return }
            },
            'z' => {
                if self.ends("ize") {}
                else { return }
            },
            _ => return,
        }
        if self.measure() > 1 { self.k = self.j }
    }

    /// stem.step5() removes a final -e if self.measure() > 1, and changes -ll
    /// to -l if self.measure() > 1.
    pub fn step5(&mut self) {
       self.j = self.k;
       if self.b.get(self.k - 1).to_char() == 'e' {
           let a = self.measure();
           if a > 1 || a == 1 && !self.cvc(self.k - 2) { self.k -= 1 }
       }
       if self.b.get(self.k-1).to_char() == 'l'
           && self.double_consonant(self.k-1) && self.measure() > 1 {
           self.k-=1;
       }
    }

    pub fn get(&self) -> ~str {
        let borrowed = self.b.slice_to(self.k);
        borrowed.as_str_ascii().into_owned()
    }
}

pub fn get(word: &str) -> Result<~str, ~str> {
    if word.len() > 2 {
        match Stemmer::new(word) {
            Ok(w) => {
                let mut mw = w;
                mw.step1ab();
                mw.step1c();
                mw.step2();
                mw.step3();
                mw.step4();
                mw.step5();
                Ok(mw.get())
            }
            Err(e) => Err(e),
        }
    } else {
        Ok(word.into_owned())
    }
}

#[cfg(test)]
mod test {
    use std::io::File;
    use std::io::BufferedReader;
    use std::path;

    use super::get;

    #[test]
    fn lexicon() {
        let input = File::open(&path::Path::new("test-data/voc.txt")).unwrap();
        let result = File::open(&path::Path::new("test-data/output.txt")).unwrap();
        let mut input_reader = BufferedReader::new(input);
        let mut result_reader = BufferedReader::new(result);
        loop {
            match input_reader.read_line() {
                Ok(word) => match get(word.trim()) {
                    Ok(stem) => {
                        match result_reader.read_line() {
                            Ok(answer) => if answer.trim() != stem {
                                fail!("\n[FAILED] '{:s}' != '{:s}'", stem, answer);
                            } else {
                                print!(".");
                            },
                            Err(_) => break,
                        }
                    },
                    Err(e) => fail!("\n[FAILED] Cannot get stem for '{:s}': {:s}", word, e),
                },
                Err(_) => break,
            }
        }
        println!("");
    }
}
<commit_msg>updated to latest Rust<commit_after>#![crate_id = "stem#0.1.0"]
#![crate_type = "lib"]

use std::ascii;
use std::ascii::Ascii;
use std::vec::Vec;

/// Member b is a vector of std::ascii::Ascii holding a word to be stemmed.
/// The letters are in b[0], b[1] ... ending at b[z->k]. Member k is readjusted
/// downwards as the stemming progresses. Zero termination is not in fact used
/// in the algorithm.
///
/// Note that only lower case sequences are stemmed. Forcing to lower case
/// should be done before get(...) is called.
///
///
/// Typical usage is:
///
///     let b = ~"pencils";
///     let res = stem::get(b);
///         /- stem the 7 characters of b[0] to b[6]. The result, res,
///            will be "pencil" (the 's' is removed). -/
///
pub struct Stemmer {
    b: Vec<ascii::Ascii>,
    k: uint,
    j: uint,
}

impl Stemmer {
    pub fn new(word: &str) -> Result<Stemmer, &str> {
        if !word.is_ascii() {
            Err("Only support English words with ASCII characters")
        } else {
            let b = unsafe { word.to_ascii_nocheck().to_lower() };
            let k = b.len();
            Ok(Stemmer {
                b: b,
                k: k,
                j: 0,
            })
        }
    }

    /// stem.is_consonant(i) is true <=> stem[i] is a consonant
    pub fn is_consonant(&self, i: uint) -> bool {
        match self.b.get(i).to_char() {
            'a' | 'e' | 'i' | 'o' | 'u' => false,
            'y' => if i == 0 {
                true
            } else {
                !self.is_consonant(i - 1)
            },
            _ => true,
        }
    }

    /// stem.measure() measures the number of consonant sequences in [0, j).
    /// if c is a consonant sequence and v a vowel sequence, and <..> indicates
    /// arbitrary presence,
    ///
    ///    <c><v>       gives 0
    ///    <c>vc<v>     gives 1
    ///    <c>vcvc<v>   gives 2
    ///    <c>vcvcvc<v> gives 3
    ///    ....
    ///
    pub fn measure(&self) -> uint {
        let mut n = 0u;
        let mut i = 0u;
        let j = self.j;
        loop {
            if i >= j { return n }
            if !self.is_consonant(i) { break }
            i += 1;
        }
        i += 1;
        loop {
            loop {
                if i >= j { return n }
                if self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
            n += 1;
            loop {
                if i >= j { return n }
                if !self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
        }
    }

    /// stem.has_vowel() is TRUE <=> [0, j-1) contains a vowel
    pub fn has_vowel(&self) -> bool {
        for i in range(0, self.j) {
            if !self.is_consonant(i) {
                return true;
            }
        }
        return false;
    }

    /// stem.double_consonant(i) is TRUE <=> i,(i-1) contain a double consonant.
    pub fn double_consonant(&self, i: uint) -> bool {
        if i < 1 {
            false
        } else if self.b.get(i) != self.b.get(i - 1) {
            false
        } else {
            self.is_consonant(i)
        }
    }

    /// cvc(z, i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant
    /// and also if the second c is not w,x or y. this is used when trying to
    /// restore an e at the end of a short word. e.g.
    ///
    ///    cav(e), lov(e), hop(e), crim(e), but
    ///    snow, box, tray.
    ///
    pub fn cvc(&self, i: uint) -> bool {
        if i < 2 || !self.is_consonant(i) || self.is_consonant(i - 1)
            || !self.is_consonant(i - 2) { return false }
        match self.b.get(i).to_char() {
            'w' | 'x' | 'y' => false,
            _ => true,
        }
    }

    /// stem.ends(s) is true <=> [0, k) ends with the string s.
    pub fn ends(&mut self, s: &str) -> bool {
        let len = s.len();
        let k = self.k;
        if s[len - 1] != self.b.get(k-1).to_byte() { return false } /* tiny speed-up */
        if len > k { return false }
        let mut iter = s.bytes();
        for ac in self.b.slice(k - len, k).iter() {
            if ac.to_byte() != iter.next().unwrap() { return false }
        }
        self.j = k - len;
        return true;
    }

    /// stem.setto(s) sets [j,k) to the characters in the string s,
    /// readjusting k.
    fn set_to(&mut self, s: &str) {
        let length = s.len();
        let j = self.j;
        for i in range(0, length) {
            *self.b.get_mut(j + i) = s[i].to_ascii();
        }
        self.k = j + length;
    }

    /// self.replace(s) is used further down.
    fn r(&mut self, s: &str) {
        if self.measure() > 0 {
            self.set_to(s);
        }
    }

    /// stem.step1ab() gets rid of plurals and -ed or -ing. e.g.
    ///
    ///     caresses  ->  caress
    ///     ponies    ->  poni
    ///     ties      ->  ti
    ///     caress    ->  caress
    ///     cats      ->  cat
    ///
    ///     feed      ->  feed
    ///     agreed    ->  agree
    ///     disabled  ->  disable
    ///
    ///     matting   ->  mat
    ///     mating    ->  mate
    ///     meeting   ->  meet
    ///     milling   ->  mill
    ///     messing   ->  mess
    ///
    ///     meetings  ->  meet
    ///
    pub fn step1ab(&mut self) {
        if self.b.get(self.k - 1).to_char() == 's' {
            if self.ends("sses") {
                self.k -= 2;
            } else if self.ends("ies") {
                self.set_to("i");
            } else if self.b.get(self.k - 2).to_char() != 's' {
                self.k -= 1;
            }
        }
        if self.ends("eed") {
            if self.measure() > 0 { self.k -= 1 }
        } else if (self.ends("ed") || self.ends("ing")) && self.has_vowel() {
            self.k = self.j;
            if self.ends("at") {
                self.set_to("ate");
            } else if self.ends("bl") {
                self.set_to("ble");
            } else if self.ends("iz") {
                self.set_to("ize");
            } else if self.double_consonant(self.k - 1) {
                self.k -= 1;
                match self.b.get(self.k - 1).to_char() {
                    'l' | 's' | 'z' => self.k += 1,
                    _ => (),
                }
            } else if self.measure() == 1 && self.cvc(self.k - 1) {
                self.set_to("e");
            }
        }
    }

    /// stem.step1c() turns terminal y to i when there is another vowel in the stem.
    pub fn step1c(&mut self) {
       if self.ends("y") && self.has_vowel() {
           *self.b.get_mut(self.k-1) = 'i'.to_ascii();
        }
    }

    /// stem.step2() maps double suffices to single ones. so -ization ( = -ize
    /// plus -ation) maps to -ize etc. note that the string before the suffix
    /// must give m(z) > 0.
    pub fn step2(&mut self) {
        match self.b.get(self.k-2).to_char() {
            'a' => {
                if self.ends("ational") { self.r("ate"); return }
                if self.ends("tional") { self.r("tion"); return }
            },
            'c' => {
                if self.ends("enci") { self.r("ence"); return }
                if self.ends("anci") { self.r("ance"); return }
            },
            'e' => if self.ends("izer") { self.r("ize"); return },
            'l' => {
                if self.ends("bli") { self.r("ble"); return } /*-DEPARTURE-*/

             /* To match the published algorithm, replace this line with
                'l' => {
                    if self.ends("abli") { self.r("able"); return } */

                 if self.ends("alli") { self.r("al"); return }
                 if self.ends("entli") { self.r("ent"); return }
                 if self.ends("eli") { self.r("e"); return }
                 if self.ends("ousli") { self.r("ous"); return }
            },
            'o' => {
                if self.ends("ization") { self.r("ize"); return }
                if self.ends("ation") { self.r("ate"); return }
                if self.ends("ator") { self.r("ate"); return }
            },
            's' => {
                if self.ends("alism") { self.r("al"); return }
                if self.ends("iveness") { self.r("ive"); return }
                if self.ends("fulness") { self.r("ful"); return }
                if self.ends("ousness") { self.r("ous"); return }
            },
            't' => {
                if self.ends("aliti") { self.r("al"); return }
                if self.ends("iviti") { self.r("ive"); return }
                if self.ends("biliti") { self.r("ble"); return }
            },
            'g' => if self.ends("logi") { self.r("log"); return }, /*-DEPARTURE-*/
             /* To match the published algorithm, delete this line */
            _ => (),
        }
    }

    /// stem.step3() deals with -ic-, -full, -ness etc. similar strategy to step2.
    pub fn step3(&mut self) {
        match self.b.get(self.k-1).to_char() {
            'e' => {
                if self.ends("icate") { self.r("ic"); return }
                if self.ends("ative") { self.r(""); return }
                if self.ends("alize") { self.r("al"); return }
            },
            'i' => if self.ends("iciti") { self.r("ic"); return },
            'l' => {
                if self.ends("ical") { self.r("ic"); return }
                if self.ends("ful") { self.r(""); return }
            },
            's' => if self.ends("ness") { self.r(""); return },
            _ => (),
        }
    }

    /// stem.step4() takes off -ant, -ence etc., in context <c>vcvc<v>.
    pub fn step4(&mut self) {
        match self.b.get(self.k-2).to_char() {
            'a' => {
                if self.ends("al") {}
                else { return }
            }
            'c' => {
                if self.ends("ance") {}
                else if self.ends("ence") {}
                else { return }
            },
            'e' => {
                if self.ends("er") {}
                else { return }
            }
            'i' => {
                if self.ends("ic") {}
                else { return }
            },
            'l' => {
                if self.ends("able") {}
                else if self.ends("ible") {}
                else { return }
            },
            'n' => {
                if self.ends("ant") {}
                else if self.ends("ement") {}
                else if self.ends("ment") {}
                else if self.ends("ent") {}
                else { return }
            },
            'o' => {
                if self.ends("ion")
                    && (self.b.get(self.j-1).to_char() == 's' || self.b.get(self.j-1).to_char() == 't') {}
                else if self.ends("ou") {}
                else { return }
                /* takes care of -ous */
            },
            's' => {
                if self.ends("ism") {}
                else { return }
            },
            't' => {
                if self.ends("ate") {}
                else if self.ends("iti") {}
                else { return }
            },
            'u' => {
                if self.ends("ous") {}
                else { return }
            },
            'v' => {
                if self.ends("ive") {}
                else { return }
            },
            'z' => {
                if self.ends("ize") {}
                else { return }
            },
            _ => return,
        }
        if self.measure() > 1 { self.k = self.j }
    }

    /// stem.step5() removes a final -e if self.measure() > 1, and changes -ll
    /// to -l if self.measure() > 1.
    pub fn step5(&mut self) {
       self.j = self.k;
       if self.b.get(self.k - 1).to_char() == 'e' {
           let a = self.measure();
           if a > 1 || a == 1 && !self.cvc(self.k - 2) { self.k -= 1 }
       }
       if self.b.get(self.k-1).to_char() == 'l'
           && self.double_consonant(self.k-1) && self.measure() > 1 {
           self.k-=1;
       }
    }

    pub fn get(&self) -> String {
        let borrowed = self.b.slice_to(self.k);
        borrowed.as_str_ascii().into_string()
    }
}

pub fn get(word: &str) -> Result<String, &str> {
    if word.len() > 2 {
        match Stemmer::new(word) {
            Ok(w) => {
                let mut mw = w;
                mw.step1ab();
                mw.step1c();
                mw.step2();
                mw.step3();
                mw.step4();
                mw.step5();
                Ok(mw.get())
            }
            Err(e) => Err(e),
        }
    } else {
        Ok(word.into_string())
    }
}

#[cfg(test)]
mod test {
    use std::io::File;
    use std::io::BufferedReader;
    use std::path;

    use super::get;

    #[test]
    fn lexicon() {
        let input = File::open(&path::Path::new("test-data/voc.txt")).unwrap();
        let result = File::open(&path::Path::new("test-data/output.txt")).unwrap();
        let mut input_reader = BufferedReader::new(input);
        let mut result_reader = BufferedReader::new(result);
        loop {
            match input_reader.read_line() {
                Ok(word) => match get(word.as_slice().trim()) {
                    Ok(stem) => {
                        match result_reader.read_line() {
                            Ok(answer) => if answer.as_slice().trim() != stem.as_slice() {
                                fail!("\n[FAILED] '{:s}' != '{:s}'", stem, answer);
                            } else {
                                print!(".");
                            },
                            Err(_) => break,
                        }
                    },
                    Err(e) => fail!("\n[FAILED] Cannot get stem for '{:s}': {:s}", word, e),
                },
                Err(_) => break,
            }
        }
        println!("");
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::thread;
use std::time as stdtime;

use time;

use scheduler::Scheduler;
use cron::CronEntry;
use error::Error;

use job::Job;
use output_keeper::OutputKeeper;

pub struct Task<'a, 'b: 'a> {
    scheduler: Scheduler<'a>,
    cron: CronEntry,
    keeper: &'a OutputKeeper<'b>,
    job: Option<Job<'a, 'b>>,
}

impl<'a, 'b: 'a> Task<'a, 'b> {
    pub fn new(sch: Scheduler<'a>, cron: CronEntry, keeper: &'b OutputKeeper) -> Task<'a, 'b> {
        Task {
            scheduler: sch,
            cron: cron,
            job: None,
            keeper: keeper,
        }
    }

    // start() periodically make a new job and then execute it in background.
    pub fn start(&mut self) {
        loop {
            let tm = time::now();

            if self.scheduler.isTimeUp(&tm) {
                let mut job = Job::new(100, self.cron.to_command(), self.keeper);
                job.start();
            }

            // sleep
            thread::sleep(stdtime::Duration::from_millis(1000u64 * (60u64-tm.tm_sec)));
        }
    }

    // fn is_running(&self) -> bool {
    //     //TODO: fix it, and make it public
    //     false
    // }
}
<commit_msg>fix(task): cargo publish error<commit_after>use std::str::FromStr;
use std::thread;
use std::time as stdtime;

use time;

use scheduler::Scheduler;
use cron::CronEntry;
use error::Error;

use job::Job;
use output_keeper::OutputKeeper;

pub struct Task<'a, 'b: 'a> {
    scheduler: Scheduler<'a>,
    cron: CronEntry,
    keeper: &'a OutputKeeper<'b>,
    job: Option<Job<'a, 'b>>,
}

impl<'a, 'b: 'a> Task<'a, 'b> {
    pub fn new(sch: Scheduler<'a>, cron: CronEntry, keeper: &'b OutputKeeper) -> Task<'a, 'b> {
        Task {
            scheduler: sch,
            cron: cron,
            job: None,
            keeper: keeper,
        }
    }

    // start() periodically make a new job and then execute it in background.
    pub fn start(&mut self) {
        loop {
            let tm = time::now();

            if self.scheduler.isTimeUp(&tm) {
                let mut job = Job::new(100, self.cron.to_command(), self.keeper);
                job.start();
            }

            // sleep
            thread::sleep(stdtime::Duration::from_millis((60-tm.tm_sec) as u64 * 1000u64));
        }
    }

    // fn is_running(&self) -> bool {
    //     //TODO: fix it, and make it public
    //     false
    // }
}
<|endoftext|>


<commit_before>//! Implementation of Reversi rules to play a turn.

use board::*;
use ::Result;

/// A turn can be in two states: either running (with a side to play next) or ended.
pub type State = Option<::Side>;

/// A turn is given by a board and by which player has to move next.
/// For convenience we also annotate current scores.
#[derive(Debug, Clone, Copy)]
pub struct Turn {
    board: Board,
    state: State,
    score_dark: u8,
    score_light: u8,
}

impl Turn {
    /// Initializing a new first turn: starting positions on the board and Dark is the first to play
    #[inline(always)]
    pub fn first_turn() -> Turn {
        let mut board = Board::new([[None; BOARD_SIZE]; BOARD_SIZE]);
        let center = BOARD_SIZE/2;
        board.place_disk(::Side::Dark, Coord::new(center - 1, center))
            .and(board.place_disk(::Side::Dark, Coord::new(center, center - 1)))
            .and(board.place_disk(::Side::Light, Coord::new(center - 1, center - 1)))
            .and(board.place_disk(::Side::Light, Coord::new(center, center)))
            .expect("Initial board setup failed");

        Turn {
            board: board,
            state: Some(::Side::Dark),
            score_dark: 2,
            score_light: 2,
        }
    }

    /// Returns the turn's board
    #[inline(always)]
    pub fn get_board(&self) -> &Board {
        &self.board
    }

    /// Returns the board's cell corresponding to the given coordinates.
    #[inline(always)]
    pub fn get_cell(&self, coord: Coord) -> Result<&Cell> {
        self.board.get_cell(coord)
    }

    /// Returns the turn's status
    #[inline(always)]
    pub fn get_state(&self) -> State {
        self.state
    }

    /// Returns whether the turn has `None` state.
    #[inline(always)]
    pub fn is_end_state(&self) -> bool {
        self.state.is_none()
    }

    /// Returns the current score of the match.
    #[inline(always)]
    pub fn get_score(&self) -> (u8, u8) {
        (self.score_dark, self.score_light)
    }

    /// Returns the difference in score between Light and Dark.
    #[inline(always)]
    pub fn get_score_diff(&self) -> i16 {
        self.score_light as i16 - self.score_dark as i16
    }

    /// Returns turn's tempo (how many disks there are on the board).
    #[inline(always)]
    pub fn get_tempo(&self) -> u8 {
        self.score_light + self.score_dark
    }

    /// Checks whether a move leads to eat in a specified direction
    #[inline(always)]
    fn check_move_along_direction (&self, coord: Coord, dir: Direction, side: ::Side) -> bool {
        let mut next_coord = coord.step(dir);
        if let Ok(&Some(next_disk)) = self.get_cell(next_coord) {
            if next_disk.get_side() != side {
                next_coord = next_coord.step(dir);
                while let Ok(&Some(next_disk)) = self.board.get_cell(next_coord) {
                    if next_disk.get_side() == side {
                        return true;
                    }
                    next_coord = next_coord.step(dir);
                }
            }
        }
        false
    }

    /// Check whether a given move is legal
    #[inline]
    pub fn check_move (&self, coord: Coord) -> Result<()> {
        // If the game is ended, no further moves are possible
        let state_side = self.state.ok_or_else(|| ::ReversiError::EndedGame(*self))?;
        const THIRD_TO_LAST: usize = BOARD_SIZE - 3;
        const SECOND_TO_LAST: usize = BOARD_SIZE - 2;

        macro_rules! check_move_along_directions {
            ($dir:ident, $($dirs:ident),+) => (
                self.check_move_along_direction(coord, Direction::$dir, state_side) || check_move_along_directions!($($dirs),+)
            );
            ($dir:ident) => (
                self.check_move_along_direction(coord, Direction::$dir, state_side)
            );
        }

        if self.board.get_cell(coord)?.is_some() {
            // If a cell is already taken, it's not possible to move there
            Err(::ReversiError::CellAlreadyTaken(coord))
        } else if match coord.get_row() {
            0...1 => check_move_along_directions!(South)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, SW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, SE)),
            2...THIRD_TO_LAST => check_move_along_directions!(North, South)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, SW, NW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, NE, SE)),
            _ => check_move_along_directions!(North)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, NW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, NE)),
        } {
            // If a move leads to eat in at least one direction, then it is legal
            Ok(())
        } else {
            // Otherwise, the move is not legal
            Err(::ReversiError::IllegalMove(coord))
        }
    }

    /// Current player performs a move, after verifying that it is legal.
    /// It returns either the new turn or the error preventing the move to be performed.
    #[inline]
    pub fn make_move (&mut self, coord: Coord) -> Result<()> {
        if self.get_cell(coord)?.is_none() {
            let turn_side = self.state.ok_or_else(|| ::ReversiError::EndedGame(*self))?;
            let mut legal = false;
            let mut eating: u8 = 0;
            for &dir in DIRECTIONS.into_iter().filter(|&&dir|
                (coord.get_row() >= 2                   || (dir != Direction::North && dir != Direction::NE && dir != Direction::NW))
                && (coord.get_row() < BOARD_SIZE - 2    || (dir != Direction::South && dir != Direction::SE && dir != Direction::SW))
                && (coord.get_col() >= 2                || (dir != Direction::West  && dir != Direction::NW && dir != Direction::SW))
                && (coord.get_col() < BOARD_SIZE - 2    || (dir != Direction::East  && dir != Direction::NE && dir != Direction::SE)) ) {
                if self.check_move_along_direction(coord, dir, turn_side) {
                    // Eats all of the opponent's occupied cells from a specified cell (given by its coordinates) in a specified direction until it finds a cell of the current player.
                    let mut next_coord = coord.step(dir);
                    self.board.flip_disk(next_coord)
                        .expect("Eating in this direction has already been checked to work!");
                    eating += 1;
                    next_coord = next_coord.step(dir);
                    while let Ok(&Some(disk)) = self.board.get_cell(next_coord) {
                        if disk.get_side() != turn_side {
                            self.board.flip_disk(next_coord)
                                .expect("Eating in this direction has already been checked to work!");
                            eating += 1;
                            next_coord = next_coord.step(dir);
                        } else {
                            legal = true;
                            break;
                        }
                    }
                }
            }
            if legal {
                self.board.place_disk(turn_side, coord)
                    .expect("This cell has already been checked empty!");
                match turn_side {
                    ::Side::Dark => {
                        self.score_light -= eating;
                        self.score_dark  += eating + 1;
                    }
                    ::Side::Light => {
                        self.score_light += eating + 1;
                        self.score_dark  -= eating;
                    }
                }
                // If a move is legal, the next player to play has to be determined
                // If the opposite player can make any move at all, it gets the turn
                // If not, if the previous player can make any move at all, it gets the turn
                // If not (that is, if no player can make any move at all) the game is ended
                if self.get_tempo() == NUM_CELLS as u8 {
                    // Quick check to rule out games with filled up boards as ended.
                    self.state = None;
                } else {
                    // Turn passes to the other player.
                    self.state = Some(turn_side.opposite());
                    if !self.can_move() {
                        // If the other player cannot move, turn passes back to the first player.
                        self.state = Some(turn_side);
                        if !self.can_move() {
                            // If neither platers can move, game is ended.
                            self.state = None;
                        }
                    }
                }
                Ok(())
            } else {
                Err(::ReversiError::IllegalMove(coord))
            }
        } else {
            Err(::ReversiError::CellAlreadyTaken(coord))
        }
    }

    /// Returns whether or not next_player can make any move at all.
    /// To be used privately. User should rather look at turn's state.
 //     #[inline(always)]
 //     fn can_move(&self) -> bool {
 //        (0..BOARD_SIZE).any(|row|
 //            (0..BOARD_SIZE).any(|col|
 //                self.check_move(Coord::new(row, col)).is_ok()))
 //     }

    #[inline]
    fn can_move(&self) -> bool {
        if let Some(state_side) = self.state {
            macro_rules! check_move_along_directions {
                ($coord:expr, $($dirs:ident),+) => ({
                    let coord = $coord;
                    self.get_cell(coord).expect("This coord should be alright!").is_none()
                        && check_move_along_directions_after_setup!(coord, $($dirs),+)
                });
            }
            macro_rules! check_move_along_directions_after_setup {
                ($coord:ident, $dir:ident, $($dirs:ident),+) => (
                    self.check_move_along_direction($coord, Direction::$dir, state_side)
                        || check_move_along_directions_after_setup!($coord, $($dirs),+)
                );
                ($coord:ident, $dir:ident) => (
                    self.check_move_along_direction($coord, Direction::$dir, state_side)
                );
            }

            (BOARD_SIZE-2..BOARD_SIZE).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), North, West, NW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, West, NW))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East))
            } ) || (2..BOARD_SIZE-2).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), North, West, South, SW, NW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, SE, South, SW, West, NW))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, SE, South))
            } ) || (0..2).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), West, South, SW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), East, SE, South, SW, West))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), East, SE, South))
            })
        } else {
            false
        }
    }

}
<commit_msg>Add `inline(always)` back<commit_after>//! Implementation of Reversi rules to play a turn.

use board::*;
use ::Result;

/// A turn can be in two states: either running (with a side to play next) or ended.
pub type State = Option<::Side>;

/// A turn is given by a board and by which player has to move next.
/// For convenience we also annotate current scores.
#[derive(Debug, Clone, Copy)]
pub struct Turn {
    board: Board,
    state: State,
    score_dark: u8,
    score_light: u8,
}

impl Turn {
    /// Initializing a new first turn: starting positions on the board and Dark is the first to play
    #[inline(always)]
    pub fn first_turn() -> Turn {
        let mut board = Board::new([[None; BOARD_SIZE]; BOARD_SIZE]);
        let center = BOARD_SIZE/2;
        board.place_disk(::Side::Dark, Coord::new(center - 1, center))
            .and(board.place_disk(::Side::Dark, Coord::new(center, center - 1)))
            .and(board.place_disk(::Side::Light, Coord::new(center - 1, center - 1)))
            .and(board.place_disk(::Side::Light, Coord::new(center, center)))
            .expect("Initial board setup failed");

        Turn {
            board: board,
            state: Some(::Side::Dark),
            score_dark: 2,
            score_light: 2,
        }
    }

    /// Returns the turn's board
    #[inline(always)]
    pub fn get_board(&self) -> &Board {
        &self.board
    }

    /// Returns the board's cell corresponding to the given coordinates.
    #[inline(always)]
    pub fn get_cell(&self, coord: Coord) -> Result<&Cell> {
        self.board.get_cell(coord)
    }

    /// Returns the turn's status
    #[inline(always)]
    pub fn get_state(&self) -> State {
        self.state
    }

    /// Returns whether the turn has `None` state.
    #[inline(always)]
    pub fn is_end_state(&self) -> bool {
        self.state.is_none()
    }

    /// Returns the current score of the match.
    #[inline(always)]
    pub fn get_score(&self) -> (u8, u8) {
        (self.score_dark, self.score_light)
    }

    /// Returns the difference in score between Light and Dark.
    #[inline(always)]
    pub fn get_score_diff(&self) -> i16 {
        self.score_light as i16 - self.score_dark as i16
    }

    /// Returns turn's tempo (how many disks there are on the board).
    #[inline(always)]
    pub fn get_tempo(&self) -> u8 {
        self.score_light + self.score_dark
    }

    /// Checks whether a move leads to eat in a specified direction
    #[inline(always)]
    fn check_move_along_direction (&self, coord: Coord, dir: Direction, side: ::Side) -> bool {
        let mut next_coord = coord.step(dir);
        if let Ok(&Some(next_disk)) = self.get_cell(next_coord) {
            if next_disk.get_side() != side {
                next_coord = next_coord.step(dir);
                while let Ok(&Some(next_disk)) = self.board.get_cell(next_coord) {
                    if next_disk.get_side() == side {
                        return true;
                    }
                    next_coord = next_coord.step(dir);
                }
            }
        }
        false
    }

    /// Check whether a given move is legal
    #[inline(always)]
    pub fn check_move (&self, coord: Coord) -> Result<()> {
        // If the game is ended, no further moves are possible
        let state_side = self.state.ok_or_else(|| ::ReversiError::EndedGame(*self))?;
        const THIRD_TO_LAST: usize = BOARD_SIZE - 3;
        const SECOND_TO_LAST: usize = BOARD_SIZE - 2;

        macro_rules! check_move_along_directions {
            ($dir:ident, $($dirs:ident),+) => (
                self.check_move_along_direction(coord, Direction::$dir, state_side) || check_move_along_directions!($($dirs),+)
            );
            ($dir:ident) => (
                self.check_move_along_direction(coord, Direction::$dir, state_side)
            );
        }

        if self.board.get_cell(coord)?.is_some() {
            // If a cell is already taken, it's not possible to move there
            Err(::ReversiError::CellAlreadyTaken(coord))
        } else if match coord.get_row() {
            0...1 => check_move_along_directions!(South)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, SW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, SE)),
            2...THIRD_TO_LAST => check_move_along_directions!(North, South)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, SW, NW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, NE, SE)),
            _ => check_move_along_directions!(North)
                || (coord.get_col() >= 2 && check_move_along_directions!(West, NW))
                || (coord.get_col() < SECOND_TO_LAST && check_move_along_directions!(East, NE)),
        } {
            // If a move leads to eat in at least one direction, then it is legal
            Ok(())
        } else {
            // Otherwise, the move is not legal
            Err(::ReversiError::IllegalMove(coord))
        }
    }

    /// Current player performs a move, after verifying that it is legal.
    /// It returns either the new turn or the error preventing the move to be performed.
    #[inline(always)]
    pub fn make_move (&mut self, coord: Coord) -> Result<()> {
        if self.get_cell(coord)?.is_none() {
            let turn_side = self.state.ok_or_else(|| ::ReversiError::EndedGame(*self))?;
            let mut legal = false;
            let mut eating: u8 = 0;
            for &dir in DIRECTIONS.into_iter().filter(|&&dir|
                (coord.get_row() >= 2                   || (dir != Direction::North && dir != Direction::NE && dir != Direction::NW))
                && (coord.get_row() < BOARD_SIZE - 2    || (dir != Direction::South && dir != Direction::SE && dir != Direction::SW))
                && (coord.get_col() >= 2                || (dir != Direction::West  && dir != Direction::NW && dir != Direction::SW))
                && (coord.get_col() < BOARD_SIZE - 2    || (dir != Direction::East  && dir != Direction::NE && dir != Direction::SE)) ) {
                if self.check_move_along_direction(coord, dir, turn_side) {
                    // Eats all of the opponent's occupied cells from a specified cell (given by its coordinates) in a specified direction until it finds a cell of the current player.
                    let mut next_coord = coord.step(dir);
                    self.board.flip_disk(next_coord)
                        .expect("Eating in this direction has already been checked to work!");
                    eating += 1;
                    next_coord = next_coord.step(dir);
                    while let Ok(&Some(disk)) = self.board.get_cell(next_coord) {
                        if disk.get_side() != turn_side {
                            self.board.flip_disk(next_coord)
                                .expect("Eating in this direction has already been checked to work!");
                            eating += 1;
                            next_coord = next_coord.step(dir);
                        } else {
                            legal = true;
                            break;
                        }
                    }
                }
            }
            if legal {
                self.board.place_disk(turn_side, coord)
                    .expect("This cell has already been checked empty!");
                match turn_side {
                    ::Side::Dark => {
                        self.score_light -= eating;
                        self.score_dark  += eating + 1;
                    }
                    ::Side::Light => {
                        self.score_light += eating + 1;
                        self.score_dark  -= eating;
                    }
                }
                // If a move is legal, the next player to play has to be determined
                // If the opposite player can make any move at all, it gets the turn
                // If not, if the previous player can make any move at all, it gets the turn
                // If not (that is, if no player can make any move at all) the game is ended
                if self.get_tempo() == NUM_CELLS as u8 {
                    // Quick check to rule out games with filled up boards as ended.
                    self.state = None;
                } else {
                    // Turn passes to the other player.
                    self.state = Some(turn_side.opposite());
                    if !self.can_move() {
                        // If the other player cannot move, turn passes back to the first player.
                        self.state = Some(turn_side);
                        if !self.can_move() {
                            // If neither platers can move, game is ended.
                            self.state = None;
                        }
                    }
                }
                Ok(())
            } else {
                Err(::ReversiError::IllegalMove(coord))
            }
        } else {
            Err(::ReversiError::CellAlreadyTaken(coord))
        }
    }

    /// Returns whether or not next_player can make any move at all.
    /// To be used privately. User should rather look at turn's state.
 //     #[inline(always)]
 //     fn can_move(&self) -> bool {
 //        (0..BOARD_SIZE).any(|row|
 //            (0..BOARD_SIZE).any(|col|
 //                self.check_move(Coord::new(row, col)).is_ok()))
 //     }

    #[inline(always)]
    fn can_move(&self) -> bool {
        if let Some(state_side) = self.state {
            macro_rules! check_move_along_directions {
                ($coord:expr, $($dirs:ident),+) => ({
                    let coord = $coord;
                    self.get_cell(coord).expect("This coord should be alright!").is_none()
                        && check_move_along_directions_after_setup!(coord, $($dirs),+)
                });
            }
            macro_rules! check_move_along_directions_after_setup {
                ($coord:ident, $dir:ident, $($dirs:ident),+) => (
                    self.check_move_along_direction($coord, Direction::$dir, state_side)
                        || check_move_along_directions_after_setup!($coord, $($dirs),+)
                );
                ($coord:ident, $dir:ident) => (
                    self.check_move_along_direction($coord, Direction::$dir, state_side)
                );
            }

            (BOARD_SIZE-2..BOARD_SIZE).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), North, West, NW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, West, NW))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East))
            } ) || (2..BOARD_SIZE-2).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), North, West, South, SW, NW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, SE, South, SW, West, NW))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), North, NE, East, SE, South))
            } ) || (0..2).any(|row| {
                (BOARD_SIZE-2..BOARD_SIZE).any(|col| check_move_along_directions!(Coord::new(row, col), West, South, SW))
                || (2..BOARD_SIZE-2).any(|col| check_move_along_directions!(Coord::new(row, col), East, SE, South, SW, West))
                || (0..2).any(|col| check_move_along_directions!(Coord::new(row, col), East, SE, South))
            })
        } else {
            false
        }
    }

}
<|endoftext|>


<commit_before>use hyper::header::{Accept, Connection, ContentType, Headers, Quality,
                    QualityItem, qitem};
use hyper::mime::{Mime, SubLevel, TopLevel};
use protobuf::{self, Message};

use proto::mesos::*;

pub fn protobuf_headers() -> Headers {
    let mut headers = Headers::new();

    headers.set(Accept(vec![
            qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
            qitem(Mime(TopLevel::Application,
            SubLevel::Ext("x-protobuf".to_owned()), vec![])),
        ]));

    headers.set(ContentType(Mime(TopLevel::Application,
                                 SubLevel::Ext("x-protobuf".to_owned()),
                                 vec![])));

    headers
}

pub fn framework_id<'a>(id: &'a str) -> FrameworkID {
    let mut framework_id = FrameworkID::new();
    framework_id.set_value(id.to_string());
    framework_id
}

pub fn framework_info<'a>(user: &'a str,
                          name: &'a str,
                          failover_timeout: f64)
                          -> FrameworkInfo {
    let mut framework_info = FrameworkInfo::new();
    framework_info.set_user(user.to_string());
    framework_info.set_name(name.to_string());
    framework_info.set_failover_timeout(failover_timeout);
    framework_info
}

pub fn task_id<'a>(id: &'a str) -> TaskID {
    let mut task_id = TaskID::new();
    task_id.set_value(id.to_string());
    task_id
}

pub fn task_info<'a>(name: &'a str,
                     task_id: &TaskID,
                     agent_id: &AgentID,
                     command: &CommandInfo,
                     resources: Vec<Resource>)
                     -> TaskInfo {

    let mut task_info = TaskInfo::new();
    task_info.set_name(name.to_string());
    task_info.set_task_id(task_id.clone());
    task_info.set_agent_id(agent_id.clone());
    task_info.set_command(command.clone());
    task_info.set_resources(protobuf::RepeatedField::from_vec(resources));
    task_info
}

pub fn launch_operation(task_infos: Vec<TaskInfo>) -> Operation {
    let mut launch = Operation_Launch::new();
    launch.set_task_infos(protobuf::RepeatedField::from_vec(task_infos));

    let mut operation = Operation::new();
    operation.set_field_type(Operation_Type::LAUNCH);
    operation.set_launch(launch);
    operation
}

pub fn scalar<'a>(name: &'a str, role: &'a str, value: f64) -> Resource {
    let mut scalar = Value_Scalar::new();
    scalar.set_value(value);

    let mut res = Resource::new();
    res.set_name(name.to_string());
    res.set_role(role.to_string());
    res.set_field_type(Value_Type::SCALAR);
    res.set_scalar(scalar);
    res
}

pub fn get_scalar_resource_sum<'a>(name: &'a str, offers: Vec<&Offer>) -> f64 {
    offers.iter()
          .flat_map(|o| o.get_resources())
          .filter(|r| r.get_name() == "mem")
          .map(|c| c.get_scalar())
          .fold(0f64, |acc, mem_res| acc + mem_res.get_value())
}
<commit_msg>added task_info_for_container function to utils<commit_after>use hyper::header::{Accept, Connection, ContentType, Headers, Quality,
                    QualityItem, qitem};
use hyper::mime::{Mime, SubLevel, TopLevel};
use protobuf::{self, Message};

use proto::mesos::*;

pub fn protobuf_headers() -> Headers {
    let mut headers = Headers::new();

    headers.set(Accept(vec![
            qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
            qitem(Mime(TopLevel::Application,
            SubLevel::Ext("x-protobuf".to_owned()), vec![])),
        ]));

    headers.set(ContentType(Mime(TopLevel::Application,
                                 SubLevel::Ext("x-protobuf".to_owned()),
                                 vec![])));

    headers
}

pub fn framework_id<'a>(id: &'a str) -> FrameworkID {
    let mut framework_id = FrameworkID::new();
    framework_id.set_value(id.to_string());
    framework_id
}

pub fn framework_info<'a>(user: &'a str,
                          name: &'a str,
                          failover_timeout: f64)
                          -> FrameworkInfo {
    let mut framework_info = FrameworkInfo::new();
    framework_info.set_user(user.to_string());
    framework_info.set_name(name.to_string());
    framework_info.set_failover_timeout(failover_timeout);
    framework_info
}

pub fn task_id<'a>(id: &'a str) -> TaskID {
    let mut task_id = TaskID::new();
    task_id.set_value(id.to_string());
    task_id
}

pub fn task_info<'a>(name: &'a str,
                     task_id: &TaskID,
                     agent_id: &AgentID,
                     command: &CommandInfo,
                     resources: Vec<Resource>)
                     -> TaskInfo {

    let mut task_info = TaskInfo::new();
    task_info.set_name(name.to_string());
    task_info.set_task_id(task_id.clone());
    task_info.set_agent_id(agent_id.clone());
    task_info.set_command(command.clone());
    task_info.set_resources(protobuf::RepeatedField::from_vec(resources));
    task_info
}

pub fn task_info_for_container<'a>(name: &'a str,
                                   task_id: &TaskID,
                                   agent_id: &AgentID,
                                   command: &CommandInfo,
                                   container: &ContainerInfo,
                                   resources: Vec<Resource>)
                                   -> TaskInfo {
    let mut task_info = TaskInfo::new();
    task_info.set_name(name.to_string());
    task_info.set_task_id(task_id.clone());
    task_info.set_agent_id(agent_id.clone());
    task_info.set_command(command.clone());
    task_info.set_container(container.clone());
    task_info.set_resources(protobuf::RepeatedField::from_vec(resources));
    task_info
}

pub fn launch_operation(task_infos: Vec<TaskInfo>) -> Operation {
    let mut launch = Operation_Launch::new();
    launch.set_task_infos(protobuf::RepeatedField::from_vec(task_infos));

    let mut operation = Operation::new();
    operation.set_field_type(Operation_Type::LAUNCH);
    operation.set_launch(launch);
    operation
}

pub fn scalar<'a>(name: &'a str, role: &'a str, value: f64) -> Resource {
    let mut scalar = Value_Scalar::new();
    scalar.set_value(value);

    let mut res = Resource::new();
    res.set_name(name.to_string());
    res.set_role(role.to_string());
    res.set_field_type(Value_Type::SCALAR);
    res.set_scalar(scalar);
    res
}

pub fn get_scalar_resource_sum<'a>(name: &'a str, offers: Vec<&Offer>) -> f64 {
    offers.iter()
          .flat_map(|o| o.get_resources())
          .filter(|r| r.get_name() == "mem")
          .map(|c| c.get_scalar())
          .fold(0f64, |acc, mem_res| acc + mem_res.get_value())
}
<|endoftext|>


<commit_before>use line::Line;
use update::Update;

#[derive(Clone)]
pub struct View {
    last_rev: u64,
    pub filepath: String,
    pub lines: Vec<Line>,
}

impl View {
    pub fn new(filepath: String) -> View {
        View {
            last_rev: 0,
            filepath: filepath,
            lines: vec![],
        }
    }

    pub fn update(&mut self, update: &Update) {
        if self.last_rev > update.rev {
            return;
        }
        let mut lines = vec![];
        let mut index = 0;

        for op in update.ops.iter() {
            index = op.apply(&self.lines, index, &mut lines);
        }

        self.last_rev = update.rev;
        self.lines = lines;
    }
}
<commit_msg>clippy: explicit_iter_loop<commit_after>use line::Line;
use update::Update;

#[derive(Clone)]
pub struct View {
    last_rev: u64,
    pub filepath: String,
    pub lines: Vec<Line>,
}

impl View {
    pub fn new(filepath: String) -> View {
        View {
            last_rev: 0,
            filepath: filepath,
            lines: vec![],
        }
    }

    pub fn update(&mut self, update: &Update) {
        if self.last_rev > update.rev {
            return;
        }
        let mut lines = vec![];
        let mut index = 0;

        for op in &update.ops {
            index = op.apply(&self.lines, index, &mut lines);
        }

        self.last_rev = update.rev;
        self.lines = lines;
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This example creates a 200x200 white rect and allows the user to move it
//! around by using the arrow keys and rotate with '<'/'>'.
//! It does this by using the animation API.

//! The example also features seamless opaque/transparent split of a
//! rounded cornered rectangle, which is done automatically during the
//! scene building for render optimization.

extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;

#[path = "common/boilerplate.rs"]
mod boilerplate;

use boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Angle;
use webrender::api::*;

struct App {
    property_key0: PropertyBindingKey<LayoutTransform>,
    property_key1: PropertyBindingKey<LayoutTransform>,
    property_key2: PropertyBindingKey<LayoutTransform>,
    opacity_key: PropertyBindingKey<f32>,
    opacity: f32,
    angle0: f32,
    angle1: f32,
    angle2: f32,
}

impl App {
    fn add_rounded_rect(
        &mut self,
        bounds: LayoutRect,
        color: ColorF,
        builder: &mut DisplayListBuilder,
        property_key: PropertyBindingKey<LayoutTransform>,
    ) {
        let filters = [
            FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
        ];

        let reference_frame_id = builder.push_reference_frame(
            &LayoutPrimitiveInfo::new(LayoutRect::new(bounds.origin, LayoutSize::zero())),
            TransformStyle::Flat,
            Some(PropertyBinding::Binding(property_key, LayoutTransform::identity())),
            None,
        );

        builder.push_clip_id(reference_frame_id);

        builder.push_stacking_context(
            &LayoutPrimitiveInfo::new(LayoutRect::zero()),
            None,
            TransformStyle::Flat,
            MixBlendMode::Normal,
            &filters,
            RasterSpace::Screen,
        );

        let clip_bounds = LayoutRect::new(LayoutPoint::zero(), bounds.size);
        let complex_clip = ComplexClipRegion {
            rect: clip_bounds,
            radii: BorderRadius::uniform(30.0),
            mode: ClipMode::Clip,
        };
        let clip_id = builder.define_clip(clip_bounds, vec![complex_clip], None);
        builder.push_clip_id(clip_id);

        // Fill it with a white rect
        builder.push_rect(
            &LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), bounds.size)),
            color,
        );

        builder.pop_clip_id();

        builder.pop_stacking_context();

        builder.pop_clip_id();
        builder.pop_reference_frame();
    }
}

impl Example for App {
    const WIDTH: u32 = 1024;
    const HEIGHT: u32 = 1024;

    fn render(
        &mut self,
        _api: &RenderApi,
        builder: &mut DisplayListBuilder,
        _txn: &mut Transaction,
        _framebuffer_size: DeviceIntSize,
        _pipeline_id: PipelineId,
        _document_id: DocumentId,
    ) {
        let bounds = (150, 150).to(250, 250);
        let key0 = self.property_key0;
        self.add_rounded_rect(bounds, ColorF::new(1.0, 0.0, 0.0, 0.5), builder, key0);

        let bounds = (400, 400).to(600, 600);
        let key1 = self.property_key1;
        self.add_rounded_rect(bounds, ColorF::new(0.0, 1.0, 0.0, 0.5), builder, key1);

        let bounds = (200, 500).to(350, 580);
        let key2 = self.property_key2;
        self.add_rounded_rect(bounds, ColorF::new(0.0, 0.0, 1.0, 0.5), builder, key2);
    }

    fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
        match win_event {
            winit::WindowEvent::KeyboardInput {
                input: winit::KeyboardInput {
                    state: winit::ElementState::Pressed,
                    virtual_keycode: Some(key),
                    ..
                },
                ..
            } => {
                let (delta_angle, delta_opacity) = match key {
                    winit::VirtualKeyCode::Down => (0.0, -0.1),
                    winit::VirtualKeyCode::Up => (0.0, 0.1),
                    winit::VirtualKeyCode::Right => (1.0, 0.0),
                    winit::VirtualKeyCode::Left => (-1.0, 0.0),
                    _ => return false,
                };
                // Update the transform based on the keyboard input and push it to
                // webrender using the generate_frame API. This will recomposite with
                // the updated transform.
                self.opacity += delta_opacity;
                self.angle0 += delta_angle * 0.1;
                self.angle1 += delta_angle * 0.2;
                self.angle2 -= delta_angle * 0.15;
                let xf0 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0));
                let xf1 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1));
                let xf2 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2));
                let mut txn = Transaction::new();
                txn.update_dynamic_properties(
                    DynamicProperties {
                        transforms: vec![
                            PropertyValue {
                                key: self.property_key0,
                                value: xf0,
                            },
                            PropertyValue {
                                key: self.property_key1,
                                value: xf1,
                            },
                            PropertyValue {
                                key: self.property_key2,
                                value: xf2,
                            },
                        ],
                        floats: vec![
                            PropertyValue {
                                key: self.opacity_key,
                                value: self.opacity,
                            }
                        ],
                    },
                );
                txn.generate_frame();
                api.send_transaction(document_id, txn);
            }
            _ => (),
        }

        false
    }
}

fn main() {
    let mut app = App {
        property_key0: PropertyBindingKey::new(42), // arbitrary magic number
        property_key1: PropertyBindingKey::new(44), // arbitrary magic number
        property_key2: PropertyBindingKey::new(45), // arbitrary magic number
        opacity_key: PropertyBindingKey::new(43),
        opacity: 0.5,
        angle0: 0.0,
        angle1: 0.0,
        angle2: 0.0,
    };
    boilerplate::main_wrapper(&mut app, None);
}
<commit_msg>Add option to rebuild display list for picture cache debugging.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This example creates a 200x200 white rect and allows the user to move it
//! around by using the arrow keys and rotate with '<'/'>'.
//! It does this by using the animation API.

//! The example also features seamless opaque/transparent split of a
//! rounded cornered rectangle, which is done automatically during the
//! scene building for render optimization.

extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;

#[path = "common/boilerplate.rs"]
mod boilerplate;

use boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Angle;
use webrender::api::*;

struct App {
    property_key0: PropertyBindingKey<LayoutTransform>,
    property_key1: PropertyBindingKey<LayoutTransform>,
    property_key2: PropertyBindingKey<LayoutTransform>,
    opacity_key: PropertyBindingKey<f32>,
    opacity: f32,
    angle0: f32,
    angle1: f32,
    angle2: f32,
}

impl App {
    fn add_rounded_rect(
        &mut self,
        bounds: LayoutRect,
        color: ColorF,
        builder: &mut DisplayListBuilder,
        property_key: PropertyBindingKey<LayoutTransform>,
    ) {
        let filters = [
            FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
        ];

        let reference_frame_id = builder.push_reference_frame(
            &LayoutPrimitiveInfo::new(LayoutRect::new(bounds.origin, LayoutSize::zero())),
            TransformStyle::Flat,
            Some(PropertyBinding::Binding(property_key, LayoutTransform::identity())),
            None,
        );

        builder.push_clip_id(reference_frame_id);

        builder.push_stacking_context(
            &LayoutPrimitiveInfo::new(LayoutRect::zero()),
            None,
            TransformStyle::Flat,
            MixBlendMode::Normal,
            &filters,
            RasterSpace::Screen,
        );

        let clip_bounds = LayoutRect::new(LayoutPoint::zero(), bounds.size);
        let complex_clip = ComplexClipRegion {
            rect: clip_bounds,
            radii: BorderRadius::uniform(30.0),
            mode: ClipMode::Clip,
        };
        let clip_id = builder.define_clip(clip_bounds, vec![complex_clip], None);
        builder.push_clip_id(clip_id);

        // Fill it with a white rect
        builder.push_rect(
            &LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), bounds.size)),
            color,
        );

        builder.pop_clip_id();

        builder.pop_stacking_context();

        builder.pop_clip_id();
        builder.pop_reference_frame();
    }
}

impl Example for App {
    const WIDTH: u32 = 1024;
    const HEIGHT: u32 = 1024;

    fn render(
        &mut self,
        _api: &RenderApi,
        builder: &mut DisplayListBuilder,
        _txn: &mut Transaction,
        _framebuffer_size: DeviceIntSize,
        _pipeline_id: PipelineId,
        _document_id: DocumentId,
    ) {
        let bounds = (150, 150).to(250, 250);
        let key0 = self.property_key0;
        self.add_rounded_rect(bounds, ColorF::new(1.0, 0.0, 0.0, 0.5), builder, key0);

        let bounds = (400, 400).to(600, 600);
        let key1 = self.property_key1;
        self.add_rounded_rect(bounds, ColorF::new(0.0, 1.0, 0.0, 0.5), builder, key1);

        let bounds = (200, 500).to(350, 580);
        let key2 = self.property_key2;
        self.add_rounded_rect(bounds, ColorF::new(0.0, 0.0, 1.0, 0.5), builder, key2);
    }

    fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
        let mut rebuild_display_list = false;

        match win_event {
            winit::WindowEvent::KeyboardInput {
                input: winit::KeyboardInput {
                    state: winit::ElementState::Pressed,
                    virtual_keycode: Some(key),
                    ..
                },
                ..
            } => {
                let (delta_angle, delta_opacity) = match key {
                    winit::VirtualKeyCode::Down => (0.0, -0.1),
                    winit::VirtualKeyCode::Up => (0.0, 0.1),
                    winit::VirtualKeyCode::Right => (1.0, 0.0),
                    winit::VirtualKeyCode::Left => (-1.0, 0.0),
                    winit::VirtualKeyCode::R => {
                        rebuild_display_list = true;
                        (0.0, 0.0)
                    }
                    _ => return false,
                };
                // Update the transform based on the keyboard input and push it to
                // webrender using the generate_frame API. This will recomposite with
                // the updated transform.
                self.opacity += delta_opacity;
                self.angle0 += delta_angle * 0.1;
                self.angle1 += delta_angle * 0.2;
                self.angle2 -= delta_angle * 0.15;
                let xf0 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0));
                let xf1 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1));
                let xf2 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2));
                let mut txn = Transaction::new();
                txn.update_dynamic_properties(
                    DynamicProperties {
                        transforms: vec![
                            PropertyValue {
                                key: self.property_key0,
                                value: xf0,
                            },
                            PropertyValue {
                                key: self.property_key1,
                                value: xf1,
                            },
                            PropertyValue {
                                key: self.property_key2,
                                value: xf2,
                            },
                        ],
                        floats: vec![
                            PropertyValue {
                                key: self.opacity_key,
                                value: self.opacity,
                            }
                        ],
                    },
                );
                txn.generate_frame();
                api.send_transaction(document_id, txn);
            }
            _ => (),
        }

        rebuild_display_list
    }
}

fn main() {
    let mut app = App {
        property_key0: PropertyBindingKey::new(42), // arbitrary magic number
        property_key1: PropertyBindingKey::new(44), // arbitrary magic number
        property_key2: PropertyBindingKey::new(45), // arbitrary magic number
        opacity_key: PropertyBindingKey::new(43),
        opacity: 0.5,
        angle0: 0.0,
        angle1: 0.0,
        angle2: 0.0,
    };
    boilerplate::main_wrapper(&mut app, None);
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate md5;

use std::collections::HashMap;
use std::io::Write;

use super::bucket::Bucket;
use chrono::{DateTime, Utc};
use super::command::Command;
use hmac::Mac;
use reqwest::{Client, Response};
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue};
use sha2::{Digest, Sha256};
use url::Url;

use crate::signing;

use crate::{S3Error, Result};
use crate::EMPTY_PAYLOAD_SHA;
use crate::LONG_DATE;

/// Collection of HTTP headers sent to S3 service, in key/value format.
pub type Headers = HashMap<String, String>;

/// Collection of HTTP query parameters sent to S3 service, in key/value
/// format.
pub type Query = HashMap<String, String>;

// Temporary structure for making a request
pub struct Request<'a> {
    pub bucket: &'a Bucket,
    pub path: &'a str,
    pub command: Command<'a>,
    pub datetime: DateTime<Utc>,
    pub sync: bool,
}

impl<'a> Request<'a> {
    pub fn new<'b>(bucket: &'b Bucket, path: &'b str, command: Command<'b>) -> Request<'b> {
        Request {
            bucket,
            path,
            command,
            datetime: Utc::now(),
            sync: false,
        }
    }

    fn url(&self) -> Url {
        let mut url_str = format!("{}://{}", self.bucket.scheme(), self.bucket.self_host());

        if !self.path.starts_with('/') {
            url_str.push_str("/");
        }

        url_str.push_str(self.path);

        // Since every part of this URL is either pre-encoded or statically
        // generated, there's really no way this should fail.
        let mut url = Url::parse(&url_str).expect("static URL parsing");

        for (key, value) in &self.bucket.extra_query {
            url.query_pairs_mut().append_pair(key, value);
        }

        if let Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        } = self.command.clone()
        {
            let mut query_pairs = url.query_pairs_mut();
            delimiter.map(|d| query_pairs.append_pair("delimiter", &d));
            query_pairs.append_pair("prefix", &prefix);
            query_pairs.append_pair("list-type", "2");
            if let Some(token) = continuation_token {
                query_pairs.append_pair("continuation-token", &token);
            }
        }

        match self.command {
            Command::PutObjectTagging { .. }
            | Command::GetObjectTagging
            | Command::DeleteObjectTagging => {
                url.query_pairs_mut().append_pair("tagging", "");
            }
            _ => {}
        }

        //        println!("{}", url);
        url
    }

    fn content_length(&self) -> usize {
        match self.command {
            Command::PutObject { content, .. } => content.len(),
            Command::PutObjectTagging { tags } => tags.len(),
            _ => 0,
        }
    }

    fn content_type(&self) -> String {
        match self.command {
            Command::PutObject { content_type, .. } => content_type.into(),
            _ => "text/plain".into(),
        }
    }

    fn sha256(&self) -> String {
        match self.command {
            Command::PutObject { content, .. } => {
                let mut sha = Sha256::default();
                sha.input(content);
                hex::encode(sha.result().as_slice())
            }
            Command::PutObjectTagging { tags } => {
                let mut sha = Sha256::default();
                sha.input(tags.as_bytes());
                hex::encode(sha.result().as_slice())
            }
            _ => EMPTY_PAYLOAD_SHA.into(),
        }
    }

    fn long_date(&self) -> String {
        self.datetime.format(LONG_DATE).to_string()
    }

    fn canonical_request(&self, headers: &HeaderMap) -> String {
        signing::canonical_request(
            self.command.http_verb().as_str(),
            &self.url(),
            headers,
            &self.sha256(),
        )
    }

    fn string_to_sign(&self, request: &str) -> String {
        signing::string_to_sign(&self.datetime, &self.bucket.region(), request)
    }

    fn signing_key(&self) -> Result<Vec<u8>> {
        Ok(signing::signing_key(
            &self.datetime,
            &self.bucket.secret_key(),
            &self.bucket.region(),
            "s3",
        )?)
    }

    fn authorization(&self, headers: &HeaderMap) -> Result<String> {
        let canonical_request = self.canonical_request(headers);
        let string_to_sign = self.string_to_sign(&canonical_request);
        let mut hmac = signing::HmacSha256::new_varkey(&self.signing_key()?)?;
        hmac.input(string_to_sign.as_bytes());
        let signature = hex::encode(hmac.result().code());
        let signed_header = signing::signed_header_string(headers);
        Ok(signing::authorization_header(
            &self.bucket.access_key(),
            &self.datetime,
            &self.bucket.region(),
            &signed_header,
            &signature,
        ))
    }

    fn headers(&self) -> Result<HeaderMap> {
        // Generate this once, but it's used in more than one place.
        let sha256 = self.sha256();

        // Start with extra_headers, that way our headers replace anything with
        // the same name.
        let mut headers = self
            .bucket
            .extra_headers
            .iter()
            .map(|(k, v)| Ok((k.parse::<HeaderName>()?, v.parse::<HeaderValue>()?)))
            .collect::<Result<HeaderMap, S3Error>>()?;
        headers.insert(header::HOST, self.bucket.self_host().parse()?);
        match self.command {
            Command::ListBucket { .. } => {},
            Command::GetObject => {},
            Command::GetObjectTagging => {},
            Command::GetBucketLocation => {},
            _ => {
                headers.insert(
                    header::CONTENT_LENGTH,
                    self.content_length().to_string().parse()?,
                );
                headers.insert(header::CONTENT_TYPE, self.content_type().parse()?);
            }
        }
        headers.insert("X-Amz-Content-Sha256", sha256.parse()?);
        headers.insert("X-Amz-Date", self.long_date().parse()?);

        if let Some(session_token) = self.bucket.session_token() {
            headers.insert("X-Amz-Security-Token", session_token.parse()?);
        } else if let Some(security_token) = self.bucket.security_token() {
            headers.insert("X-Amz-Security-Token", security_token.parse()?);
        }

        if let Command::PutObjectTagging { tags } = self.command {
            let digest = md5::compute(tags);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::PutObject { content, .. } = self.command {
            let digest = md5::compute(content);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::GetObject {} = self.command {
            headers.insert(
                header::ACCEPT,
                HeaderValue::from_str("application/octet-stream")?,
            );
            // headers.insert(header::ACCEPT_CHARSET, HeaderValue::from_str("UTF-8")?);
        }

        // This must be last, as it signs the other headers
        let authorization = self.authorization(&headers)?;
        headers.insert(header::AUTHORIZATION, authorization.parse()?);

        // The format of RFC2822 is somewhat malleable, so including it in
        // signed headers can cause signature mismatches. We do include the
        // X-Amz-Date header, so requests are still properly limited to a date
        // range and can't be used again e.g. reply attacks. Adding this header
        // after the generation of the Authorization header leaves it out of
        // the signed headers.
        headers.insert(header::DATE, self.datetime.to_rfc2822().parse()?);

        Ok(headers)
    }

    // pub fn response_data(&self) -> Result<(Vec<u8>, u16)> {
    //     Ok(futures::executor::block_on(self.response_data_future())?)
    // }

    // pub fn response_data_to_writer<T: Write>(&self, writer: &mut T) -> Result<u16> {
    //     Ok(futures::executor::block_on(self.response_data_to_writer_future(writer))?)
    // }

    pub async fn response_future(&self) -> Result<Response> {
        let client = if cfg!(feature = "no-verify-ssl") {
            Client::builder()
                .danger_accept_invalid_certs(true)
                .danger_accept_invalid_hostnames(true)
                .build()
                .expect("Could not build dangerous client!")
        } else {
            Client::new()
        };

        // Build headers
        let headers = self.headers().expect("Could not get headers!");

        // Get owned content to pass to reqwest
        let content = if let Command::PutObject { content, .. } = self.command {
            Vec::from(content)
        } else if let Command::PutObjectTagging { tags } = self.command {
            Vec::from(tags)
        } else {
            Vec::new()
        };

        println!("{}", self.url());

        let request = client
            .request(self.command.http_verb(), self.url().as_str())
            .headers(headers.to_owned())
            .body(content.to_owned());

        Ok(request.send().await?)
    }

    pub async fn response_data_future(&self) -> Result<(Vec<u8>, u16)> {
        let response = self.response_future().await?;
        let status_code = response.status().as_u16();
        let body = response.bytes().await?;
        let mut body_vec = Vec::new();
        body_vec.extend_from_slice(&body[..]);
        Ok((body_vec, status_code))
    }

    pub async fn response_data_to_writer_future<'b, T: Write>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let (body, status_code) = self.response_data_future().await?;
        writer.write_all(&body[..]).expect("Could not write to writer");
        Ok(status_code)
    }
}

#[cfg(test)]
mod tests {
    use crate::bucket::Bucket;
    use crate::command::Command;
    use awscreds::Credentials;
    use crate::Result;
    use crate::request::Request;

    // Fake keys - otherwise using Credentials::default will use actual user
    // credentials if they exist.
    fn fake_credentials() -> Credentials {
        const ACCESS_KEY: &'static str = "AKIAIOSFODNN7EXAMPLE";
        const SECRET_KEY: &'static str = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
        Credentials::new_blocking(Some(ACCESS_KEY.into()), Some(SECRET_KEY.into()), None, None, None).unwrap()
    }

    #[test]
    fn url_uses_https_by_default() -> Result<()> {
        let region = "custom-region".parse()?;
        let bucket = Bucket::new("my-first-bucket", region, fake_credentials())?;
        let path = "/my-first/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "https");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        assert_eq!(*host, "my-first-bucket.custom-region".to_string());
        Ok(())
    }

    #[test]
    fn url_uses_scheme_from_custom_region_if_defined() -> Result<()> {
        let region = "http://custom-region".parse()?;
        let bucket = Bucket::new("my-second-bucket", region, fake_credentials())?;
        let path = "/my-second/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "http");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        assert_eq!(*host, "my-second-bucket.custom-region".to_string());
        Ok(())
    }
}
<commit_msg>Remove debug println!<commit_after>extern crate base64;
extern crate md5;

use std::collections::HashMap;
use std::io::Write;

use super::bucket::Bucket;
use chrono::{DateTime, Utc};
use super::command::Command;
use hmac::Mac;
use reqwest::{Client, Response};
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue};
use sha2::{Digest, Sha256};
use url::Url;

use crate::signing;

use crate::{S3Error, Result};
use crate::EMPTY_PAYLOAD_SHA;
use crate::LONG_DATE;

/// Collection of HTTP headers sent to S3 service, in key/value format.
pub type Headers = HashMap<String, String>;

/// Collection of HTTP query parameters sent to S3 service, in key/value
/// format.
pub type Query = HashMap<String, String>;

// Temporary structure for making a request
pub struct Request<'a> {
    pub bucket: &'a Bucket,
    pub path: &'a str,
    pub command: Command<'a>,
    pub datetime: DateTime<Utc>,
    pub sync: bool,
}

impl<'a> Request<'a> {
    pub fn new<'b>(bucket: &'b Bucket, path: &'b str, command: Command<'b>) -> Request<'b> {
        Request {
            bucket,
            path,
            command,
            datetime: Utc::now(),
            sync: false,
        }
    }

    fn url(&self) -> Url {
        let mut url_str = format!("{}://{}", self.bucket.scheme(), self.bucket.self_host());

        if !self.path.starts_with('/') {
            url_str.push_str("/");
        }

        url_str.push_str(self.path);

        // Since every part of this URL is either pre-encoded or statically
        // generated, there's really no way this should fail.
        let mut url = Url::parse(&url_str).expect("static URL parsing");

        for (key, value) in &self.bucket.extra_query {
            url.query_pairs_mut().append_pair(key, value);
        }

        if let Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        } = self.command.clone()
        {
            let mut query_pairs = url.query_pairs_mut();
            delimiter.map(|d| query_pairs.append_pair("delimiter", &d));
            query_pairs.append_pair("prefix", &prefix);
            query_pairs.append_pair("list-type", "2");
            if let Some(token) = continuation_token {
                query_pairs.append_pair("continuation-token", &token);
            }
        }

        match self.command {
            Command::PutObjectTagging { .. }
            | Command::GetObjectTagging
            | Command::DeleteObjectTagging => {
                url.query_pairs_mut().append_pair("tagging", "");
            }
            _ => {}
        }

        url
    }

    fn content_length(&self) -> usize {
        match self.command {
            Command::PutObject { content, .. } => content.len(),
            Command::PutObjectTagging { tags } => tags.len(),
            _ => 0,
        }
    }

    fn content_type(&self) -> String {
        match self.command {
            Command::PutObject { content_type, .. } => content_type.into(),
            _ => "text/plain".into(),
        }
    }

    fn sha256(&self) -> String {
        match self.command {
            Command::PutObject { content, .. } => {
                let mut sha = Sha256::default();
                sha.input(content);
                hex::encode(sha.result().as_slice())
            }
            Command::PutObjectTagging { tags } => {
                let mut sha = Sha256::default();
                sha.input(tags.as_bytes());
                hex::encode(sha.result().as_slice())
            }
            _ => EMPTY_PAYLOAD_SHA.into(),
        }
    }

    fn long_date(&self) -> String {
        self.datetime.format(LONG_DATE).to_string()
    }

    fn canonical_request(&self, headers: &HeaderMap) -> String {
        signing::canonical_request(
            self.command.http_verb().as_str(),
            &self.url(),
            headers,
            &self.sha256(),
        )
    }

    fn string_to_sign(&self, request: &str) -> String {
        signing::string_to_sign(&self.datetime, &self.bucket.region(), request)
    }

    fn signing_key(&self) -> Result<Vec<u8>> {
        Ok(signing::signing_key(
            &self.datetime,
            &self.bucket.secret_key(),
            &self.bucket.region(),
            "s3",
        )?)
    }

    fn authorization(&self, headers: &HeaderMap) -> Result<String> {
        let canonical_request = self.canonical_request(headers);
        let string_to_sign = self.string_to_sign(&canonical_request);
        let mut hmac = signing::HmacSha256::new_varkey(&self.signing_key()?)?;
        hmac.input(string_to_sign.as_bytes());
        let signature = hex::encode(hmac.result().code());
        let signed_header = signing::signed_header_string(headers);
        Ok(signing::authorization_header(
            &self.bucket.access_key(),
            &self.datetime,
            &self.bucket.region(),
            &signed_header,
            &signature,
        ))
    }

    fn headers(&self) -> Result<HeaderMap> {
        // Generate this once, but it's used in more than one place.
        let sha256 = self.sha256();

        // Start with extra_headers, that way our headers replace anything with
        // the same name.
        let mut headers = self
            .bucket
            .extra_headers
            .iter()
            .map(|(k, v)| Ok((k.parse::<HeaderName>()?, v.parse::<HeaderValue>()?)))
            .collect::<Result<HeaderMap, S3Error>>()?;
        headers.insert(header::HOST, self.bucket.self_host().parse()?);
        match self.command {
            Command::ListBucket { .. } => {},
            Command::GetObject => {},
            Command::GetObjectTagging => {},
            Command::GetBucketLocation => {},
            _ => {
                headers.insert(
                    header::CONTENT_LENGTH,
                    self.content_length().to_string().parse()?,
                );
                headers.insert(header::CONTENT_TYPE, self.content_type().parse()?);
            }
        }
        headers.insert("X-Amz-Content-Sha256", sha256.parse()?);
        headers.insert("X-Amz-Date", self.long_date().parse()?);

        if let Some(session_token) = self.bucket.session_token() {
            headers.insert("X-Amz-Security-Token", session_token.parse()?);
        } else if let Some(security_token) = self.bucket.security_token() {
            headers.insert("X-Amz-Security-Token", security_token.parse()?);
        }

        if let Command::PutObjectTagging { tags } = self.command {
            let digest = md5::compute(tags);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::PutObject { content, .. } = self.command {
            let digest = md5::compute(content);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::GetObject {} = self.command {
            headers.insert(
                header::ACCEPT,
                HeaderValue::from_str("application/octet-stream")?,
            );
            // headers.insert(header::ACCEPT_CHARSET, HeaderValue::from_str("UTF-8")?);
        }

        // This must be last, as it signs the other headers
        let authorization = self.authorization(&headers)?;
        headers.insert(header::AUTHORIZATION, authorization.parse()?);

        // The format of RFC2822 is somewhat malleable, so including it in
        // signed headers can cause signature mismatches. We do include the
        // X-Amz-Date header, so requests are still properly limited to a date
        // range and can't be used again e.g. reply attacks. Adding this header
        // after the generation of the Authorization header leaves it out of
        // the signed headers.
        headers.insert(header::DATE, self.datetime.to_rfc2822().parse()?);

        Ok(headers)
    }

    // pub fn response_data(&self) -> Result<(Vec<u8>, u16)> {
    //     Ok(futures::executor::block_on(self.response_data_future())?)
    // }

    // pub fn response_data_to_writer<T: Write>(&self, writer: &mut T) -> Result<u16> {
    //     Ok(futures::executor::block_on(self.response_data_to_writer_future(writer))?)
    // }

    pub async fn response_future(&self) -> Result<Response> {
        let client = if cfg!(feature = "no-verify-ssl") {
            Client::builder()
                .danger_accept_invalid_certs(true)
                .danger_accept_invalid_hostnames(true)
                .build()
                .expect("Could not build dangerous client!")
        } else {
            Client::new()
        };

        // Build headers
        let headers = self.headers().expect("Could not get headers!");

        // Get owned content to pass to reqwest
        let content = if let Command::PutObject { content, .. } = self.command {
            Vec::from(content)
        } else if let Command::PutObjectTagging { tags } = self.command {
            Vec::from(tags)
        } else {
            Vec::new()
        };

        let request = client
            .request(self.command.http_verb(), self.url().as_str())
            .headers(headers.to_owned())
            .body(content.to_owned());

        Ok(request.send().await?)
    }

    pub async fn response_data_future(&self) -> Result<(Vec<u8>, u16)> {
        let response = self.response_future().await?;
        let status_code = response.status().as_u16();
        let body = response.bytes().await?;
        let mut body_vec = Vec::new();
        body_vec.extend_from_slice(&body[..]);
        Ok((body_vec, status_code))
    }

    pub async fn response_data_to_writer_future<'b, T: Write>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let (body, status_code) = self.response_data_future().await?;
        writer.write_all(&body[..]).expect("Could not write to writer");
        Ok(status_code)
    }
}

#[cfg(test)]
mod tests {
    use crate::bucket::Bucket;
    use crate::command::Command;
    use awscreds::Credentials;
    use crate::Result;
    use crate::request::Request;

    // Fake keys - otherwise using Credentials::default will use actual user
    // credentials if they exist.
    fn fake_credentials() -> Credentials {
        const ACCESS_KEY: &'static str = "AKIAIOSFODNN7EXAMPLE";
        const SECRET_KEY: &'static str = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
        Credentials::new_blocking(Some(ACCESS_KEY.into()), Some(SECRET_KEY.into()), None, None, None).unwrap()
    }

    #[test]
    fn url_uses_https_by_default() -> Result<()> {
        let region = "custom-region".parse()?;
        let bucket = Bucket::new("my-first-bucket", region, fake_credentials())?;
        let path = "/my-first/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "https");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        assert_eq!(*host, "my-first-bucket.custom-region".to_string());
        Ok(())
    }

    #[test]
    fn url_uses_scheme_from_custom_region_if_defined() -> Result<()> {
        let region = "http://custom-region".parse()?;
        let bucket = Bucket::new("my-second-bucket", region, fake_credentials())?;
        let path = "/my-second/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "http");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        assert_eq!(*host, "my-second-bucket.custom-region".to_string());
        Ok(())
    }
}
<|endoftext|>