<commit_before>pub use self::keys::Keys;
pub use self::mouse::Mouse;
pub use self::window::Window;

/// Tracked state related to the focused window.
pub mod window {
    use window;
    use math::Vector2;

    /// State of the window in focus.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct Window {
        /// ID of the window currently in focus.
        pub id: Option<window::Id>,
        /// The width of the focused window agnostic of DPI.
        ///
        /// This is equal to the pixel width divided by the hidpi_factor.
        pub width: f64,
        /// The height of the focused window agnostic of DPI.
        ///
        /// This is equal to the pixel height divided by the hidpi_factor.
        pub height: f64,
        /// The high "dots-per-inch" multiplier that describes the density of the screens pixels.
        pub hidpi_factor: f64,
    }

    impl Window {
        /// Initialise the window state.
        pub fn new() -> Self {
            Window {
                id: None,
                width: 0.0,
                height: 0.0,
                hidpi_factor: 1.0,
            }
        }

        /// Return the `width` and `height` as a `Vector2`.
        pub fn size(&self) -> Vector2<f64> {
            Vector2 { x: self.width, y: self.height }
        }
    }
}

/// Tracked state related to the keyboard.
pub mod keys {
    use event::{Key, ModifiersState};
    use std::collections::HashSet;
    use std::ops::Deref;

    /// The state of the keyboard.
    #[derive(Clone, Debug, Default)]
    pub struct Keys {
        /// The state of the modifier keys as last indicated by winit.
        pub mods: ModifiersState,
        /// The state of all keys as tracked via the nannou App event handling.
        pub down: Down,
    }

    /// The set of keys that are currently pressed.
    #[derive(Clone, Debug, Default)]
    pub struct Down {
        pub(crate) keys: HashSet<Key>,
    }

    impl Deref for Down {
        type Target = HashSet<Key>;
        fn deref(&self) -> &Self::Target {
            &self.keys
        }
    }
}

/// Tracked state related to the mouse.
pub mod mouse {
    use math::Point2;
    use std;
    use window;

    #[doc(inline)]
    pub use event::MouseButton as Button;

    /// The max total number of buttons on a mouse.
    pub const NUM_BUTTONS: usize = 9;

    /// The state of the `Mouse` at a single moment in time.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct Mouse {
        /// The ID of the last window currently in focus.
        pub window: Option<window::Id>,
        /// *x* position relative to the middle of `window`.
        pub x: f64,
        /// *y* position relative to the middle of `window`.
        pub y: f64,
        /// A map describing the state of each mouse button.
        pub buttons: ButtonMap,
    }

    /// Whether the button is up or down.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum ButtonPosition {
        /// The button is up (i.e. pressed).
        Up,
        /// The button is down and was originally pressed down at the given `Point2`.
        Down(Point2<f64>),
    }

    /// Stores the state of all mouse buttons.
    ///
    /// If the mouse button is down, it stores the position of the mouse when the button was pressed
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct ButtonMap {
        buttons: [ButtonPosition; NUM_BUTTONS],
    }

    /// An iterator yielding all pressed buttons.
    #[derive(Clone)]
    pub struct PressedButtons<'a> {
        buttons: std::iter::Enumerate<std::slice::Iter<'a, ButtonPosition>>,
    }

    impl Mouse {
        /// Construct a new default `Mouse`.
        pub fn new() -> Self {
            Mouse {
                window: None,
                buttons: ButtonMap::new(),
                x: 0.0,
                y: 0.0,
            }
        }

        /// The position of the mouse relative to the middle of the window in focus..
        pub fn position(&self) -> Point2<f64> {
            Point2 { x: self.x, y: self.y }
        }
    }

    impl ButtonPosition {
        /// If the mouse button is down, return a new one with position relative to the given `xy`.
        pub fn relative_to(self, xy: Point2<f64>) -> Self {
            match self {
                ButtonPosition::Down(pos) => {
                    let rel_p = pos - xy;
                    ButtonPosition::Down(Point2 { x: rel_p.x, y: rel_p.y })
                }
                button_pos => button_pos,
            }
        }

        /// Is the `ButtonPosition` down.
        pub fn is_down(&self) -> bool {
            match *self {
                ButtonPosition::Down(_) => true,
                _ => false,
            }
        }

        /// Is the `ButtonPosition` up.
        pub fn is_up(&self) -> bool {
            match *self {
                ButtonPosition::Up => true,
                _ => false,
            }
        }

        /// Returns the position at which the button was pressed.
        pub fn if_down(&self) -> Option<Point2<f64>> {
            match *self {
                ButtonPosition::Down(xy) => Some((xy)),
                _ => None,
            }
        }
    }

    impl ButtonMap {
        /// Returns a new button map with all states set to `None`
        pub fn new() -> Self {
            ButtonMap { buttons: [ButtonPosition::Up; NUM_BUTTONS] }
        }

        /// Returns a copy of the ButtonMap relative to the given `Point`
        pub fn relative_to(self, xy: Point2<f64>) -> Self {
            self.buttons.iter().enumerate().fold(
                ButtonMap::new(),
                |mut map,
                 (idx, button_pos)| {
                    map.buttons[idx] = button_pos.relative_to(xy);
                    map
                },
            )
        }

        /// The state of the left mouse button.
        pub fn left(&self) -> &ButtonPosition {
            &self[Button::Left]
        }

        /// The state of the middle mouse button.
        pub fn middle(&self) -> &ButtonPosition {
            &self[Button::Middle]
        }

        /// The state of the right mouse button.
        pub fn right(&self) -> &ButtonPosition {
            &self[Button::Right]
        }

        /// Sets the `Button` in the `Down` position.
        pub fn press(&mut self, button: Button, xy: Point2<f64>) {
            self.buttons[button_to_idx(button)] = ButtonPosition::Down(xy);
        }

        /// Set's the `Button` in the `Up` position.
        pub fn release(&mut self, button: Button) {
            self.buttons[button_to_idx(button)] = ButtonPosition::Up;
        }

        /// An iterator yielding all pressed mouse buttons along with the location at which they
        /// were originally pressed.
        pub fn pressed(&self) -> PressedButtons {
            PressedButtons { buttons: self.buttons.iter().enumerate() }
        }
    }

    impl std::ops::Index<Button> for ButtonMap {
        type Output = ButtonPosition;
        fn index(&self, button: Button) -> &Self::Output {
            &self.buttons[button_to_idx(button)]
        }
    }

    impl<'a> Iterator for PressedButtons<'a> {
        type Item = (Button, Point2<f64>);
        fn next(&mut self) -> Option<Self::Item> {
            while let Some((idx, button_pos)) = self.buttons.next() {
                if let ButtonPosition::Down(xy) = *button_pos {
                    return Some((idx_to_button(idx), xy));
                }
            }
            None
        }
    }

    fn idx_to_button(i: usize) -> Button {
        match i {
            n @ 0...5 => Button::Other(n as u8),
            6 => Button::Left,
            7 => Button::Right,
            8 => Button::Middle,
            _ => Button::Other(std::u8::MAX),
        }
    }

    fn button_to_idx(button: Button) -> usize {
        match button {
            Button::Other(n) => n as usize,
            Button::Left => 6,
            Button::Right => 7,
            Button::Middle => 8,
        }
    }
}
<commit_msg>Add x_range, y_range and rect methods to<commit_after>pub use self::keys::Keys;
pub use self::mouse::Mouse;
pub use self::window::Window;

/// Tracked state related to the focused window.
pub mod window {
    use geom;
    use window;
    use math::Vector2;

