<commit_before>use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && (self.bytes[i] == b'%' || (!is_operator(self.bytes[i]) && isnt_space(self.bytes[i]))) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], isnt_space);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(8.9))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(2.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(4.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(15.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(5.0))), vt.next());
        assert_eq!(None, vt.next());
    }
}
<commit_msg>Don't add parens to units<commit_after>use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Option<ValuePart<'a>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && valid_unit_char(self.bytes[i]) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], valid_unit_char);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = ValuePart<'a>;

    fn next(&mut self) -> Option<ValuePart<'a>> {
        if self.offset < self.value_str.len() {
            return self.parse()
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn valid_unit_char(c: u8) -> bool {
    c == b'%' || (!is_space(c) && !is_operator(c))
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"bar"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$foo"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(ValuePart::String(Borrowed(&"foo"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$bar"))), vt.next());
        assert_eq!(Some(ValuePart::String(Borrowed(&"baz"))), vt.next());
        assert_eq!(Some(ValuePart::Variable(Borrowed(&"$quux"))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(8.9))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units_but_not_parens() {
        let mut vt = ValueTokenizer::new("(3px)");
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_percents_as_units() {
        let mut vt = ValueTokenizer::new("10px 8%");
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(10.0, Borrowed(&"px")))),
            vt.next()
        );
        assert_eq!(
            Some(ValuePart::Number(NumberValue::with_units(8.0, Borrowed(&"%")))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(6.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(75.2))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(2.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Plus)), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::LeftParen)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(4.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::RightParen)), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(15.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(3.0))), vt.next());
        assert_eq!(Some(ValuePart::Operator(Op::Slash)), vt.next());
        assert_eq!(Some(ValuePart::Number(NumberValue::from_scalar(5.0))), vt.next());
        assert_eq!(None, vt.next());
    }
}
<|endoftext|>


<commit_before>use Cursive;
use Printer;
use With;
use direction;
use event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent};
use std::any::Any;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::{ScrollBase, Selector, View};

/// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
    /// A single row, with a label and a view.
    Row(String, Box<View>),
    /// A delimiter between groups.
    Delimiter,
}

impl ListChild {
    fn label(&self) -> &str {
        match *self {
            ListChild::Row(ref label, _) => label,
            _ => "",
        }
    }

    fn view(&mut self) -> Option<&mut View> {
        match *self {
            ListChild::Row(_, ref mut view) => Some(view.as_mut()),
            _ => None,
        }
    }
}

/// Displays a scrollable list of elements.
pub struct ListView {
    children: Vec<ListChild>,
    scrollbase: ScrollBase,
    focus: usize,
    // This callback is called when the selection is changed.
    on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
    last_size: Vec2,
}

new_default!(ListView);

impl ListView {
    /// Creates a new, empty `ListView`.
    pub fn new() -> Self {
        ListView {
            children: Vec::new(),
            scrollbase: ScrollBase::new(),
            focus: 0,
            on_select: None,
            last_size: Vec2::zero(),
        }
    }

    /// Returns the number of children, including delimiters.
    pub fn len(&self) -> usize {
        self.children.len()
    }

    /// Returns `true` if this view contains no children.
    ///
    /// Returns `false` if at least a delimiter or a view is present.
    pub fn is_empty(&self) -> bool {
        self.children.is_empty()
    }

    /// Returns a reference to the children
    pub fn children(&self) -> &[ListChild] {
        &self.children[..]
    }

    /// Returns a reference to the child at the given position.
    pub fn get_row(&self, id: usize) -> &ListChild {
        &self.children[id]
    }

    /// Gives mutable access to the child at the given position.
    ///
    /// # Panics
    ///
    /// Panics if `id >= self.len()`.
    pub fn row_mut(&mut self, id: usize) -> &mut ListChild {
        &mut self.children[id]
    }

    /// Adds a view to the end of the list.
    pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
        view.take_focus(direction::Direction::none());
        self.children.push(ListChild::Row(
            label.to_string(),
            Box::new(view),
        ));
    }

    /// Removes all children from this view.
    pub fn clear(&mut self) {
        self.children.clear();
        self.focus = 0;
    }

    /// Adds a view to the end of the list.
    ///
    /// Chainable variant.
    pub fn child<V: View + 'static>(self, label: &str, view: V) -> Self {
        self.with(|s| s.add_child(label, view))
    }

    /// Adds a delimiter to the end of the list.
    pub fn add_delimiter(&mut self) {
        self.children.push(ListChild::Delimiter);
    }

    /// Adds a delimiter to the end of the list.
    ///
    /// Chainable variant.
    pub fn delimiter(self) -> Self {
        self.with(Self::add_delimiter)
    }

    /// Sets a callback to be used when an item is selected.
    pub fn set_on_select<F>(&mut self, cb: F)
    where
        F: Fn(&mut Cursive, &String) + 'static,
    {
        self.on_select = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// Chainable variant.
    pub fn on_select<F>(self, cb: F) -> Self
    where
        F: Fn(&mut Cursive, &String) + 'static,
    {
        self.with(|s| s.set_on_select(cb))
    }

    /// Returns the index of the currently focused item.
    ///
    /// Panics if the list is empty.
    pub fn focus(&self) -> usize {
        self.focus
    }

    fn iter_mut<'a>(
        &'a mut self, from_focus: bool, source: direction::Relative
    ) -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
        match source {
            direction::Relative::Front => {
                let start = if from_focus {
                    self.focus
                } else {
                    0
                };

                Box::new(self.children.iter_mut().enumerate().skip(start))
            }
            direction::Relative::Back => {
                let end = if from_focus {
                    self.focus + 1
                } else {
                    self.children.len()
                };
                Box::new(
                    self.children[..end]
                        .iter_mut()
                        .enumerate()
                        .rev(),
                )
            }
        }
    }

    fn move_focus(
        &mut self, n: usize, source: direction::Direction
    ) -> EventResult {
        let i = if let Some(i) = source
            .relative(direction::Orientation::Vertical)
            .and_then(|rel| {
                // The iterator starts at the focused element.
                // We don't want that one.
                self.iter_mut(true, rel)
                    .skip(1)
                    .filter_map(|p| try_focus(p, source))
                    .take(n)
                    .last()
            }) {
            i
        } else {
            return EventResult::Ignored;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(self.on_select.clone().map(|cb| {
            let i = self.focus();
            let focused_string = String::from(self.children[i].label());
            Callback::from_fn(move |s| cb(s, &focused_string))
        }))
    }

    fn labels_width(&self) -> usize {
        self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0)
    }

    fn check_focus_grab(&mut self, event: &Event) {
        if let Event::Mouse {
            offset,
            position,
            event,
        } = *event
        {
            if !event.grabs_focus() {
                return;
            }

            let position = match position.checked_sub(offset) {
                None => return,
                Some(pos) => pos,
            };

            if position.y > self.scrollbase.view_height {
                return;
            }

            // eprintln!("Rel pos: {:?}", position);

            // Now that we have a relative position, checks for buttons?
            let focus = position.y + self.scrollbase.start_line;
            if focus >= self.children.len() {
                return;
            }

            if let ListChild::Row(_, ref mut view) = self.children[focus] {
                if view.take_focus(direction::Direction::none()) {
                    self.focus = focus;
                }
            }
        }
    }
}

fn try_focus(
    (i, child): (usize, &mut ListChild), source: direction::Direction
) -> Option<usize> {
    match *child {
        ListChild::Delimiter => None,
        ListChild::Row(_, ref mut view) => if view.take_focus(source) {
            Some(i)
        } else {
            None
        },
    }
}