    /// State of the window in focus.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct Window {
        /// ID of the window currently in focus.
        pub id: Option<window::Id>,
        /// The width of the focused window agnostic of DPI.
        ///
        /// This is equal to the pixel width divided by the hidpi_factor.
        pub width: f64,
        /// The height of the focused window agnostic of DPI.
        ///
        /// This is equal to the pixel height divided by the hidpi_factor.
        pub height: f64,
        /// The high "dots-per-inch" multiplier that describes the density of the screens pixels.
        pub hidpi_factor: f64,
    }

    impl Window {
        /// Initialise the window state.
        pub fn new() -> Self {
            Window {
                id: None,
                width: 0.0,
                height: 0.0,
                hidpi_factor: 1.0,
            }
        }

        /// Get the range along the *x* axis occupied by the window.
        pub fn x_range(&self) -> geom::Range<f64> {
            let half_w = self.width * 0.5;
            geom::Range { start: -half_w, end: half_w }
        }

        /// Get the range along the *y* axis occupied by the window.
        pub fn y_range(&self) -> geom::Range<f64> {
            let half_h = self.height * 0.5;
            geom::Range { start: -half_h, end: half_h }
        }

        /// Get the x coordinate for the left edge of the window.
        pub fn rect(&self) -> geom::Rect<f64> {
            let x = self.x_range();
            let y = self.y_range();
            geom::Rect { x, y }
        }

        /// Return the `width` and `height` as a `Vector2`.
        pub fn size(&self) -> Vector2<f64> {
            Vector2 { x: self.width, y: self.height }
        }
    }
}

/// Tracked state related to the keyboard.
pub mod keys {
    use event::{Key, ModifiersState};
    use std::collections::HashSet;
    use std::ops::Deref;

    /// The state of the keyboard.
    #[derive(Clone, Debug, Default)]
    pub struct Keys {
        /// The state of the modifier keys as last indicated by winit.
        pub mods: ModifiersState,
        /// The state of all keys as tracked via the nannou App event handling.
        pub down: Down,
    }

    /// The set of keys that are currently pressed.
    #[derive(Clone, Debug, Default)]
    pub struct Down {
        pub(crate) keys: HashSet<Key>,
    }

    impl Deref for Down {
        type Target = HashSet<Key>;
        fn deref(&self) -> &Self::Target {
            &self.keys
        }
    }
}

/// Tracked state related to the mouse.
pub mod mouse {
    use math::Point2;
    use std;
    use window;

    #[doc(inline)]
    pub use event::MouseButton as Button;

    /// The max total number of buttons on a mouse.
    pub const NUM_BUTTONS: usize = 9;

    /// The state of the `Mouse` at a single moment in time.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct Mouse {
        /// The ID of the last window currently in focus.
        pub window: Option<window::Id>,
        /// *x* position relative to the middle of `window`.
        pub x: f64,
        /// *y* position relative to the middle of `window`.
        pub y: f64,
        /// A map describing the state of each mouse button.
        pub buttons: ButtonMap,
    }

    /// Whether the button is up or down.
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub enum ButtonPosition {
        /// The button is up (i.e. pressed).
        Up,
        /// The button is down and was originally pressed down at the given `Point2`.
        Down(Point2<f64>),
    }

    /// Stores the state of all mouse buttons.
    ///
    /// If the mouse button is down, it stores the position of the mouse when the button was pressed
    #[derive(Copy, Clone, Debug, PartialEq)]
    pub struct ButtonMap {
        buttons: [ButtonPosition; NUM_BUTTONS],
    }

    /// An iterator yielding all pressed buttons.
    #[derive(Clone)]
    pub struct PressedButtons<'a> {
        buttons: std::iter::Enumerate<std::slice::Iter<'a, ButtonPosition>>,
    }

    impl Mouse {
        /// Construct a new default `Mouse`.
        pub fn new() -> Self {
            Mouse {
                window: None,
                buttons: ButtonMap::new(),
                x: 0.0,
                y: 0.0,
            }
        }

        /// The position of the mouse relative to the middle of the window in focus..
        pub fn position(&self) -> Point2<f64> {
            Point2 { x: self.x, y: self.y }
        }
    }

    impl ButtonPosition {
        /// If the mouse button is down, return a new one with position relative to the given `xy`.
        pub fn relative_to(self, xy: Point2<f64>) -> Self {
            match self {
                ButtonPosition::Down(pos) => {
                    let rel_p = pos - xy;
                    ButtonPosition::Down(Point2 { x: rel_p.x, y: rel_p.y })
                }
                button_pos => button_pos,
            }
        }

        /// Is the `ButtonPosition` down.
        pub fn is_down(&self) -> bool {
            match *self {
                ButtonPosition::Down(_) => true,
                _ => false,
            }
        }

        /// Is the `ButtonPosition` up.
        pub fn is_up(&self) -> bool {
            match *self {
                ButtonPosition::Up => true,
                _ => false,
            }
        }

        /// Returns the position at which the button was pressed.
        pub fn if_down(&self) -> Option<Point2<f64>> {
            match *self {
                ButtonPosition::Down(xy) => Some((xy)),
                _ => None,
            }
        }
    }

    impl ButtonMap {
        /// Returns a new button map with all states set to `None`
        pub fn new() -> Self {
            ButtonMap { buttons: [ButtonPosition::Up; NUM_BUTTONS] }
        }

        /// Returns a copy of the ButtonMap relative to the given `Point`
        pub fn relative_to(self, xy: Point2<f64>) -> Self {
            self.buttons.iter().enumerate().fold(
                ButtonMap::new(),
                |mut map,
                 (idx, button_pos)| {
                    map.buttons[idx] = button_pos.relative_to(xy);
                    map
                },
            )
        }

        /// The state of the left mouse button.
        pub fn left(&self) -> &ButtonPosition {
            &self[Button::Left]
        }

        /// The state of the middle mouse button.
        pub fn middle(&self) -> &ButtonPosition {
            &self[Button::Middle]
        }

        /// The state of the right mouse button.
        pub fn right(&self) -> &ButtonPosition {
            &self[Button::Right]
        }

        /// Sets the `Button` in the `Down` position.
        pub fn press(&mut self, button: Button, xy: Point2<f64>) {
            self.buttons[button_to_idx(button)] = ButtonPosition::Down(xy);
        }

        /// Set's the `Button` in the `Up` position.
        pub fn release(&mut self, button: Button) {
            self.buttons[button_to_idx(button)] = ButtonPosition::Up;
        }

        /// An iterator yielding all pressed mouse buttons along with the location at which they
        /// were originally pressed.
        pub fn pressed(&self) -> PressedButtons {
            PressedButtons { buttons: self.buttons.iter().enumerate() }
        }
    }

    impl std::ops::Index<Button> for ButtonMap {
        type Output = ButtonPosition;
        fn index(&self, button: Button) -> &Self::Output {
            &self.buttons[button_to_idx(button)]
        }
    }

    impl<'a> Iterator for PressedButtons<'a> {
        type Item = (Button, Point2<f64>);
        fn next(&mut self) -> Option<Self::Item> {
            while let Some((idx, button_pos)) = self.buttons.next() {
                if let ButtonPosition::Down(xy) = *button_pos {
                    return Some((idx_to_button(idx), xy));
                }
            }
            None
        }
    }

    fn idx_to_button(i: usize) -> Button {
        match i {
            n @ 0...5 => Button::Other(n as u8),
            6 => Button::Left,
            7 => Button::Right,
            8 => Button::Middle,
            _ => Button::Other(std::u8::MAX),
        }
    }

    fn button_to_idx(button: Button) -> usize {
        match button {
            Button::Other(n) => n as usize,
            Button::Left => 6,
            Button::Right => 7,
            Button::Middle => 8,
        }
    }
}
<|endoftext|>


<commit_before>extern crate stemmer;

use self::stemmer::Stemmer;

//#[derive(Debug)]
pub struct Stems<'a> {
    text: &'a str,
    // The current cursor position
    current: usize,
    stemmer: Stemmer,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub stemmed_offset: usize,
    // Where the suffix starts
    pub suffix_offset: usize,
    // The length of the suffix
    suffix_len: usize,
    pub stemmed: String,
    pub suffix: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            text: text,
            current: 0,
            stemmer: Stemmer::new("english").unwrap(),
        }
    }

    /// Return the *byte* length of the common prefix between two strings
    fn common_prefix_len(aa: &str, bb: &str) -> usize {
        let mut count = 0;
        for (charsa, charsb) in aa.chars().zip(bb.chars()) {
            if charsa != charsb {
                break;
            }
            count += charsa.len_utf8();
          }
        count
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        let text = &self.text[self.current..];
        let mut word_start = 0;
        let mut word_len = 0;
        for cc in text.chars() {
            // A character that is part of a word we want to stem
            if cc.is_alphabetic() {
                word_len += cc.len_utf8();
            }
            else {
                // There might be characters we don't care about for stemming
                // (whitespace and numbers) before the word
                if word_len == 0 {
                    word_start += cc.len_utf8();
                }
                // We've reached the end of the word
                else  {
                    break;
                }
            }
        }
        // No word found
        if word_len == 0 {
            None
        } else {
            // Do the actual stemming
            let input = text[word_start..word_start + word_len].to_lowercase();
            // The lowercase version might contain more bytes than the uppercase one:
            // e.g. İ => i̇
            let lowercase_len = input.len();
            let stemmed = self.stemmer.stem(&input);
            let prefix_len = Stems::common_prefix_len(&stemmed, &input);
            println!("{} {} {}", input, stemmed, prefix_len);

            let ret = StemmedWord {
                stemmed_offset: self.current + word_start,
                suffix_offset: self.current + word_start + prefix_len,
                suffix_len: lowercase_len - prefix_len,
                stemmed: stemmed,
                suffix: (&input[prefix_len..lowercase_len]).to_string(),
            };
            self.current += word_start + word_len;
            Some(ret)
        }
    }

}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_lowercase() {
        let input = "These words deeply test smoothly that stemming";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5, suffix_len: 0,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10, suffix_len: 1,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17, suffix_len: 1,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23, suffix_len: 0,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30, suffix_len: 2,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37, suffix_len: 0,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42, suffix_len: 4,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5, suffix_len: 0,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10, suffix_len: 1,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17, suffix_len: 1,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23, suffix_len: 0,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30, suffix_len: 2,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37, suffix_len: 0,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42, suffix_len: 4,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  1234 \t89";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "534   Let's seeing89";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 6, suffix_offset: 9, suffix_len: 0,
                          stemmed: String::from("let"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 10, suffix_offset: 11, suffix_len: 0,
                          stemmed: String::from("s"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 12, suffix_offset: 15, suffix_len: 3,
                          stemmed: String::from("see"), suffix: String::from("ing") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 8, suffix_len: 1,
                          stemmed: String::from("ünicöd"), suffix: String::from("e") },
            StemmedWord { stemmed_offset: 10, suffix_offset: 14, suffix_len: 4,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        println!("{} {} {} {}", input, input.to_lowercase(), input.len(), input.to_lowercase().len());
        let stems = Stems::new(input);
        for stemmed in stems {
            println!("stemmed: {:?}", stemmed);
        }
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 3, suffix_len: 0,
                          stemmed: String::from("i̇"), suffix: String::from("") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // NOTE vmx 2016-10-21: Those cases exist in theory, but (language independent) it doesn't
    // seem to be the case (after reading
    // ftp://ftp.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt ). Some characters don't have
    // a corresponding uppercase chararacter with the same byte size, but when those uppercase
    // letters are transformed, to lowercase, they are transformed to the same number of bytes
    // combining several characters (although there is a single character for it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    //#[test]
    //fn test_stems_unicode_lowercase_has_less_bytes() {
    //}

    #[test]
    fn test_common_prefix_len() {
        let tests = vec![
            ("a", "a", 1),
            ("ab", "a", 1),
            ("a", "ab", 1),
            ("ab", "ab", 2),
            ("a", "b", 0),
            ("b", "a", 0),
            ("ab", "cd", 0),
            ("ab", "bc", 0),
            ("abc", "abd", 2),
            ("ac", "abcd", 1),
            (" a", "a", 0),
            ("a", "a ", 1),
            ("xyzabc", "xyz", 3),
            ("xyz", "xyzabc", 3),
            ("öxyz", "öx", 3),
            ];
        for (aa, bb, expected) in tests {
            let prefix_len = Stems::common_prefix_len(aa, bb);
            assert_eq!(prefix_len, expected);
        }
    }
}
<commit_msg>Improved stemming<commit_after>extern crate stemmer;
extern crate unicode_normalization;
extern crate unicode_segmentation;

use self::stemmer::Stemmer;
use self::unicode_normalization::UnicodeNormalization;
use self::unicode_segmentation::UnicodeSegmentation;

pub struct Stems<'a> {
    words: unicode_segmentation::UnicodeWordIndices<'a>,
    // The current cursor position
    current: usize,
    stemmer: Stemmer,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub stemmed_offset: usize,
    // Where the suffix starts
    pub suffix_offset: usize,
    // The stemmed word
    pub stemmed: String,
    // The difference between the stemmed word and the original lowercased one. It can be
    // used to recontruct the original word (for exact match searches)
    pub suffix: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            words: text.unicode_word_indices(),
            current: 0,
            stemmer: Stemmer::new("english").unwrap(),
        }
    }

    /// Return the *byte* length of the common prefix between two strings
    fn common_prefix_len(aa: &str, bb: &str) -> usize {
        let mut count = 0;
        for (charsa, charsb) in aa.chars().zip(bb.chars()) {
            if charsa != charsb {
                break;
            }
            count += charsa.len_utf8();
          }
        count
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        match self.words.next() {
            Some((pos, word)) => {
                let lowercased = word.to_lowercase();
                let normalized = lowercased.nfkc().collect::<String>();
                let stemmed = self.stemmer.stem(&normalized);
                let prefix_len = Stems::common_prefix_len(&stemmed, &normalized);
                let ret = StemmedWord {
                    stemmed_offset: pos,
                    suffix_offset: pos + prefix_len,
                    stemmed: stemmed,
                    suffix: (&normalized[prefix_len..normalized.len()]).to_string(),
                };
                Some(ret)
            },
            None => None
        }
    }

}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_lowercase() {
        let input = "These words deeply test smoothly that stemming";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  @#$!== \t+-";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "@!?   Let's seeing...";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 6, suffix_offset: 9,
                          stemmed: String::from("let"), suffix: String::from("'s") },
            StemmedWord { stemmed_offset: 12, suffix_offset: 15,
                          stemmed: String::from("see"), suffix: String::from("ing") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 8,
                          stemmed: String::from("ünicöd"), suffix: String::from("e") },
            StemmedWord { stemmed_offset: 10, suffix_offset: 14,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        let stems = Stems::new(input);
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 3,
                          stemmed: String::from("i̇"), suffix: String::from("") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // Some characters don't have a corresponding uppercase chararacter with the same byte size,
    // but when those uppercase letters are transformed to lowercase, they are transformed to the
    // same number of bytes combining several characters (although there is a single character for
    // it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".lower()
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    #[test]
    fn test_stems_unicode_lowercase_has_less_bytes() {
        // The input is: Ρ̓ῤῤ (11 bytes), lowercases is ῤῤῤ (9 bytes)
        let input = "\u{03A1}\u{0313}\u{03C1}\u{0313}\u{1FE4}";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let stemmed = Stems::new(input);
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 9,
                          stemmed: String::from("\u{1FE4}\u{1FE4}\u{1FE4}"), suffix: String::from("") },
           ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_common_prefix_len() {
        let tests = vec![
            ("a", "a", 1),
            ("ab", "a", 1),
            ("a", "ab", 1),
            ("ab", "ab", 2),
            ("a", "b", 0),
            ("b", "a", 0),
            ("ab", "cd", 0),
            ("ab", "bc", 0),
            ("abc", "abd", 2),
            ("ac", "abcd", 1),
            (" a", "a", 0),
            ("a", "a ", 1),
            ("xyzabc", "xyz", 3),
            ("xyz", "xyzabc", 3),
            ("öxyz", "öx", 3),
            ];
        for (aa, bb, expected) in tests {
            let prefix_len = Stems::common_prefix_len(aa, bb);
            assert_eq!(prefix_len, expected);
        }
    }
}
<|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/. */