impl View for ListView {
    fn draw(&self, printer: &Printer) {
        if self.children.is_empty() {
            return;
        }

        let offset = self.labels_width() + 1;

        debug!("Offset: {}", offset);
        self.scrollbase
            .draw(printer, |printer, i| match self.children[i] {
                ListChild::Row(ref label, ref view) => {
                    printer.print((0, 0), label);
                    view.draw(&printer.offset((offset, 0), i == self.focus));
                }
                ListChild::Delimiter => (),
            });
    }

    fn required_size(&mut self, req: Vec2) -> Vec2 {
        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let view_size = self.children
            .iter_mut()
            .filter_map(ListChild::view)
            .map(|v| v.required_size(req).x)
            .max()
            .unwrap_or(0);

        if self.children.len() > req.y {
            // Include a scroll bar
            Vec2::new(label_width + 1 + view_size + 2, req.y)
        } else {
            Vec2::new(label_width + 1 + view_size, self.children.len())
        }
    }

    fn layout(&mut self, size: Vec2) {
        self.last_size = size;
        self.scrollbase
            .set_heights(size.y, self.children.len());

        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let spacing = 1;
        let scrollbar_width = if self.children.len() > size.y {
            2
        } else {
            0
        };

        let available = size.x
            .saturating_sub(label_width + spacing + scrollbar_width);

        debug!("Available: {}", available);

        for child in self.children
            .iter_mut()
            .filter_map(ListChild::view)
        {
            child.layout(Vec2::new(available, 1));
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.children.is_empty() {
            return EventResult::Ignored;
        }

        // First: some events can directly affect the ListView
        match event {
            Event::Mouse {
                event: MouseEvent::Press(MouseButton::Left),
                position,
                offset,
            } if position
                .checked_sub(offset)
                .map(|position| {
                    self.scrollbase
                        .start_drag(position, self.last_size.x)
                })
                .unwrap_or(false) =>
            {
                return EventResult::Consumed(None);
            }
            Event::Mouse {
                event: MouseEvent::Hold(MouseButton::Left),
                position,
                offset,
            } if self.scrollbase.is_dragging() =>
            {
                let position = position.saturating_sub(offset);
                self.scrollbase.drag(position);
                return EventResult::Consumed(None);
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                ..
            } if self.scrollbase.is_dragging() =>
            {
                self.scrollbase.release_grab();
                return EventResult::Consumed(None);
            }
            _ => (),
        }

        // Then: some events can move the focus around.
        self.check_focus_grab(&event);

        // Send the event to the focused child.
        let labels_width = self.labels_width();
        if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
            // If self.focus < self.scrollbase.start_line, it means the focus is not
            // in view. Something's fishy, so don't send the event.
            if let Some(y) = self.focus
                .checked_sub(self.scrollbase.start_line)
            {
                let offset = (labels_width + 1, y);
                let result = view.on_event(event.relativized(offset));
                if result.is_consumed() {
                    return result;
                }
            }
        }

        // If the child ignored this event, change the focus.
        match event {
            Event::Key(Key::Up) if self.focus > 0 => {
                self.move_focus(1, direction::Direction::down())
            }
            Event::Key(Key::Down) if self.focus + 1 < self.children.len() => {
                self.move_focus(1, direction::Direction::up())
            }
            Event::Key(Key::PageUp) => {
                self.move_focus(10, direction::Direction::down())
            }
            Event::Key(Key::PageDown) => {
                self.move_focus(10, direction::Direction::up())
            }
            Event::Key(Key::Home) | Event::Ctrl(Key::Home) => self.move_focus(
                usize::max_value(),
                direction::Direction::back(),
            ),
            Event::Key(Key::End) | Event::Ctrl(Key::End) => self.move_focus(
                usize::max_value(),
                direction::Direction::front(),
            ),
            Event::Key(Key::Tab) => {
                self.move_focus(1, direction::Direction::front())
            }
            Event::Shift(Key::Tab) => {
                self.move_focus(1, direction::Direction::back())
            }
            Event::Mouse {
                event: MouseEvent::WheelDown,
                ..
            } if self.scrollbase.can_scroll_down() =>
            {
                self.scrollbase.scroll_down(5);
                EventResult::Consumed(None)
            }
            Event::Mouse {
                event: MouseEvent::WheelUp,
                ..
            } if self.scrollbase.can_scroll_up() =>
            {
                self.scrollbase.scroll_up(5);
                EventResult::Consumed(None)
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, source: direction::Direction) -> bool {
        let rel = source.relative(direction::Orientation::Vertical);
        let i = if let Some(i) = self.iter_mut(
            rel.is_none(),
            rel.unwrap_or(direction::Relative::Front),
        ).filter_map(|p| try_focus(p, source))
            .next()
        {
            i
        } else {
            // No one wants to be in focus
            return false;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);
        true
    }

    fn call_on_any<'a>(
        &mut self, selector: &Selector,
        mut callback: Box<FnMut(&mut Any) + 'a>,
    ) {
        for view in self.children
            .iter_mut()
            .filter_map(ListChild::view)
        {
            view.call_on_any(selector, Box::new(|any| callback(any)));
        }
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        if let Some(i) = self.children
            .iter_mut()
            .enumerate()
            .filter_map(|(i, v)| v.view().map(|v| (i, v)))
            .filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
            .next()
        {
            self.focus = i;
            Ok(())
        } else {
            Err(())
        }
    }
}
<commit_msg>Implement ListView::important_area<commit_after>use Cursive;
use Printer;
use With;
use direction;
use event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent};
use rect::Rect;
use std::any::Any;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::{ScrollBase, Selector, View};

/// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
    /// A single row, with a label and a view.
    Row(String, Box<View>),
    /// A delimiter between groups.
    Delimiter,
}

impl ListChild {
    fn label(&self) -> &str {
        match *self {
            ListChild::Row(ref label, _) => label,
            _ => "",
        }
    }

    fn view(&mut self) -> Option<&mut View> {
        match *self {
            ListChild::Row(_, ref mut view) => Some(view.as_mut()),
            _ => None,
        }
    }
}

/// Displays a scrollable list of elements.
pub struct ListView {
    children: Vec<ListChild>,
    scrollbase: ScrollBase,
    focus: usize,
    // This callback is called when the selection is changed.
    on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
    last_size: Vec2,
}

new_default!(ListView);

impl ListView {
    /// Creates a new, empty `ListView`.
    pub fn new() -> Self {
        ListView {
            children: Vec::new(),
            scrollbase: ScrollBase::new(),
            focus: 0,
            on_select: None,
            last_size: Vec2::zero(),
        }
    }

    /// Returns the number of children, including delimiters.
    pub fn len(&self) -> usize {
        self.children.len()
    }

    /// Returns `true` if this view contains no children.
    ///
    /// Returns `false` if at least a delimiter or a view is present.
    pub fn is_empty(&self) -> bool {
        self.children.is_empty()
    }

    /// Returns a reference to the children
    pub fn children(&self) -> &[ListChild] {
        &self.children[..]
    }

    /// Returns a reference to the child at the given position.
    pub fn get_row(&self, id: usize) -> &ListChild {
        &self.children[id]
    }

    /// Gives mutable access to the child at the given position.
    ///
    /// # Panics
    ///
    /// Panics if `id >= self.len()`.
    pub fn row_mut(&mut self, id: usize) -> &mut ListChild {
        &mut self.children[id]
    }

    /// Adds a view to the end of the list.
    pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
        view.take_focus(direction::Direction::none());
        self.children.push(ListChild::Row(
            label.to_string(),
            Box::new(view),
        ));
    }

    /// Removes all children from this view.
    pub fn clear(&mut self) {
        self.children.clear();
        self.focus = 0;
    }

    /// Adds a view to the end of the list.
    ///
    /// Chainable variant.
    pub fn child<V: View + 'static>(self, label: &str, view: V) -> Self {
        self.with(|s| s.add_child(label, view))
    }

    /// Adds a delimiter to the end of the list.
    pub fn add_delimiter(&mut self) {
        self.children.push(ListChild::Delimiter);
    }

    /// Adds a delimiter to the end of the list.
    ///
    /// Chainable variant.
    pub fn delimiter(self) -> Self {
        self.with(Self::add_delimiter)
    }

    /// Sets a callback to be used when an item is selected.
    pub fn set_on_select<F>(&mut self, cb: F)
    where
        F: Fn(&mut Cursive, &String) + 'static,
    {
        self.on_select = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// Chainable variant.
    pub fn on_select<F>(self, cb: F) -> Self
    where
        F: Fn(&mut Cursive, &String) + 'static,
    {
        self.with(|s| s.set_on_select(cb))
    }

    /// Returns the index of the currently focused item.
    ///
    /// Panics if the list is empty.
    pub fn focus(&self) -> usize {
        self.focus
    }

    fn iter_mut<'a>(
        &'a mut self, from_focus: bool, source: direction::Relative
    ) -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
        match source {
            direction::Relative::Front => {
                let start = if from_focus {
                    self.focus
                } else {
                    0
                };

                Box::new(self.children.iter_mut().enumerate().skip(start))
            }
            direction::Relative::Back => {
                let end = if from_focus {
                    self.focus + 1
                } else {
                    self.children.len()
                };
                Box::new(
                    self.children[..end]
                        .iter_mut()
                        .enumerate()
                        .rev(),
                )
            }
        }
    }

    fn move_focus(
        &mut self, n: usize, source: direction::Direction
    ) -> EventResult {
        let i = if let Some(i) = source
            .relative(direction::Orientation::Vertical)
            .and_then(|rel| {
                // The iterator starts at the focused element.
                // We don't want that one.
                self.iter_mut(true, rel)
                    .skip(1)
                    .filter_map(|p| try_focus(p, source))
                    .take(n)
                    .last()
            }) {
            i
        } else {
            return EventResult::Ignored;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(self.on_select.clone().map(|cb| {
            let i = self.focus();
            let focused_string = String::from(self.children[i].label());
            Callback::from_fn(move |s| cb(s, &focused_string))
        }))
    }

    fn labels_width(&self) -> usize {
        self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0)
    }

    fn check_focus_grab(&mut self, event: &Event) {
        if let Event::Mouse {
            offset,
            position,
            event,
        } = *event
        {
            if !event.grabs_focus() {
                return;
            }

            let position = match position.checked_sub(offset) {
                None => return,
                Some(pos) => pos,
            };

            if position.y > self.scrollbase.view_height {
                return;
            }

            // eprintln!("Rel pos: {:?}", position);

            // Now that we have a relative position, checks for buttons?
            let focus = position.y + self.scrollbase.start_line;
            if focus >= self.children.len() {
                return;
            }

            if let ListChild::Row(_, ref mut view) = self.children[focus] {
                if view.take_focus(direction::Direction::none()) {
                    self.focus = focus;
                }
            }
        }
    }
}

fn try_focus(
    (i, child): (usize, &mut ListChild), source: direction::Direction
) -> Option<usize> {
    match *child {
        ListChild::Delimiter => None,
        ListChild::Row(_, ref mut view) => if view.take_focus(source) {
            Some(i)
        } else {
            None
        },
    }
}

impl View for ListView {
    fn draw(&self, printer: &Printer) {
        if self.children.is_empty() {
            return;
        }

        let offset = self.labels_width() + 1;

        debug!("Offset: {}", offset);
        self.scrollbase
            .draw(printer, |printer, i| match self.children[i] {
                ListChild::Row(ref label, ref view) => {
                    printer.print((0, 0), label);
                    view.draw(&printer.offset((offset, 0), i == self.focus));
                }
                ListChild::Delimiter => (),
            });
    }

    fn required_size(&mut self, req: Vec2) -> Vec2 {
        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let view_size = self.children
            .iter_mut()
            .filter_map(ListChild::view)
            .map(|v| v.required_size(req).x)
            .max()
            .unwrap_or(0);

        if self.children.len() > req.y {
            // Include a scroll bar
            Vec2::new(label_width + 1 + view_size + 2, req.y)
        } else {
            Vec2::new(label_width + 1 + view_size, self.children.len())
        }
    }