//! Generic type for CSS properties that are composed by two dimensions.

use cssparser::Parser;
use euclid::Size2D;
use parser::ParserContext;
use std::fmt;
use style_traits::{ToCss, ParseError};
use values::animated::ToAnimatedValue;

/// A generic size, for `border-*-radius` longhand properties, or
/// `border-spacing`.
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
#[derive(MallocSizeOf, PartialEq, ToComputedValue)]
pub struct Size<L>(pub Size2D<L>);

impl<L> Size<L> {
    #[inline]
    /// Create a new `Size` for an area of given width and height.
    pub fn new(width: L, height: L) -> Size<L> {
        Size(Size2D::new(width, height))
    }

    /// Returns the width component.
    pub fn width(&self) -> &L {
        &self.0.width
    }

    /// Returns the height component.
    pub fn height(&self) -> &L {
        &self.0.height
    }

    /// Parse a `Size` with a given parsing function.
    pub fn parse_with<'i, 't, F>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        parse_one: F,
    ) -> Result<Self, ParseError<'i>>
    where
        L: Clone,
        F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<L, ParseError<'i>>
    {
        let first = parse_one(context, input)?;
        let second = input
            .try(|i| parse_one(context, i))
            .unwrap_or_else(|_| first.clone());
        Ok(Self::new(first, second))
    }
}

impl<L: Clone> From<L> for Size<L> {
    fn from(size: L) -> Self {
        Self::new(size.clone(), size)
    }
}

impl<L> ToCss for Size<L>
where L:
    ToCss + PartialEq,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where W:
        fmt::Write
    {
        self.0.width.to_css(dest)?;

        if self.0.height != self.0.width {
            dest.write_str(" ")?;
            self.0.height.to_css(dest)?;
        }

        Ok(())
    }
}

impl<L> ToAnimatedValue for Size<L>
where L:
    ToAnimatedValue,
{
    type AnimatedValue = Size<L::AnimatedValue>;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        Size(Size2D::new(
            self.0.width.to_animated_value(),
            self.0.height.to_animated_value(),
        ))
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        Size(Size2D::new(
            L::from_animated_value(animated.0.width),
            L::from_animated_value(animated.0.height),
        ))
    }
}
<commit_msg>style: Remove unused From impl.<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/. */

//! Generic type for CSS properties that are composed by two dimensions.

use cssparser::Parser;
use euclid::Size2D;
use parser::ParserContext;
use std::fmt;
use style_traits::{ToCss, ParseError};
use values::animated::ToAnimatedValue;

/// A generic size, for `border-*-radius` longhand properties, or
/// `border-spacing`.
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
#[derive(MallocSizeOf, PartialEq, ToComputedValue)]
pub struct Size<L>(pub Size2D<L>);

impl<L> Size<L> {
    #[inline]
    /// Create a new `Size` for an area of given width and height.
    pub fn new(width: L, height: L) -> Size<L> {
        Size(Size2D::new(width, height))
    }

    /// Returns the width component.
    pub fn width(&self) -> &L {
        &self.0.width
    }

    /// Returns the height component.
    pub fn height(&self) -> &L {
        &self.0.height
    }

    /// Parse a `Size` with a given parsing function.
    pub fn parse_with<'i, 't, F>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
        parse_one: F,
    ) -> Result<Self, ParseError<'i>>
    where
        L: Clone,
        F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<L, ParseError<'i>>
    {
        let first = parse_one(context, input)?;
        let second = input
            .try(|i| parse_one(context, i))
            .unwrap_or_else(|_| first.clone());
        Ok(Self::new(first, second))
    }
}

impl<L> ToCss for Size<L>
where L:
    ToCss + PartialEq,
{
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
    where W:
        fmt::Write
    {
        self.0.width.to_css(dest)?;

        if self.0.height != self.0.width {
            dest.write_str(" ")?;
            self.0.height.to_css(dest)?;
        }

        Ok(())
    }
}

impl<L> ToAnimatedValue for Size<L>
where L:
    ToAnimatedValue,
{
    type AnimatedValue = Size<L::AnimatedValue>;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        Size(Size2D::new(
            self.0.width.to_animated_value(),
            self.0.height.to_animated_value(),
        ))
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        Size(Size2D::new(
            L::from_animated_value(animated.0.width),
            L::from_animated_value(animated.0.height),
        ))
    }
}
<|endoftext|>


<commit_before>use chrono::{DateTime, UTC};
use std::collections::BTreeMap;
use std::fmt;
use std::io;
use std::path::PathBuf;

pub type ToResult<T> = Result<T, ToError>;

// SEE: http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/
#[derive(Debug)]
pub enum ToError {
    Io(io::Error),
}

impl From<io::Error> for ToError {
    fn from(err: io::Error) -> ToError {
        ToError::Io(err)
    }
}

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
pub struct Bookmark {
    pub name: String,
    pub directory: PathBuf,
    // pub created_at: DateTime<UTC>,
    // pub updted_at: DateTime<UTC>,
    // pub last_accessed_at: DateTime<UTC>,
}

impl fmt::Debug for Bookmark {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Bookmark{{ name: {:?}, directory: {:?} }}", self.name, self.directory)
    }
}

impl Bookmark {
    pub fn new(name: String, directory: PathBuf) -> Bookmark {
        return Bookmark {
            name: name,
            directory: directory,
        };
    }
}

pub type Bookmarks = BTreeMap<String, Bookmark>;
<commit_msg>Derive debug trait for Bookmark.<commit_after>use chrono::{DateTime, UTC};
use std::collections::BTreeMap;
use std::fmt;
use std::io;
use std::path::PathBuf;

pub type ToResult<T> = Result<T, ToError>;

// SEE: http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/
#[derive(Debug)]
pub enum ToError {
    Io(io::Error),
}

impl From<io::Error> for ToError {
    fn from(err: io::Error) -> ToError {
        ToError::Io(err)
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable, PartialEq)]
pub struct Bookmark {
    pub name: String,
    pub directory: PathBuf,
    // pub created_at: DateTime<UTC>,
    // pub updted_at: DateTime<UTC>,
    // pub last_accessed_at: DateTime<UTC>,
}

impl Bookmark {
    pub fn new(name: String, directory: PathBuf) -> Bookmark {
        return Bookmark {
            name: name,
            directory: directory,
        };
    }
}

pub type Bookmarks = BTreeMap<String, Bookmark>;
<|endoftext|>


<commit_before>//! Globally-used traits, structs, and enums

use svd::types::SVDError;
use eigenvalues::types::EigenError;
use solve_linear::types::SolveError;
use least_squares::LeastSquaresError;
pub use lapack::{c32, c64};