    fn layout(&mut self, size: Vec2) {
        self.last_size = size;
        self.scrollbase
            .set_heights(size.y, self.children.len());

        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let spacing = 1;
        let scrollbar_width = if self.children.len() > size.y {
            2
        } else {
            0
        };

        let available = size.x
            .saturating_sub(label_width + spacing + scrollbar_width);

        debug!("Available: {}", available);

        for child in self.children
            .iter_mut()
            .filter_map(ListChild::view)
        {
            child.layout(Vec2::new(available, 1));
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.children.is_empty() {
            return EventResult::Ignored;
        }

        // First: some events can directly affect the ListView
        match event {
            Event::Mouse {
                event: MouseEvent::Press(MouseButton::Left),
                position,
                offset,
            } if position
                .checked_sub(offset)
                .map(|position| {
                    self.scrollbase
                        .start_drag(position, self.last_size.x)
                })
                .unwrap_or(false) =>
            {
                return EventResult::Consumed(None);
            }
            Event::Mouse {
                event: MouseEvent::Hold(MouseButton::Left),
                position,
                offset,
            } if self.scrollbase.is_dragging() =>
            {
                let position = position.saturating_sub(offset);
                self.scrollbase.drag(position);
                return EventResult::Consumed(None);
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                ..
            } if self.scrollbase.is_dragging() =>
            {
                self.scrollbase.release_grab();
                return EventResult::Consumed(None);
            }
            _ => (),
        }

        // Then: some events can move the focus around.
        self.check_focus_grab(&event);

        // Send the event to the focused child.
        let labels_width = self.labels_width();
        if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
            // If self.focus < self.scrollbase.start_line, it means the focus is not
            // in view. Something's fishy, so don't send the event.
            if let Some(y) = self.focus
                .checked_sub(self.scrollbase.start_line)
            {
                let offset = (labels_width + 1, y);
                let result = view.on_event(event.relativized(offset));
                if result.is_consumed() {
                    return result;
                }
            }
        }

        // If the child ignored this event, change the focus.
        match event {
            Event::Key(Key::Up) if self.focus > 0 => {
                self.move_focus(1, direction::Direction::down())
            }
            Event::Key(Key::Down) if self.focus + 1 < self.children.len() => {
                self.move_focus(1, direction::Direction::up())
            }
            Event::Key(Key::PageUp) => {
                self.move_focus(10, direction::Direction::down())
            }
            Event::Key(Key::PageDown) => {
                self.move_focus(10, direction::Direction::up())
            }
            Event::Key(Key::Home) | Event::Ctrl(Key::Home) => self.move_focus(
                usize::max_value(),
                direction::Direction::back(),
            ),
            Event::Key(Key::End) | Event::Ctrl(Key::End) => self.move_focus(
                usize::max_value(),
                direction::Direction::front(),
            ),
            Event::Key(Key::Tab) => {
                self.move_focus(1, direction::Direction::front())
            }
            Event::Shift(Key::Tab) => {
                self.move_focus(1, direction::Direction::back())
            }
            Event::Mouse {
                event: MouseEvent::WheelDown,
                ..
            } if self.scrollbase.can_scroll_down() =>
            {
                self.scrollbase.scroll_down(5);
                EventResult::Consumed(None)
            }
            Event::Mouse {
                event: MouseEvent::WheelUp,
                ..
            } if self.scrollbase.can_scroll_up() =>
            {
                self.scrollbase.scroll_up(5);
                EventResult::Consumed(None)
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, source: direction::Direction) -> bool {
        let rel = source.relative(direction::Orientation::Vertical);
        let i = if let Some(i) = self.iter_mut(
            rel.is_none(),
            rel.unwrap_or(direction::Relative::Front),
        ).filter_map(|p| try_focus(p, source))
            .next()
        {
            i
        } else {
            // No one wants to be in focus
            return false;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);
        true
    }

    fn call_on_any<'a>(
        &mut self, selector: &Selector,
        mut callback: Box<FnMut(&mut Any) + 'a>,
    ) {
        for view in self.children
            .iter_mut()
            .filter_map(ListChild::view)
        {
            view.call_on_any(selector, Box::new(|any| callback(any)));
        }
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        if let Some(i) = self.children
            .iter_mut()
            .enumerate()
            .filter_map(|(i, v)| v.view().map(|v| (i, v)))
            .filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
            .next()
        {
            self.focus = i;
            Ok(())
        } else {
            Err(())
        }
    }

    fn important_area(&self, size: Vec2) -> Rect {
        if self.children.is_empty() {
            return Rect::from((0, 0));
        }

        let labels_width = self.labels_width();

        let area = match self.children[self.focus] {
            ListChild::Row(_, ref view) => {
                let available = Vec2::new(size.x - labels_width - 1, 1);
                view.important_area(available) + (labels_width, 0)
            }
            ListChild::Delimiter => Rect::from_size((0, 0), (size.x, 1)),
        };

        area + (0, self.focus)
    }
}
<|endoftext|>


<commit_before>#[macro_use(rpc_method, rpc_method_no_params, rpc_params)]
extern crate json_rpc;
extern crate hyper;

use json_rpc::Server as RpcServer;
use json_rpc::{Json, Error};
use json_rpc::serialize::json::ToJson;
use std::io::Read;
use std::io::Write;
use std::collections::BTreeMap;
use hyper::Server as ServerHttp;
use hyper::server::{Request,Response};

fn main() {
    println!("Started server on 10.0.2.15:8080");

    let mut rpc_server = RpcServer::new();

    register_methods(&mut rpc_server);

    ServerHttp::http(move |mut req:Request, mut res:Response| {
        match req.method {
            hyper::Post => {
                let mut str_req = String::new();
                req.read_to_string(&mut str_req).unwrap();
                let mut res = res.start().unwrap();
                match rpc_server.request(str_req) {
                    Some(str_res) => res.write_all(str_res.as_bytes()).unwrap() ,
                    None => (),
                };
                res.end().unwrap();
            },
            _ => *res.status_mut() = hyper::status::StatusCode::MethodNotAllowed
        }
    }).listen("10.0.2.15:8080").unwrap();

    println!("Stopped server!");

}

fn register_methods(rpc_server:&mut RpcServer) {
    // Registers a Rpc Method named "Subtract" with two parameter "by Name".
    rpc_method!(rpc_server, Subtract, oper1<u64>;oper2<u64>, {
        Ok(Json::U64(oper1 - oper2))
    });

    // Registers a Rpc Method named "Multiply" with N parameteres "by Position".
    rpc_method!(rpc_server, Multiply, values[u64], {
        let mut r = 1;
        for v in values { r *= v }
        Ok(Json::U64(r))
    });

    // This method can return an error
    rpc_method!(rpc_server, Division, oper1<f64>;oper2<f64>, {
        if oper2 == 0f64 {
            Err(Error::custom(1, "Division by zero", Some(Json::F64(oper1))))
        } else {
            Ok(Json::F64(oper1 / oper2))
        }
    });

    // Registers a method that returns an Array
    rpc_method!(rpc_server, Sequence, start<u64>;step<f64>;iterations<u64>, {
        let mut value = start as f64;
        let mut res = Vec::new();
        for _ in 0..iterations {
            res.push(Json::F64(value));
            value += step;
        }
        Ok(Json::Array(res))
    });

    struct Info {
        amount: u32,
        price: f64,
        description: String,
    }
    impl ToJson for Info {
        fn to_json(&self) -> Json {
            let mut d = BTreeMap::new();
            d.insert("amount".to_string(), self.amount.to_json());
            d.insert("price".to_string(), self.price.to_json());
            d.insert("description".to_string(), self.description.to_json());
            Json::Object(d)
        }
    }

    // Registers a method that returns an Object
    rpc_method_no_params!(rpc_server, GetInfo, {
        let info = Info { amount : 15, price: 2.33, description: "Apples".to_string() };
        Ok(info.to_json())
    });

    // Register a Rpc manually without macros
    rpc_server.register_method("Add", |json_params| {   // json_params: String
        // It uses a macro for parse the String into a Struct. rpc_params : { oper1:u64, oper2:u64 }
        let rpc_params = rpc_params!(json_params, oper1<u64>;oper2<u64> );
        let result = Json::U64(rpc_params.oper1 + rpc_params.oper2);
        Ok(result)
    });
}
<commit_msg>Fix Ip listening server on examples<commit_after>#[macro_use(rpc_method, rpc_method_no_params, rpc_params)]
extern crate json_rpc;
extern crate hyper;

use json_rpc::Server as RpcServer;
use json_rpc::{Json, Error};
use json_rpc::serialize::json::ToJson;
use std::io::Read;
use std::io::Write;
use std::collections::BTreeMap;
use hyper::Server as ServerHttp;
use hyper::server::{Request,Response};

fn main() {
    println!("Started server on 127.0.0.1:8080");

    let mut rpc_server = RpcServer::new();

    register_methods(&mut rpc_server);

    ServerHttp::http(move |mut req:Request, mut res:Response| {
        match req.method {
            hyper::Post => {
                let mut str_req = String::new();
                req.read_to_string(&mut str_req).unwrap();
                let mut res = res.start().unwrap();
                match rpc_server.request(str_req) {
                    Some(str_res) => res.write_all(str_res.as_bytes()).unwrap() ,
                    None => (),
                };
                res.end().unwrap();
            },
            _ => *res.status_mut() = hyper::status::StatusCode::MethodNotAllowed
        }
    }).listen("127.0.0.1:8080").unwrap();

    println!("Stopped server!");

}

fn register_methods(rpc_server:&mut RpcServer) {
    // Registers a Rpc Method named "Subtract" with two parameter "by Name".
    rpc_method!(rpc_server, Subtract, oper1<u64>;oper2<u64>, {
        Ok(Json::U64(oper1 - oper2))
    });

    // Registers a Rpc Method named "Multiply" with N parameteres "by Position".
    rpc_method!(rpc_server, Multiply, values[u64], {
        let mut r = 1;
        for v in values { r *= v }
        Ok(Json::U64(r))
    });

    // This method can return an error
    rpc_method!(rpc_server, Division, oper1<f64>;oper2<f64>, {
        if oper2 == 0f64 {
            Err(Error::custom(1, "Division by zero", Some(Json::F64(oper1))))
        } else {
            Ok(Json::F64(oper1 / oper2))
        }
    });

    // Registers a method that returns an Array
    rpc_method!(rpc_server, Sequence, start<u64>;step<f64>;iterations<u64>, {
        let mut value = start as f64;
        let mut res = Vec::new();
        for _ in 0..iterations {
            res.push(Json::F64(value));
            value += step;
        }
        Ok(Json::Array(res))
    });

    struct Info {
        amount: u32,
        price: f64,
        description: String,
    }
    impl ToJson for Info {
        fn to_json(&self) -> Json {
            let mut d = BTreeMap::new();
            d.insert("amount".to_string(), self.amount.to_json());
            d.insert("price".to_string(), self.price.to_json());
            d.insert("description".to_string(), self.description.to_json());
            Json::Object(d)
        }
    }

    // Registers a method that returns an Object
    rpc_method_no_params!(rpc_server, GetInfo, {
        let info = Info { amount : 15, price: 2.33, description: "Apples".to_string() };
        Ok(info.to_json())
    });

    // Register a Rpc manually without macros
    rpc_server.register_method("Add", |json_params| {   // json_params: String
        // It uses a macro for parse the String into a Struct. rpc_params : { oper1:u64, oper2:u64 }
        let rpc_params = rpc_params!(json_params, oper1<u64>;oper2<u64> );
        let result = Json::U64(rpc_params.oper1 + rpc_params.oper2);
        Ok(result)
    });
}
<|endoftext|>


<commit_before>use rustc_serialize::json::Json;

pub trait Decodable {
    fn decode(json: &mut Json) -> Result<Self, String>;
}
<commit_msg>deleted unused file<commit_after><|endoftext|>


<commit_before>use webcore::reference_type::ReferenceType;
use webapi::node_list::NodeList;
use webapi::element::Element;
use private::TODO;

/// The `ParentNode` mixin contains methods and properties
/// that are common to all types of `Node` objects that can
/// have children.
///
/// You most likely don't want to `use` this directly; instead
/// you should `use stdweb::traits::*;`.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode)
// https://dom.spec.whatwg.org/#parentnode
pub trait IParentNode: ReferenceType {
    /// Returns the first element that is a descendant of the element on which it is
    /// invoked that matches the specified group of selectors.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector)
    // https://dom.spec.whatwg.org/#ref-for-dom-parentnode-queryselector
    fn query_selector( &self, selector: &str ) -> Result< Option< Element >, TODO > {
        unsafe {
            Ok( js!( return @{self.as_ref()}.querySelector( @{selector} ); ).into_reference_unchecked() )
        }
    }