/// Enum for symmetric matrix inputs.
#[repr(u8)]
pub enum Symmetric {
    /// Read elements from the upper-triangular portion of the matrix
    Upper = b'U',

    /// Read elements from the lower-triangular portion of the matrix
    Lower = b'L',
}

/// Universal `linxal` error enum
///
/// This enum can be used as a catch-all for errors from `linxal`
/// computations.
pub enum Error {
    SVD(SVDError),
    Eigen(EigenError),
    LeastSquares(LeastSquaresError),
    SolveLinear(SolveError),
}

impl From<SVDError> for Error {
    fn from(e: SVDError) -> Error {
        Error::SVD(e)
    }
}

impl From<EigenError> for Error {
    fn from(e: EigenError) -> Error {
        Error::Eigen(e)
    }
}

impl From<LeastSquaresError> for Error {
    fn from(e: LeastSquaresError) -> Error {
        Error::LeastSquares(e)
    }
}

impl From<SolveError> for Error {
    fn from(e: SolveError) -> Error {
        Error::SolveLinear(e)
    }
}

pub trait Magnitude: Copy {
    fn mag(self) -> f64;
}

impl Magnitude for f32 {
    fn mag(self) -> f64 {
        self.abs() as f64
    }
}

impl Magnitude for f64 {
    fn mag(self) -> f64 {
        self.abs()
    }
}

impl Magnitude for c32 {
    fn mag(self) -> f64 {
        self.norm() as f64
    }
}

impl Magnitude for c64 {
    fn mag(self) -> f64 {
        self.norm()
    }
}
<commit_msg>Add QRError to global Error enum<commit_after>//! Globally-used traits, structs, and enums

use svd::types::SVDError;
use eigenvalues::types::EigenError;
use solve_linear::types::SolveError;
use least_squares::LeastSquaresError;
use factorization::qr::QRError;
pub use lapack::{c32, c64};

/// Enum for symmetric matrix inputs.
#[repr(u8)]
pub enum Symmetric {
    /// Read elements from the upper-triangular portion of the matrix
    Upper = b'U',

    /// Read elements from the lower-triangular portion of the matrix
    Lower = b'L',
}

/// Universal `linxal` error enum
///
/// This enum can be used as a catch-all for errors from `linxal`
/// computations.
pub enum Error {
    SVD(SVDError),
    Eigen(EigenError),
    LeastSquares(LeastSquaresError),
    SolveLinear(SolveError),
    QR(QRError),
}

impl From<SVDError> for Error {
    fn from(e: SVDError) -> Error {
        Error::SVD(e)
    }
}

impl From<EigenError> for Error {
    fn from(e: EigenError) -> Error {
        Error::Eigen(e)
    }
}

impl From<LeastSquaresError> for Error {
    fn from(e: LeastSquaresError) -> Error {
        Error::LeastSquares(e)
    }
}

impl From<SolveError> for Error {
    fn from(e: SolveError) -> Error {
        Error::SolveLinear(e)
    }
}

impl From<QRError> for Error {
    fn from(e: QRError) -> Error {
        Error::QR(e)
    }
}

pub trait Magnitude: Copy {
    fn mag(self) -> f64;
}

impl Magnitude for f32 {
    fn mag(self) -> f64 {
        self.abs() as f64
    }
}

impl Magnitude for f64 {
    fn mag(self) -> f64 {
        self.abs()
    }
}

impl Magnitude for c32 {
    fn mag(self) -> f64 {
        self.norm() as f64
    }
}

impl Magnitude for c64 {
    fn mag(self) -> f64 {
        self.norm()
    }
}
<|endoftext|>


<commit_before>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, GlobBuilder, GlobSet, GlobSetBuilder};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.config"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("jinja", &["*.jinja", "*.jinja2"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("md", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("spark", &["*.spark"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<GlobSet>,
    negated: Option<GlobSet>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<GlobSet>,
        negated: Option<GlobSet>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

#[cfg(test)]
mod tests {
    use super::TypesBuilder;

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<commit_msg>Add .gradle as a groovy extension pattern<commit_after>/*!
The types module provides a way of associating glob patterns on file names to
file types.
*/

use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::path::Path;

use regex;

use gitignore::{Match, Pattern};
use globset::{self, GlobBuilder, GlobSet, GlobSetBuilder};

const TYPE_EXTENSIONS: &'static [(&'static str, &'static [&'static str])] = &[
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("awk", &["*.awk"]),
    ("c", &["*.c", "*.h", "*.H"]),
    ("cbor", &["*.cbor"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.config"]),
    ("cpp", &[
        "*.C", "*.cc", "*.cpp", "*.cxx",
        "*.h", "*.H", "*.hh", "*.hpp",
    ]),
    ("csharp", &["*.cs"]),
    ("css", &["*.css"]),
    ("cython", &["*.pyx"]),
    ("dart", &["*.dart"]),
    ("d", &["*.d"]),
    ("elisp", &["*.el"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("go", &["*.go"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("haskell", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html"]),
    ("java", &["*.java"]),
    ("jinja", &["*.jinja", "*.jinja2"]),
    ("js", &[
        "*.js", "*.jsx", "*.vue",
    ]),
    ("json", &["*.json"]),
    ("jsonl", &["*.jsonl"]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lua", &["*.lua"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk"]),
    ("markdown", &["*.md"]),
    ("md", &["*.md"]),
    ("matlab", &["*.m"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("nim", &["*.nim"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("py", &["*.py"]),
    ("readme", &["README*", "*README"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("rst", &["*.rst"]),
    ("ruby", &["*.rb"]),
    ("rust", &["*.rs"]),
    ("scala", &["*.scala"]),
    ("sh", &["*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh"]),
    ("spark", &["*.spark"]),
    ("sql", &["*.sql"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("swift", &["*.swift"]),
    ("tex", &["*.tex", "*.cls", "*.sty"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("txt", &["*.txt"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vimscript", &["*.vim"]),
    ("xml", &["*.xml"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
];

/// Describes all the possible failure conditions for building a file type
/// matcher.
#[derive(Debug)]
pub enum Error {
    /// We tried to select (or negate) a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
    /// There was an error building the matcher (probably a bad glob).
    Glob(globset::Error),
    /// There was an error compiling a glob as a regex.
    Regex(regex::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
        }
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

/// A single file type definition.
#[derive(Clone, Debug)]
pub struct FileTypeDef {
    name: String,
    pats: Vec<String>,
}

impl FileTypeDef {
    /// Return the name of this file type.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Return the glob patterns used to recognize this file type.
    pub fn patterns(&self) -> &[String] {
        &self.pats
    }
}

/// Types is a file type matcher.
#[derive(Clone, Debug)]
pub struct Types {
    defs: Vec<FileTypeDef>,
    selected: Option<GlobSet>,
    negated: Option<GlobSet>,
    has_selected: bool,
    unmatched_pat: Pattern,
}

impl Types {
    /// Creates a new file type matcher from the given Gitignore matcher. If
    /// not Gitignore matcher is provided, then the file type matcher has no
    /// effect.
    ///
    /// If has_selected is true, then at least one file type was selected.
    /// Therefore, any non-matches should be ignored.
    fn new(
        selected: Option<GlobSet>,
        negated: Option<GlobSet>,
        has_selected: bool,
        defs: Vec<FileTypeDef>,
    ) -> Types {
        Types {
            defs: defs,
            selected: selected,
            negated: negated,
            has_selected: has_selected,
            unmatched_pat: Pattern {
                from: Path::new("<filetype>").to_path_buf(),
                original: "<N/A>".to_string(),
                pat: "<N/A>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Creates a new file type matcher that never matches.
    pub fn empty() -> Types {
        Types::new(None, None, false, vec![])
    }

    /// Returns a match for the given path against this file type matcher.
    ///
    /// The path is considered whitelisted if it matches a selected file type.
    /// The path is considered ignored if it matched a negated file type.
    /// If at least one file type is selected and path doesn't match, then
    /// the path is also considered ignored.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // If we don't have any matcher, then we can't do anything.
        if self.negated.is_none() && self.selected.is_none() {
            return Match::None;
        }
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy(),
            None if self.has_selected => {
                return Match::Ignored(&self.unmatched_pat);
            }
            None => {
                return Match::None;
            }
        };
        if self.negated.as_ref().map(|s| s.is_match(&*name)).unwrap_or(false) {
            return Match::Ignored(&self.unmatched_pat);
        }
        if self.selected.as_ref().map(|s|s.is_match(&*name)).unwrap_or(false) {
            return Match::Whitelist(&self.unmatched_pat);
        }
        if self.has_selected {
            Match::Ignored(&self.unmatched_pat)
        } else {
            Match::None
        }
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> &[FileTypeDef] {
        &self.defs
    }
}

/// TypesBuilder builds a type matcher from a set of file type definitions and
/// a set of file type selections.
pub struct TypesBuilder {
    types: HashMap<String, Vec<String>>,
    selected: Vec<String>,
    negated: Vec<String>,
}

impl TypesBuilder {
    /// Create a new builder for a file type matcher.
    pub fn new() -> TypesBuilder {
        TypesBuilder {
            types: HashMap::new(),
            selected: vec![],
            negated: vec![],
        }
    }

    /// Build the current set of file type definitions *and* selections into
    /// a file type matcher.
    pub fn build(&self) -> Result<Types, Error> {
        let selected_globs =
            if self.selected.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.selected {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        let negated_globs =
            if self.negated.is_empty() {
                None
            } else {
                let mut bset = GlobSetBuilder::new();
                for name in &self.negated {
                    let globs = match self.types.get(name) {
                        Some(globs) => globs,
                        None => {
                            let msg = name.to_string();
                            return Err(Error::UnrecognizedFileType(msg));
                        }
                    };
                    for glob in globs {
                        let pat = try!(
                            GlobBuilder::new(glob)
                                .literal_separator(true).build());
                        bset.add(pat);
                    }
                }
                Some(try!(bset.build()))
            };
        Ok(Types::new(
            selected_globs,
            negated_globs,
            !self.selected.is_empty(),
            self.definitions(),
        ))
    }

    /// Return the set of current file type definitions.
    pub fn definitions(&self) -> Vec<FileTypeDef> {
        let mut defs = vec![];
        for (ref name, ref pats) in &self.types {
            let mut pats = pats.to_vec();
            pats.sort();
            defs.push(FileTypeDef {
                name: name.to_string(),
                pats: pats,
            });
        }
        defs.sort_by(|def1, def2| def1.name().cmp(def2.name()));
        defs
    }

    /// Select the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are selected.
    pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.selected.push(name.to_string());
            }
        } else {
            self.selected.push(name.to_string());
        }
        self
    }

    /// Ignore the file type given by `name`.
    ///
    /// If `name` is `all`, then all file types are negated.
    pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
        if name == "all" {
            for name in self.types.keys() {
                self.negated.push(name.to_string());
            }
        } else {
            self.negated.push(name.to_string());
        }
        self
    }

    /// Clear any file type definitions for the type given.
    pub fn clear(&mut self, name: &str) -> &mut TypesBuilder {
        self.types.remove(name);
        self
    }

    /// Add a new file type definition. `name` can be arbitrary and `pat`
    /// should be a glob recognizing file paths belonging to the `name` type.
    pub fn add(&mut self, name: &str, pat: &str) -> &mut TypesBuilder {
        self.types.entry(name.to_string())
            .or_insert(vec![]).push(pat.to_string());
        self
    }

    /// Add a new file type definition specified in string form. The format
    /// is `name:glob`. Names may not include a colon.
    pub fn add_def(&mut self, def: &str) -> Result<(), Error> {
        let name: String = def.chars().take_while(|&c| c != ':').collect();
        let pat: String = def.chars().skip(name.chars().count() + 1).collect();
        if name.is_empty() || pat.is_empty() {
            return Err(Error::InvalidDefinition);
        }
        self.add(&name, &pat);
        Ok(())
    }

    /// Add a set of default file type definitions.
    pub fn add_defaults(&mut self) -> &mut TypesBuilder {
        for &(name, exts) in TYPE_EXTENSIONS {
            for ext in exts {
                self.add(name, ext);
            }
        }
        self
    }
}

#[cfg(test)]
mod tests {
    use super::TypesBuilder;

    macro_rules! matched {
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, true);
        };
        (not, $name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr) => {
            matched!($name, $types, $sel, $selnot, $path, false);
        };
        ($name:ident, $types:expr, $sel:expr, $selnot:expr,
         $path:expr, $matched:expr) => {
            #[test]
            fn $name() {
                let mut btypes = TypesBuilder::new();
                for tydef in $types {
                    btypes.add_def(tydef).unwrap();
                }
                for sel in $sel {
                    btypes.select(sel);
                }
                for selnot in $selnot {
                    btypes.negate(selnot);
                }
                let types = btypes.build().unwrap();
                let mat = types.matched($path, false);
                assert_eq!($matched, !mat.is_ignored());
            }
        };
    }

    fn types() -> Vec<&'static str> {
        vec![
            "html:*.html",
            "html:*.htm",
            "rust:*.rs",
            "js:*.js",
        ]
    }

    matched!(match1, types(), vec!["rust"], vec![], "lib.rs");
    matched!(match2, types(), vec!["html"], vec![], "index.html");
    matched!(match3, types(), vec!["html"], vec![], "index.htm");
    matched!(match4, types(), vec!["html", "rust"], vec![], "main.rs");
    matched!(match5, types(), vec![], vec![], "index.html");
    matched!(match6, types(), vec![], vec!["rust"], "index.html");

    matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
    matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
}
<|endoftext|>


<commit_before>use crate::error::{AuthErrorOr, Error};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents an access token returned by oauth2 servers. All access tokens are
/// Bearer tokens. Other types of tokens are not supported.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct AccessToken {
    value: String,
    expires_at: Option<DateTime<Utc>>,
}

impl AccessToken {
    /// A string representation of the access token.
    pub fn as_str(&self) -> &str {
        &self.value
    }

    /// The time the access token will expire, if any.
    pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
        self.expires_at
    }

    /// Determine if the access token is expired.
    /// This will report that the token is expired 1 minute prior to the
    /// expiration time to ensure that when the token is actually sent to the
    /// server it's still valid.
    pub fn is_expired(&self) -> bool {
        // Consider the token expired if it's within 1 minute of it's expiration
        // time.
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

impl AsRef<str> for AccessToken {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl From<TokenInfo> for AccessToken {
    fn from(value: TokenInfo) -> Self {
        AccessToken {
            value: value.access_token,
            expires_at: value.expires_at,
        }
    }
}

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expiry date.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub(crate) struct TokenInfo {
    /// used when authenticating calls to oauth2 enabled services.
    pub(crate) access_token: String,
    /// used to refresh an expired access_token.
    pub(crate) refresh_token: Option<String>,
    /// The time when the token expires.
    pub(crate) expires_at: Option<DateTime<Utc>>,
}

impl TokenInfo {
    pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
        #[derive(Deserialize)]
        struct RawToken {
            access_token: String,
            refresh_token: Option<String>,
            token_type: String,
            expires_in: Option<i64>,
        }

        let RawToken {
            access_token,
            refresh_token,
            token_type,
            expires_in,
        } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;

        if token_type.to_lowercase().as_str() != "bearer" {
            use std::io;
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    r#"unknown token type returned; expected "bearer" found {}"#,
                    token_type
                ),
            )
            .into());
        }

        let expires_at = expires_in
            .map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));

        Ok(TokenInfo {
            access_token,
            refresh_token,
            expires_at,
        })
    }

    /// Returns true if we are expired.
    pub fn is_expired(&self) -> bool {
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

/// Represents either 'installed' or 'web' applications in a json secrets file.
/// See `ConsoleApplicationSecret` for more information
#[derive(Deserialize, Serialize, Clone, Default, Debug)]
pub struct ApplicationSecret {
    /// The client ID.
    pub client_id: String,
    /// The client secret.
    pub client_secret: String,
    /// The token server endpoint URI.
    pub token_uri: String,
    /// The authorization server endpoint URI.
    pub auth_uri: String,
    /// The redirect uris.
    pub redirect_uris: Vec<String>,
    /// Name of the google project the credentials are associated with
    pub project_id: Option<String>,
    /// The service account email associated with the client.
    pub client_email: Option<String>,
    /// The URL of the public x509 certificate, used to verify the signature on JWTs, such
    /// as ID tokens, signed by the authentication provider.
    pub auth_provider_x509_cert_url: Option<String>,
    ///  The URL of the public x509 certificate, used to verify JWTs signed by the client.
    pub client_x509_cert_url: Option<String>,
}

/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct ConsoleApplicationSecret {
    /// web app secret
    pub web: Option<ApplicationSecret>,
    /// installed app secret
    pub installed: Option<ApplicationSecret>,
}

#[cfg(test)]
pub mod tests {
    use super::*;

    pub const SECRET: &'static str =
        "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\
         \"client_secret\":\"UqkDJd5RFwnHoiG5x5Rub8SI\",\"token_uri\":\"https://accounts.google.\
         com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:\
         oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\
         \"14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com\",\
         \"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}";

    #[test]
    fn console_secret() {
        use serde_json as json;
        match json::from_str::<ConsoleApplicationSecret>(SECRET) {
            Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
            Err(err) => panic!(err),
        }
    }
}
<commit_msg>Move to literal string in panic! macro<commit_after>use crate::error::{AuthErrorOr, Error};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents an access token returned by oauth2 servers. All access tokens are
/// Bearer tokens. Other types of tokens are not supported.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct AccessToken {
    value: String,
    expires_at: Option<DateTime<Utc>>,
}

impl AccessToken {
    /// A string representation of the access token.
    pub fn as_str(&self) -> &str {
        &self.value
    }

    /// The time the access token will expire, if any.
    pub fn expiration_time(&self) -> Option<DateTime<Utc>> {
        self.expires_at
    }

    /// Determine if the access token is expired.
    /// This will report that the token is expired 1 minute prior to the
    /// expiration time to ensure that when the token is actually sent to the
    /// server it's still valid.
    pub fn is_expired(&self) -> bool {
        // Consider the token expired if it's within 1 minute of it's expiration
        // time.
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

impl AsRef<str> for AccessToken {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl From<TokenInfo> for AccessToken {
    fn from(value: TokenInfo) -> Self {
        AccessToken {
            value: value.access_token,
            expires_at: value.expires_at,
        }
    }
}

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expiry date.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub(crate) struct TokenInfo {
    /// used when authenticating calls to oauth2 enabled services.
    pub(crate) access_token: String,
    /// used to refresh an expired access_token.
    pub(crate) refresh_token: Option<String>,
    /// The time when the token expires.
    pub(crate) expires_at: Option<DateTime<Utc>>,
}

impl TokenInfo {
    pub(crate) fn from_json(json_data: &[u8]) -> Result<TokenInfo, Error> {
        #[derive(Deserialize)]
        struct RawToken {
            access_token: String,
            refresh_token: Option<String>,
            token_type: String,
            expires_in: Option<i64>,
        }

        let RawToken {
            access_token,
            refresh_token,
            token_type,
            expires_in,
        } = serde_json::from_slice::<AuthErrorOr<RawToken>>(json_data)?.into_result()?;

        if token_type.to_lowercase().as_str() != "bearer" {
            use std::io;
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    r#"unknown token type returned; expected "bearer" found {}"#,
                    token_type
                ),
            )
            .into());
        }

        let expires_at = expires_in
            .map(|seconds_from_now| Utc::now() + chrono::Duration::seconds(seconds_from_now));

        Ok(TokenInfo {
            access_token,
            refresh_token,
            expires_at,
        })
    }

    /// Returns true if we are expired.
    pub fn is_expired(&self) -> bool {
        self.expires_at
            .map(|expiration_time| expiration_time - chrono::Duration::minutes(1) <= Utc::now())
            .unwrap_or(false)
    }
}

/// Represents either 'installed' or 'web' applications in a json secrets file.
/// See `ConsoleApplicationSecret` for more information
#[derive(Deserialize, Serialize, Clone, Default, Debug)]
pub struct ApplicationSecret {
    /// The client ID.
    pub client_id: String,
    /// The client secret.
    pub client_secret: String,
    /// The token server endpoint URI.
    pub token_uri: String,
    /// The authorization server endpoint URI.
    pub auth_uri: String,
    /// The redirect uris.
    pub redirect_uris: Vec<String>,
    /// Name of the google project the credentials are associated with
    pub project_id: Option<String>,
    /// The service account email associated with the client.
    pub client_email: Option<String>,
    /// The URL of the public x509 certificate, used to verify the signature on JWTs, such
    /// as ID tokens, signed by the authentication provider.
    pub auth_provider_x509_cert_url: Option<String>,
    ///  The URL of the public x509 certificate, used to verify JWTs signed by the client.
    pub client_x509_cert_url: Option<String>,
}

/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct ConsoleApplicationSecret {
    /// web app secret
    pub web: Option<ApplicationSecret>,
    /// installed app secret
    pub installed: Option<ApplicationSecret>,
}

#[cfg(test)]
pub mod tests {
    use super::*;

    pub const SECRET: &'static str =
        "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\
         \"client_secret\":\"UqkDJd5RFwnHoiG5x5Rub8SI\",\"token_uri\":\"https://accounts.google.\
         com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:\
         oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\
         \"14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com\",\
         \"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}";

    #[test]
    fn console_secret() {
        use serde_json as json;
        match json::from_str::<ConsoleApplicationSecret>(SECRET) {
            Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
            Err(err) => panic!("Encountered error parsing ConsoleApplicationSecret: {}", err),
        }
    }
}
<|endoftext|>


<commit_before>//! Type conversions for binding parameters and getting query results.

use super::{PreparedStatement, ResultRow};
use super::{SqliteResult, SQLITE_MISMATCH};
use super::{SQLITE_NULL};
use time;

pub trait ToSql {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()>;
}

/// A trait for result values from a query.
///
/// cf [sqlite3 result values][column].
///
/// *inspired by sfackler's FromSql (and some haskell bindings?)*
///
/// [column]: http://www.sqlite.org/c3ref/column_blob.html
///
///   - TODO: consider a `types` submodule
///   - TODO: many more implementors, including Option<T>
pub trait FromSql {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<Self>;
}

impl ToSql for i32 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_int(ix, *self)
    }
}

impl FromSql for i32 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<i32> { Ok(row.column_int(col)) }
}

impl ToSql for i64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_int64(ix, *self)
    }
}

impl FromSql for i64 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<i64> { Ok(row.column_int64(col)) }
}

impl ToSql for f64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_double(ix, *self)
    }
}

impl FromSql for f64 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<f64> { Ok(row.column_double(col)) }
}

impl<T: ToSql + Clone> ToSql for Option<T> {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        match (*self).clone() {
            Some(x) => x.to_sql(s, ix),
            None => s.bind_null(ix)
        }
    }
}

impl<T: FromSql + Clone> FromSql for Option<T> {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<Option<T>> {
        match row.column_type(col) {
            SQLITE_NULL => Ok(None),
            _ => FromSql::from_sql(row, col).map(|x| Some(x))
        }
    }
}

impl ToSql for String {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_text(ix, (*self).as_slice())
    }
}

impl FromSql for String {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<String> {
        Ok(row.column_text(col).unwrap_or("".to_string()))
    }
}

/// Format of sqlite date strings
///
/// From [Date And Time Functions][lang_datefunc]:
/// > The datetime() function returns "YYYY-MM-DD HH:MM:SS"
/// [lang_datefunc]: http://www.sqlite.org/lang_datefunc.html
pub static SQLITE_TIME_FMT: &'static str = "%F %T";

impl FromSql for time::Tm {
    /// TODO: propagate error message
    #[allow(unused_variable)]
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<time::Tm> {
        match row.column_text(col) {
            None => Err(SQLITE_MISMATCH),
            Some(txt) => match time::strptime(txt.as_slice(), SQLITE_TIME_FMT) {
                Ok(tm) => Ok(tm),
                Err(msg) => Err(SQLITE_MISMATCH)
            }
        }
    }
}

impl ToSql for time::Timespec {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_text(ix, time::at_utc(*self).strftime(SQLITE_TIME_FMT).as_slice())
    }
}

impl FromSql for time::Timespec {
    /// TODO: propagate error message
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<time::Timespec> {
        let tmo: SqliteResult<time::Tm> = FromSql::from_sql(row, col);
        tmo.map(|tm| tm.to_timespec())
    }
}

#[cfg(test)]
mod tests {
    use time::Tm;
    use super::super::{DatabaseConnection, SqliteResult};
    use super::super::{ResultRowAccess};

    #[test]
    fn get_tm() {
        fn go() -> SqliteResult<()> {
            let mut conn = try!(DatabaseConnection::in_memory()
                                .map_err(|(code, _msg)| code));
            let mut stmt = try!(
                conn.prepare("select datetime('2001-01-01', 'weekday 3', '3 hours')"));
            let mut results = stmt.execute();
            match results.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(
                        row.get::<uint, Tm>(0u),
                        Tm { tm_sec: 0,
                             tm_min: 0,
                             tm_hour: 3,
                             tm_mday: 3,
                             tm_mon: 0,
                             tm_year: 101,
                             tm_wday: 0,
                             tm_yday: 0,
                             tm_isdst: 0,
                             tm_gmtoff: 0,
                             tm_nsec: 0
                        });
                    Ok(())
                },
                None => fail!("no row"),
                Some(Err(oops)) =>  fail!("error: {}", oops)
            }
        }
        go().unwrap();
    }
}