    /// Returns a non-live [NodeList](struct.NodeList.html) of all elements descended
    /// from the element on which it is invoked that matches the specified group of CSS selectors.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll)
    // https://dom.spec.whatwg.org/#ref-for-dom-parentnode-queryselectorall
    fn query_selector_all( &self, selector: &str ) -> Result< NodeList, TODO > {
        unsafe {
            Ok( js!( return @{self.as_ref()}.querySelectorAll( @{selector} ); ).into_reference_unchecked().unwrap() )
        }
    }
}
<commit_msg>Return a proper error from IParentNode::{query_selector, query_selector_all}` (#298)<commit_after>use webcore::reference_type::ReferenceType;
use webapi::node_list::NodeList;
use webapi::element::Element;
use webapi::dom_exception::SyntaxError;

/// The `ParentNode` mixin contains methods and properties
/// that are common to all types of `Node` objects that can
/// have children.
///
/// You most likely don't want to `use` this directly; instead
/// you should `use stdweb::traits::*;`.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode)
// https://dom.spec.whatwg.org/#parentnode
pub trait IParentNode: ReferenceType {
    /// Returns the first element that is a descendant of the element on which it is
    /// invoked that matches the specified group of selectors.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector)
    // https://dom.spec.whatwg.org/#ref-for-dom-parentnode-queryselector
    fn query_selector( &self, selector: &str ) -> Result< Option< Element >, SyntaxError > {
        js_try!(
            return @{self.as_ref()}.querySelector(@{selector});
        ).unwrap()
    }

    /// Returns a non-live [NodeList](struct.NodeList.html) of all elements descended
    /// from the element on which it is invoked that matches the specified group of CSS selectors.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll)
    // https://dom.spec.whatwg.org/#ref-for-dom-parentnode-queryselectorall
    fn query_selector_all( &self, selector: &str ) -> Result< NodeList, SyntaxError > {
        js_try!(
            return @{self.as_ref()}.querySelectorAll(@{selector});
        ).unwrap()
    }
}

#[cfg(all(test, feature = "web_test"))]
mod tests {
    use super::*;
    use stdweb::web::document;
    use webapi::node::INode;

    #[test]
    fn test_query_selector_finds_h1() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        parent.append_child(&child);

        assert_eq!(parent.query_selector("h1").unwrap().unwrap().as_ref(), child.as_ref());
    }

    #[test]
    fn test_query_selector_all_finds_h1s() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        let child2 = document().create_element("h1").unwrap();
        parent.append_child(&child);
        parent.append_child(&child2);

        assert_eq!(parent.query_selector_all("h1").unwrap().len(), 2);
    }

    #[test]
    fn test_query_selector_finds_nested_h1() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("p").unwrap();
        let title = document().create_element("h1").unwrap();
        child.append_child(&title);
        parent.append_child(&child);

        assert_eq!(parent.query_selector("h1").unwrap().unwrap().as_ref(), title.as_ref());
    }

    #[test]
    fn test_query_selector_all_finds_nested_h1s() {
        let parent = document().create_element("div").unwrap();
        let top_title = document().create_element("h1").unwrap();
        let child = document().create_element("p").unwrap();
        let title = document().create_element("h1").unwrap();
        child.append_child(&title);
        parent.append_child(&child);
        parent.append_child(&top_title);

        assert_eq!(parent.query_selector_all("h1").unwrap().len(), 2);
    }

    #[test]
    fn test_query_selector_not_found() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        parent.append_child(&child);

        assert!(parent.query_selector("p").unwrap().is_none());
    }

    #[test]
    fn test_query_selector_all_not_found() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        parent.append_child(&child);

        assert_eq!(parent.query_selector_all("p").unwrap().len(), 0);
    }

    #[test]
    fn test_query_selector_syntax_error() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        parent.append_child(&child);

        assert!(child.query_selector("invalid syntax +#8$()@!(#").is_err());
    }

    #[test]
    fn test_query_selector_all_syntax_error() {
        let parent = document().create_element("div").unwrap();
        let child = document().create_element("h1").unwrap();
        parent.append_child(&child);

        assert!(child.query_selector_all("invalid syntax +#8$()@!(#").is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::VecDeque;
use std::error;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;

use rocksdb::DB;
use storage::CF_WRITE;
use util::escape;
use util::rocksdb;
use util::rocksdb::compact_range;
use util::rocksdb::stats::get_range_entries_and_versions;
use util::worker::Runnable;

use super::metrics::COMPACT_RANGE_CF;

type Key = Vec<u8>;

pub enum Task {
    Compact {
        cf_name: String,
        start_key: Option<Key>, // None means smallest key
        end_key: Option<Key>,   // None means largest key
    },

    CheckAndCompact {
        cf_names: Vec<String>,         // Column families need to compact
        ranges: Vec<Key>,              // Ranges need to check
        tombstones_num_threshold: u64, // The minimum RocksDB tombstones a range that need compacting has
        tombstones_percent_threshold: u64,
    },
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Compact {
                ref cf_name,
                ref start_key,
                ref end_key,
            } => f
                .debug_struct("Compact")
                .field("cf_name", cf_name)
                .field("start_key", &start_key.as_ref().map(|k| escape(k)))
                .field("end_key", &end_key.as_ref().map(|k| escape(k)))
                .finish(),
            Task::CheckAndCompact {
                ref cf_names,
                ref ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            } => f
                .debug_struct("CheckAndCompact")
                .field("cf_names", cf_names)
                .field(
                    "ranges",
                    &(
                        ranges.first().as_ref().map(|k| escape(k)),
                        ranges.last().as_ref().map(|k| escape(k)),
                    ),
                )
                .field("tombstones_num_threshold", &tombstones_num_threshold)
                .field(
                    "tombstones_percent_threshold",
                    &tombstones_percent_threshold,
                )
                .finish(),
        }
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("compact failed {:?}", err)
        }
    }
}

pub struct Runner {
    engine: Arc<DB>,
}

impl Runner {
    pub fn new(engine: Arc<DB>) -> Runner {
        Runner { engine }
    }

    pub fn compact_range_cf(
        &mut self,
        cf_name: String,
        start_key: Option<Vec<u8>>,
        end_key: Option<Vec<u8>>,
    ) -> Result<(), Error> {
        let handle = box_try!(rocksdb::get_cf_handle(&self.engine, &cf_name));
        let compact_range_timer = COMPACT_RANGE_CF
            .with_label_values(&[&cf_name])
            .start_coarse_timer();
        let start = start_key.as_ref().map(Vec::as_slice);
        let end = end_key.as_ref().map(Vec::as_slice);
        compact_range(
            &self.engine,
            handle,
            start,
            end,
            false,
            1, /* threads */
        );
        compact_range_timer.observe_duration();
        Ok(())
    }
}

impl Runnable<Task> for Runner {
    fn run(&mut self, task: Task) {
        match task {
            Task::Compact {
                cf_name,
                start_key,
                end_key,
            } => {
                let cf = cf_name.clone();
                if let Err(e) = self.compact_range_cf(cf_name, start_key, end_key) {
                    error!("execute compact range for cf {} failed, err {}", &cf, e);
                } else {
                    info!("compact range for cf {} finished", &cf);
                }
            }
            Task::CheckAndCompact {
                cf_names,
                ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            } => match collect_ranges_need_compact(
                &self.engine,
                ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            ) {
                Ok(mut ranges) => for (start, end) in ranges.drain(..) {
                    for cf in &cf_names {
                        if let Err(e) = self.compact_range_cf(
                            cf.clone(),
                            Some(start.clone()),
                            Some(end.clone()),
                        ) {
                            error!(
                                "compact range ({:?}, {:?}) for cf {:?} failed, error {:?}",
                                start, end, cf, e
                            );
                        }
                    }
                },
                Err(e) => warn!("check ranges need reclaim failed, err: {:?}", e),
            },
        }
    }
}

fn need_compact(
    num_entires: u64,
    num_versions: u64,
    tombstones_num_threshold: u64,
    tombstones_percent_threshold: u64,
) -> bool {
    if num_entires <= num_versions {
        return false;
    }

    // When the number of tombstones exceed threshold and ratio, this range need compacting.
    let estimate_num_del = num_entires - num_versions;
    estimate_num_del >= tombstones_num_threshold
        && estimate_num_del * 100 >= tombstones_percent_threshold * num_entires
}

fn collect_ranges_need_compact(
    engine: &DB,
    ranges: Vec<Key>,
    tombstones_num_threshold: u64,
    tombstones_percent_threshold: u64,
) -> Result<VecDeque<(Key, Key)>, Error> {
    // Check the SST properties for each range, and we will compact a range if the range
    // contains too much RocksDB tombstones. we will merge multiple neighbouring ranges
    // that need compacting into a single range.
    let mut ranges_need_compact = VecDeque::new();
    let cf = box_try!(rocksdb::get_cf_handle(engine, CF_WRITE));
    let mut compact_start = None;
    let mut compact_end = None;
    for range in ranges.windows(2) {
        // Get total entries and total versions in this range and check if need compacting.
        if let Some((num_ent, num_ver)) =
            get_range_entries_and_versions(engine, cf, &range[0], &range[1])
        {
            if need_compact(
                num_ent,
                num_ver,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            ) {
                if compact_start.is_none() {
                    // The previous range doesn't need compacting.
                    compact_start = Some(range[0].clone());
                }
                compact_end = Some(range[1].clone());
                // Move to next range.
                continue;
            }
        }

        // Current range doesn't need compacting, save previous range that need compacting.
        if compact_start.is_some() {
            ranges_need_compact.push_back((compact_start.unwrap(), compact_end.unwrap()));
            compact_start = None;
            compact_end = None;
        }
    }

    // Save the last range that need compacting.
    if compact_start.is_some() {
        ranges_need_compact.push_back((compact_start.unwrap(), compact_end.unwrap()));
    }

    Ok(ranges_need_compact)
}

#[cfg(test)]
mod test {
    use std::thread::sleep;
    use std::time::Duration;

    use tempdir::TempDir;

    use raftstore::store::keys::data_key;
    use rocksdb::{self, Writable, WriteBatch, DB};
    use storage::mvcc::{Write, WriteType};
    use storage::types::Key as MvccKey;
    use storage::{CF_DEFAULT, CF_LOCK, CF_RAFT, CF_WRITE};
    use util::properties::MvccPropertiesCollectorFactory;
    use util::rocksdb::new_engine;
    use util::rocksdb::stats::get_range_entries_and_versions;
    use util::rocksdb::{get_cf_handle, new_engine_opt, CFOptions};

    use super::*;

    const ROCKSDB_TOTAL_SST_FILES_SIZE: &str = "rocksdb.total-sst-files-size";

    #[test]
    fn test_compact_range() {
        let path = TempDir::new("compact-range-test").unwrap();
        let db = new_engine(path.path().to_str().unwrap(), &[CF_DEFAULT], None).unwrap();
        let db = Arc::new(db);

        let mut runner = Runner::new(Arc::clone(&db));

        let handle = get_cf_handle(&db, CF_DEFAULT).unwrap();

        // generate first sst file.
        let wb = WriteBatch::new();
        for i in 0..1000 {
            let k = format!("key_{}", i);
            wb.put_cf(handle, k.as_bytes(), b"whatever content")
                .unwrap();
        }
        db.write(wb).unwrap();
        db.flush_cf(handle, true).unwrap();

        // generate another sst file has the same content with first sst file.
        let wb = WriteBatch::new();
        for i in 0..1000 {
            let k = format!("key_{}", i);
            wb.put_cf(handle, k.as_bytes(), b"whatever content")
                .unwrap();
        }
        db.write(wb).unwrap();
        db.flush_cf(handle, true).unwrap();

        // get total sst files size.
        let old_sst_files_size = db
            .get_property_int_cf(handle, ROCKSDB_TOTAL_SST_FILES_SIZE)
            .unwrap();

        // schedule compact range task
        runner.run(Task::Compact {
            cf_name: String::from(CF_DEFAULT),
            start_key: None,
            end_key: None,
        });
        sleep(Duration::from_secs(5));

        // get total sst files size after compact range.
        let new_sst_files_size = db
            .get_property_int_cf(handle, ROCKSDB_TOTAL_SST_FILES_SIZE)
            .unwrap();
        assert!(old_sst_files_size > new_sst_files_size);
    }

    fn mvcc_put(db: &DB, k: &[u8], v: &[u8], start_ts: u64, commit_ts: u64) {
        let cf = get_cf_handle(db, CF_WRITE).unwrap();
        let k = MvccKey::from_encoded(data_key(k));
        let k = k.append_ts(commit_ts);
        let w = Write::new(WriteType::Put, start_ts, Some(v.to_vec()));
        db.put_cf(cf, k.encoded(), &w.to_bytes()).unwrap();
    }

    fn delete(db: &DB, k: &[u8], commit_ts: u64) {
        let cf = get_cf_handle(db, CF_WRITE).unwrap();
        let k = MvccKey::from_encoded(data_key(k));
        let k = k.append_ts(commit_ts);
        db.delete_cf(cf, k.encoded()).unwrap();
    }

    fn open_db(path: &str) -> DB {
        let db_opts = rocksdb::DBOptions::new();
        let mut cf_opts = rocksdb::ColumnFamilyOptions::new();
        cf_opts.set_level_zero_file_num_compaction_trigger(8);
        let f = Box::new(MvccPropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.test-collector", f);
        let cfs_opts = vec![
            CFOptions::new(CF_DEFAULT, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_RAFT, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_LOCK, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_WRITE, cf_opts),
        ];
        new_engine_opt(path, db_opts, cfs_opts).unwrap()
    }

    #[test]
    fn test_check_space_redundancy() {
        let p = TempDir::new("test").unwrap();
        let engine = open_db(p.path().to_str().unwrap());
        let cf = get_cf_handle(&engine, CF_WRITE).unwrap();

        // mvcc_put 0..5
        for i in 0..5 {
            let (k, v) = (format!("k{}", i), format!("value{}", i));
            mvcc_put(&engine, k.as_bytes(), v.as_bytes(), 1, 2);
        }
        engine.flush_cf(cf, true).unwrap();

        // gc 0..5
        for i in 0..5 {
            let k = format!("k{}", i);
            delete(&engine, k.as_bytes(), 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k0"), data_key(b"k5"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 10);
        assert_eq!(version, 5);

        // mvcc_put 5..10
        for i in 5..10 {
            let (k, v) = (format!("k{}", i), format!("value{}", i));
            mvcc_put(&engine, k.as_bytes(), v.as_bytes(), 1, 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k5"), data_key(b"k9"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 5);
        assert_eq!(version, 5);

        let ranges_need_to_compact = collect_ranges_need_compact(
            &engine,
            vec![data_key(b"k0"), data_key(b"k5"), data_key(b"k9")],
            1,
            50,
        ).unwrap();
        let (s, e) = (data_key(b"k0"), data_key(b"k5"));
        let mut expected_ranges = VecDeque::new();
        expected_ranges.push_back((s, e));
        assert_eq!(ranges_need_to_compact, expected_ranges);

        // gc 5..10
        for i in 5..10 {
            let k = format!("k{}", i);
            delete(&engine, k.as_bytes(), 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k5"), data_key(b"k9"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 10);
        assert_eq!(version, 5);

        let ranges_need_to_compact = collect_ranges_need_compact(
            &engine,
            vec![data_key(b"k0"), data_key(b"k5"), data_key(b"k9")],
            1,
            50,
        ).unwrap();
        let (s, e) = (data_key(b"k0"), data_key(b"k9"));
        let mut expected_ranges = VecDeque::new();
        expected_ranges.push_back((s, e));
        assert_eq!(ranges_need_to_compact, expected_ranges);
    }
}
<commit_msg>raftstore: log duration after compact_range finished (#3340)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::VecDeque;
use std::error;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;
use std::time::Instant;

use rocksdb::DB;
use storage::CF_WRITE;
use util::escape;
use util::rocksdb;
use util::rocksdb::compact_range;
use util::rocksdb::stats::get_range_entries_and_versions;
use util::worker::Runnable;

use super::metrics::COMPACT_RANGE_CF;

type Key = Vec<u8>;

pub enum Task {
    Compact {
        cf_name: String,
        start_key: Option<Key>, // None means smallest key
        end_key: Option<Key>,   // None means largest key
    },

    CheckAndCompact {
        cf_names: Vec<String>,         // Column families need to compact
        ranges: Vec<Key>,              // Ranges need to check
        tombstones_num_threshold: u64, // The minimum RocksDB tombstones a range that need compacting has
        tombstones_percent_threshold: u64,
    },
}

impl Display for Task {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Task::Compact {
                ref cf_name,
                ref start_key,
                ref end_key,
            } => f
                .debug_struct("Compact")
                .field("cf_name", cf_name)
                .field("start_key", &start_key.as_ref().map(|k| escape(k)))
                .field("end_key", &end_key.as_ref().map(|k| escape(k)))
                .finish(),
            Task::CheckAndCompact {
                ref cf_names,
                ref ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            } => f
                .debug_struct("CheckAndCompact")
                .field("cf_names", cf_names)
                .field(
                    "ranges",
                    &(
                        ranges.first().as_ref().map(|k| escape(k)),
                        ranges.last().as_ref().map(|k| escape(k)),
                    ),
                )
                .field("tombstones_num_threshold", &tombstones_num_threshold)
                .field(
                    "tombstones_percent_threshold",
                    &tombstones_percent_threshold,
                )
                .finish(),
        }
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Other(err: Box<error::Error + Sync + Send>) {
            from()
            cause(err.as_ref())
            description(err.description())
            display("compact failed {:?}", err)
        }
    }
}

pub struct Runner {
    engine: Arc<DB>,
}

impl Runner {
    pub fn new(engine: Arc<DB>) -> Runner {
        Runner { engine }
    }

    pub fn compact_range_cf(
        &mut self,
        cf_name: String,
        start_key: Option<Vec<u8>>,
        end_key: Option<Vec<u8>>,
    ) -> Result<(), Error> {
        let handle = box_try!(rocksdb::get_cf_handle(&self.engine, &cf_name));
        let timer = Instant::now();
        let compact_range_timer = COMPACT_RANGE_CF
            .with_label_values(&[&cf_name])
            .start_coarse_timer();
        let start = start_key.as_ref().map(Vec::as_slice);
        let end = end_key.as_ref().map(Vec::as_slice);
        compact_range(
            &self.engine,
            handle,
            start,
            end,
            false,
            1, /* threads */
        );
        compact_range_timer.observe_duration();
        info!(
            "compact range ({:?}, {:?}) for cf {:?} finished, takes: {:?}",
            start,
            end,
            cf_name,
            timer.elapsed()
        );
        Ok(())
    }
}

impl Runnable<Task> for Runner {
    fn run(&mut self, task: Task) {
        match task {
            Task::Compact {
                cf_name,
                start_key,
                end_key,
            } => {
                let cf = cf_name.clone();
                if let Err(e) = self.compact_range_cf(cf_name, start_key, end_key) {
                    error!("execute compact range for cf {} failed, err {}", &cf, e);
                }
            }
            Task::CheckAndCompact {
                cf_names,
                ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            } => match collect_ranges_need_compact(
                &self.engine,
                ranges,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            ) {
                Ok(mut ranges) => for (start, end) in ranges.drain(..) {
                    for cf in &cf_names {
                        if let Err(e) = self.compact_range_cf(
                            cf.clone(),
                            Some(start.clone()),
                            Some(end.clone()),
                        ) {
                            error!(
                                "compact range ({:?}, {:?}) for cf {:?} failed, error {:?}",
                                start, end, cf, e
                            );
                        }
                    }
                },
                Err(e) => warn!("check ranges need reclaim failed, err: {:?}", e),
            },
        }
    }
}

fn need_compact(
    num_entires: u64,
    num_versions: u64,
    tombstones_num_threshold: u64,
    tombstones_percent_threshold: u64,
) -> bool {
    if num_entires <= num_versions {
        return false;
    }

    // When the number of tombstones exceed threshold and ratio, this range need compacting.
    let estimate_num_del = num_entires - num_versions;
    estimate_num_del >= tombstones_num_threshold
        && estimate_num_del * 100 >= tombstones_percent_threshold * num_entires
}

fn collect_ranges_need_compact(
    engine: &DB,
    ranges: Vec<Key>,
    tombstones_num_threshold: u64,
    tombstones_percent_threshold: u64,
) -> Result<VecDeque<(Key, Key)>, Error> {
    // Check the SST properties for each range, and we will compact a range if the range
    // contains too much RocksDB tombstones. we will merge multiple neighbouring ranges
    // that need compacting into a single range.
    let mut ranges_need_compact = VecDeque::new();
    let cf = box_try!(rocksdb::get_cf_handle(engine, CF_WRITE));
    let mut compact_start = None;
    let mut compact_end = None;
    for range in ranges.windows(2) {
        // Get total entries and total versions in this range and check if need compacting.
        if let Some((num_ent, num_ver)) =
            get_range_entries_and_versions(engine, cf, &range[0], &range[1])
        {
            if need_compact(
                num_ent,
                num_ver,
                tombstones_num_threshold,
                tombstones_percent_threshold,
            ) {
                if compact_start.is_none() {
                    // The previous range doesn't need compacting.
                    compact_start = Some(range[0].clone());
                }
                compact_end = Some(range[1].clone());
                // Move to next range.
                continue;
            }
        }

        // Current range doesn't need compacting, save previous range that need compacting.
        if compact_start.is_some() {
            ranges_need_compact.push_back((compact_start.unwrap(), compact_end.unwrap()));
            compact_start = None;
            compact_end = None;
        }
    }

    // Save the last range that need compacting.
    if compact_start.is_some() {
        ranges_need_compact.push_back((compact_start.unwrap(), compact_end.unwrap()));
    }

    Ok(ranges_need_compact)
}

#[cfg(test)]
mod test {
    use std::thread::sleep;
    use std::time::Duration;

    use tempdir::TempDir;

    use raftstore::store::keys::data_key;
    use rocksdb::{self, Writable, WriteBatch, DB};
    use storage::mvcc::{Write, WriteType};
    use storage::types::Key as MvccKey;
    use storage::{CF_DEFAULT, CF_LOCK, CF_RAFT, CF_WRITE};
    use util::properties::MvccPropertiesCollectorFactory;
    use util::rocksdb::new_engine;
    use util::rocksdb::stats::get_range_entries_and_versions;
    use util::rocksdb::{get_cf_handle, new_engine_opt, CFOptions};

    use super::*;

    const ROCKSDB_TOTAL_SST_FILES_SIZE: &str = "rocksdb.total-sst-files-size";

    #[test]
    fn test_compact_range() {
        let path = TempDir::new("compact-range-test").unwrap();
        let db = new_engine(path.path().to_str().unwrap(), &[CF_DEFAULT], None).unwrap();
        let db = Arc::new(db);

        let mut runner = Runner::new(Arc::clone(&db));

        let handle = get_cf_handle(&db, CF_DEFAULT).unwrap();

        // generate first sst file.
        let wb = WriteBatch::new();
        for i in 0..1000 {
            let k = format!("key_{}", i);
            wb.put_cf(handle, k.as_bytes(), b"whatever content")
                .unwrap();
        }
        db.write(wb).unwrap();
        db.flush_cf(handle, true).unwrap();

        // generate another sst file has the same content with first sst file.
        let wb = WriteBatch::new();
        for i in 0..1000 {
            let k = format!("key_{}", i);
            wb.put_cf(handle, k.as_bytes(), b"whatever content")
                .unwrap();
        }
        db.write(wb).unwrap();
        db.flush_cf(handle, true).unwrap();

        // get total sst files size.
        let old_sst_files_size = db
            .get_property_int_cf(handle, ROCKSDB_TOTAL_SST_FILES_SIZE)
            .unwrap();

        // schedule compact range task
        runner.run(Task::Compact {
            cf_name: String::from(CF_DEFAULT),
            start_key: None,
            end_key: None,
        });
        sleep(Duration::from_secs(5));

        // get total sst files size after compact range.
        let new_sst_files_size = db
            .get_property_int_cf(handle, ROCKSDB_TOTAL_SST_FILES_SIZE)
            .unwrap();
        assert!(old_sst_files_size > new_sst_files_size);
    }

    fn mvcc_put(db: &DB, k: &[u8], v: &[u8], start_ts: u64, commit_ts: u64) {
        let cf = get_cf_handle(db, CF_WRITE).unwrap();
        let k = MvccKey::from_encoded(data_key(k));
        let k = k.append_ts(commit_ts);
        let w = Write::new(WriteType::Put, start_ts, Some(v.to_vec()));
        db.put_cf(cf, k.encoded(), &w.to_bytes()).unwrap();
    }

    fn delete(db: &DB, k: &[u8], commit_ts: u64) {
        let cf = get_cf_handle(db, CF_WRITE).unwrap();
        let k = MvccKey::from_encoded(data_key(k));
        let k = k.append_ts(commit_ts);
        db.delete_cf(cf, k.encoded()).unwrap();
    }

    fn open_db(path: &str) -> DB {
        let db_opts = rocksdb::DBOptions::new();
        let mut cf_opts = rocksdb::ColumnFamilyOptions::new();
        cf_opts.set_level_zero_file_num_compaction_trigger(8);
        let f = Box::new(MvccPropertiesCollectorFactory::default());
        cf_opts.add_table_properties_collector_factory("tikv.test-collector", f);
        let cfs_opts = vec![
            CFOptions::new(CF_DEFAULT, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_RAFT, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_LOCK, rocksdb::ColumnFamilyOptions::new()),
            CFOptions::new(CF_WRITE, cf_opts),
        ];
        new_engine_opt(path, db_opts, cfs_opts).unwrap()
    }

    #[test]
    fn test_check_space_redundancy() {
        let p = TempDir::new("test").unwrap();
        let engine = open_db(p.path().to_str().unwrap());
        let cf = get_cf_handle(&engine, CF_WRITE).unwrap();

        // mvcc_put 0..5
        for i in 0..5 {
            let (k, v) = (format!("k{}", i), format!("value{}", i));
            mvcc_put(&engine, k.as_bytes(), v.as_bytes(), 1, 2);
        }
        engine.flush_cf(cf, true).unwrap();

        // gc 0..5
        for i in 0..5 {
            let k = format!("k{}", i);
            delete(&engine, k.as_bytes(), 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k0"), data_key(b"k5"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 10);
        assert_eq!(version, 5);

        // mvcc_put 5..10
        for i in 5..10 {
            let (k, v) = (format!("k{}", i), format!("value{}", i));
            mvcc_put(&engine, k.as_bytes(), v.as_bytes(), 1, 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k5"), data_key(b"k9"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 5);
        assert_eq!(version, 5);

        let ranges_need_to_compact = collect_ranges_need_compact(
            &engine,
            vec![data_key(b"k0"), data_key(b"k5"), data_key(b"k9")],
            1,
            50,
        ).unwrap();
        let (s, e) = (data_key(b"k0"), data_key(b"k5"));
        let mut expected_ranges = VecDeque::new();
        expected_ranges.push_back((s, e));
        assert_eq!(ranges_need_to_compact, expected_ranges);

        // gc 5..10
        for i in 5..10 {
            let k = format!("k{}", i);
            delete(&engine, k.as_bytes(), 2);
        }
        engine.flush_cf(cf, true).unwrap();

        let (s, e) = (data_key(b"k5"), data_key(b"k9"));
        let (entries, version) = get_range_entries_and_versions(&engine, cf, &s, &e).unwrap();
        assert_eq!(entries, 10);
        assert_eq!(version, 5);

        let ranges_need_to_compact = collect_ranges_need_compact(
            &engine,
            vec![data_key(b"k0"), data_key(b"k5"), data_key(b"k9")],
            1,
            50,
        ).unwrap();
        let (s, e) = (data_key(b"k0"), data_key(b"k9"));
        let mut expected_ranges = VecDeque::new();
        expected_ranges.push_back((s, e));
        assert_eq!(ranges_need_to_compact, expected_ranges);
    }
}
<|endoftext|>