// Local Variables:
// flycheck-rust-crate-root: "lib.rs"
// End:
<commit_msg>update for time::at_utc API change: handle Err()<commit_after>//! Type conversions for binding parameters and getting query results.

use super::{PreparedStatement, ResultRow};
use super::{SqliteResult, SQLITE_MISMATCH};
use super::{SQLITE_NULL};
use time;

pub trait ToSql {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()>;
}

/// A trait for result values from a query.
///
/// cf [sqlite3 result values][column].
///
/// *inspired by sfackler's FromSql (and some haskell bindings?)*
///
/// [column]: http://www.sqlite.org/c3ref/column_blob.html
///
///   - TODO: consider a `types` submodule
///   - TODO: many more implementors, including Option<T>
pub trait FromSql {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<Self>;
}

impl ToSql for i32 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_int(ix, *self)
    }
}

impl FromSql for i32 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<i32> { Ok(row.column_int(col)) }
}

impl ToSql for i64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_int64(ix, *self)
    }
}

impl FromSql for i64 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<i64> { Ok(row.column_int64(col)) }
}

impl ToSql for f64 {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_double(ix, *self)
    }
}

impl FromSql for f64 {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<f64> { Ok(row.column_double(col)) }
}

impl<T: ToSql + Clone> ToSql for Option<T> {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        match (*self).clone() {
            Some(x) => x.to_sql(s, ix),
            None => s.bind_null(ix)
        }
    }
}

impl<T: FromSql + Clone> FromSql for Option<T> {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<Option<T>> {
        match row.column_type(col) {
            SQLITE_NULL => Ok(None),
            _ => FromSql::from_sql(row, col).map(|x| Some(x))
        }
    }
}

impl ToSql for String {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        s.bind_text(ix, (*self).as_slice())
    }
}

impl FromSql for String {
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<String> {
        Ok(row.column_text(col).unwrap_or("".to_string()))
    }
}

/// Format of sqlite date strings
///
/// From [Date And Time Functions][lang_datefunc]:
/// > The datetime() function returns "YYYY-MM-DD HH:MM:SS"
/// [lang_datefunc]: http://www.sqlite.org/lang_datefunc.html
pub static SQLITE_TIME_FMT: &'static str = "%F %T";

impl FromSql for time::Tm {
    /// TODO: propagate error message
    #[allow(unused_variable)]
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<time::Tm> {
        match row.column_text(col) {
            None => Err(SQLITE_MISMATCH),
            Some(txt) => match time::strptime(txt.as_slice(), SQLITE_TIME_FMT) {
                Ok(tm) => Ok(tm),
                Err(msg) => Err(SQLITE_MISMATCH)
            }
        }
    }
}

impl ToSql for time::Timespec {
    fn to_sql(&self, s: &mut PreparedStatement, ix: uint) -> SqliteResult<()> {
        match time::at_utc(*self).strftime(SQLITE_TIME_FMT) {
            Ok(text) => s.bind_text(ix, text.as_slice()),
            Err(_oops) => Err(SQLITE_MISMATCH)
        }
    }
}

impl FromSql for time::Timespec {
    /// TODO: propagate error message
    fn from_sql(row: &mut ResultRow, col: uint) -> SqliteResult<time::Timespec> {
        let tmo: SqliteResult<time::Tm> = FromSql::from_sql(row, col);
        tmo.map(|tm| tm.to_timespec())
    }
}

#[cfg(test)]
mod tests {
    use time::Tm;
    use super::super::{DatabaseConnection, SqliteResult};
    use super::super::{ResultRowAccess};

    #[test]
    fn get_tm() {
        fn go() -> SqliteResult<()> {
            let mut conn = try!(DatabaseConnection::in_memory()
                                .map_err(|(code, _msg)| code));
            let mut stmt = try!(
                conn.prepare("select datetime('2001-01-01', 'weekday 3', '3 hours')"));
            let mut results = stmt.execute();
            match results.step() {
                Some(Ok(ref mut row)) => {
                    assert_eq!(
                        row.get::<uint, Tm>(0u),
                        Tm { tm_sec: 0,
                             tm_min: 0,
                             tm_hour: 3,
                             tm_mday: 3,
                             tm_mon: 0,
                             tm_year: 101,
                             tm_wday: 0,
                             tm_yday: 0,
                             tm_isdst: 0,
                             tm_gmtoff: 0,
                             tm_nsec: 0
                        });
                    Ok(())
                },
                None => fail!("no row"),
                Some(Err(oops)) =>  fail!("error: {}", oops)
            }
        }
        go().unwrap();
    }
}

// Local Variables:
// flycheck-rust-crate-root: "lib.rs"
// End:
<|endoftext|>