<commit_before>use color::{ Color, Colorable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use rectangle;
use ui_context::{ UIID, UiContext };
use vecmath::vec2_add;
use graphics::Graphics;
use graphics::character::CharacterCache;
use widget::Widget;

/// Tuple / Callback params.
pub type Idx = usize;
pub type Len = usize;

/// Represents the state of the menu.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Closed(DrawState),
    Open(DrawState),
}

/// Represents the state of the DropDownList widget.
#[derive(PartialEq, Clone, Copy)]
pub enum DrawState {
    Normal,
    Highlighted(Idx, Len),
    Clicked(Idx, Len),
}

impl DrawState {
    /// Translate the DropDownList's DrawState to the equivalent rectangle::State.
    fn as_rect_state(&self) -> rectangle::State {
        match self {
            &DrawState::Normal => rectangle::State::Normal,
            &DrawState::Highlighted(_, _) => rectangle::State::Highlighted,
            &DrawState::Clicked(_, _) => rectangle::State::Clicked,
        }
    }
}

impl State {
    /// Translate the DropDownList's State to the equivalent rectangle::State.
    fn as_rect_state(&self) -> rectangle::State {
        match self {
            &State::Open(draw_state) | &State::Closed(draw_state) => draw_state.as_rect_state(),
        }
    }
}

widget_fns!(DropDownList, State, Widget::DropDownList(State::Closed(DrawState::Normal)));

/// Is the cursor currently over the widget? If so which item?
fn is_over(pos: Point,
           mouse_pos: Point,
           dim: Dimensions,
           state: State,
           len: Len) -> Option<Idx> {
    match state {
        State::Closed(_) => {
            match rectangle::is_over(pos, mouse_pos, dim) {
                false => None,
                true => Some(0),
            }
        },
        State::Open(_) => {
            let total_h = dim[1] * len as f64;
            match rectangle::is_over(pos, mouse_pos, [dim[0], total_h]) {
                false => None,
                true => Some((((mouse_pos[1] - pos[1]) / total_h) * len as f64) as usize),
            }
        },
    }
}

/// Determine and return the new State by comparing the mouse state
/// and position to the previous State.
fn get_new_state(is_over_idx: Option<Idx>,
                 len: Len,
                 state: State,
                 mouse: Mouse) -> State {
    use self::DrawState::{Normal, Clicked, Highlighted};
    use mouse::ButtonState::{Down, Up};
    match state {
        State::Closed(draw_state) => {
            match is_over_idx {
                Some(_) => {
                    match (draw_state, mouse.left) {
                        (Normal,            Down) => State::Closed(Normal),
                        (Normal,            Up)   |
                        (Highlighted(_, _), Up)   => State::Closed(Highlighted(0, len)),
                        (Highlighted(_, _), Down) => State::Closed(Clicked(0, len)),
                        (Clicked(_, _),     Down) => State::Closed(Clicked(0, len)),
                        (Clicked(_, _),     Up)   => State::Open(Normal),
                    }
                },
                None => State::Closed(Normal),
            }
        },
        State::Open(draw_state) => {
            match is_over_idx {
                Some(idx) => {
                    match (draw_state, mouse.left) {
                        (Normal,            Down) => State::Open(Normal),
                        (Normal,            Up)   |
                        (Highlighted(_, _), Up)   => State::Open(Highlighted(idx, len)),
                        (Highlighted(_, _), Down) => State::Open(Clicked(idx, len)),
                        (Clicked(p_idx, _), Down) => State::Open(Clicked(p_idx, len)),
                        (Clicked(_, _),     Up)   => State::Closed(Normal),
                    }
                },
                None => {
                    match (draw_state, mouse.left) {
                        (Highlighted(p_idx, _), Up) => State::Open(Highlighted(p_idx, len)),
                        _ => State::Closed(Normal),
                    }
                },
            }
        }
    }
}

/// A context on which the builder pattern can be implemented.
pub struct DropDownList<'a, F> {
    ui_id: UIID,
    strings: &'a mut Vec<String>,
    selected: &'a mut Option<Idx>,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
}

impl<'a, F> DropDownList<'a, F> {
    pub fn new(ui_id: UIID,
               strings: &'a mut Vec<String>,
               selected: &'a mut Option<Idx>) -> DropDownList<'a, F> {
        DropDownList {
            ui_id: ui_id,
            strings: strings,
            selected: selected,
            pos: [0.0, 0.0],
            dim: [128.0, 32.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }
}

impl<'a, F> Colorable for DropDownList<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

/*
quack! {
    list: DropDownList['a, F]
    get:
        fn () -> Size [] { Size(list.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(
                Widget::DropDownList(State::Closed(DrawState::Normal))
            )
        }
        fn () -> Id [] { Id(list.ui_id) }
    set:
        fn (val: Callback<F>) [where F: FnMut(&mut Option<Idx>, Idx, String) + 'a] {
            list.maybe_callback = Some(val.0)
        }
        fn (val: FrameColor) [] { list.maybe_frame_color = Some(val.0) }
        fn (val: FrameWidth) [] { list.maybe_frame = Some(val.0) }
        fn (val: LabelText<'a>) [] { list.maybe_label = Some(val.0) }
        fn (val: LabelColor) [] { list.maybe_label_color = Some(val.0) }
        fn (val: LabelFontSize) [] { list.maybe_label_font_size = Some(val.0) }
        fn (val: Position) [] { list.pos = val.0 }
        fn (val: Size) [] { list.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for DropDownList<'a, F>
    where
        F: FnMut(&mut Option<Idx>, Idx, String) + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over_idx = is_over(self.pos, mouse.pos, self.dim, state, self.strings.len());
        let new_state = get_new_state(is_over_idx, self.strings.len(), state, mouse);

        let sel = match *self.selected {
            Some(idx) if idx < self.strings.len() => { Some(idx) },
            _ => None,
        };
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let t_size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
        let t_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);

        // Call the `callback` closure if mouse was released
        // on one of the DropDownMenu items.
        match (state, new_state) {
            (State::Open(o_d_state), State::Closed(c_d_state)) => {
                match (o_d_state, c_d_state) {
                    (DrawState::Clicked(idx, _), DrawState::Normal) => {
                        match self.maybe_callback {
                            Some(ref mut callback) => (*callback)(self.selected, idx, (*self.strings)[idx].clone()),
                            None => (),
                        }
                    }, _ => (),
                }
            }, _ => (),
        }

        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };

        match new_state {

            State::Closed(_) => {
                let rect_state = new_state.as_rect_state();
                let text = match sel {
                    Some(idx) => &(*self.strings)[idx][..],
                    None => match self.maybe_label {
                        Some(text) => text,
                        None => &(*self.strings)[0][..],
                    },
                };
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, t_size, t_color
                )
            },

            State::Open(draw_state) => {
                for (i, string) in self.strings.iter().enumerate() {
                    let rect_state = match sel {
                        None => {
                            match draw_state {
                                DrawState::Normal => rectangle::State::Normal,
                                DrawState::Highlighted(idx, _) => {
                                    if i == idx { rectangle::State::Highlighted }
                                    else { rectangle::State::Normal }
                                },
                                DrawState::Clicked(idx, _) => {
                                    if i == idx { rectangle::State::Clicked }
                                    else { rectangle::State::Normal }
                                },
                            }
                        },
                        Some(sel_idx) => {
                            if sel_idx == i { rectangle::State::Clicked }
                            else {
                                match draw_state {
                                    DrawState::Normal => rectangle::State::Normal,
                                    DrawState::Highlighted(idx, _) => {
                                        if i == idx { rectangle::State::Highlighted }
                                        else { rectangle::State::Normal }
                                    },
                                    DrawState::Clicked(idx, _) => {
                                        if i == idx { rectangle::State::Clicked }
                                        else { rectangle::State::Normal }
                                    },
                                }
                            }
                        },
                    };
                    let idx_y = self.dim[1] * i as f64 - i as f64 * frame_w;
                    let idx_pos = vec2_add(self.pos, [0.0, idx_y]);
                    rectangle::draw_with_centered_label(
                        uic.win_w, uic.win_h, graphics, uic, rect_state, idx_pos,
                        self.dim, maybe_frame, color, &string,
                        t_size, t_color
                    )
                }
            },

        }

        set_state(uic, self.ui_id, Widget::DropDownList(new_state), self.pos, self.dim);

    }
}
<commit_msg>Implemented `Frameable` for `DropDownList`<commit_after>use frame::Frameable;
use color::{ Color, Colorable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use rectangle;
use ui_context::{ UIID, UiContext };
use vecmath::vec2_add;
use graphics::Graphics;
use graphics::character::CharacterCache;
use widget::Widget;

/// Tuple / Callback params.
pub type Idx = usize;
pub type Len = usize;

/// Represents the state of the menu.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Closed(DrawState),
    Open(DrawState),
}

/// Represents the state of the DropDownList widget.
#[derive(PartialEq, Clone, Copy)]
pub enum DrawState {
    Normal,
    Highlighted(Idx, Len),
    Clicked(Idx, Len),
}

impl DrawState {
    /// Translate the DropDownList's DrawState to the equivalent rectangle::State.
    fn as_rect_state(&self) -> rectangle::State {
        match self {
            &DrawState::Normal => rectangle::State::Normal,
            &DrawState::Highlighted(_, _) => rectangle::State::Highlighted,
            &DrawState::Clicked(_, _) => rectangle::State::Clicked,
        }
    }
}

impl State {
    /// Translate the DropDownList's State to the equivalent rectangle::State.
    fn as_rect_state(&self) -> rectangle::State {
        match self {
            &State::Open(draw_state) | &State::Closed(draw_state) => draw_state.as_rect_state(),
        }
    }
}

widget_fns!(DropDownList, State, Widget::DropDownList(State::Closed(DrawState::Normal)));

/// Is the cursor currently over the widget? If so which item?
fn is_over(pos: Point,
           mouse_pos: Point,
           dim: Dimensions,
           state: State,
           len: Len) -> Option<Idx> {
    match state {
        State::Closed(_) => {
            match rectangle::is_over(pos, mouse_pos, dim) {
                false => None,
                true => Some(0),
            }
        },
        State::Open(_) => {
            let total_h = dim[1] * len as f64;
            match rectangle::is_over(pos, mouse_pos, [dim[0], total_h]) {
                false => None,
                true => Some((((mouse_pos[1] - pos[1]) / total_h) * len as f64) as usize),
            }
        },
    }
}

/// Determine and return the new State by comparing the mouse state
/// and position to the previous State.
fn get_new_state(is_over_idx: Option<Idx>,
                 len: Len,
                 state: State,
                 mouse: Mouse) -> State {
    use self::DrawState::{Normal, Clicked, Highlighted};
    use mouse::ButtonState::{Down, Up};
    match state {
        State::Closed(draw_state) => {
            match is_over_idx {
                Some(_) => {
                    match (draw_state, mouse.left) {
                        (Normal,            Down) => State::Closed(Normal),
                        (Normal,            Up)   |
                        (Highlighted(_, _), Up)   => State::Closed(Highlighted(0, len)),
                        (Highlighted(_, _), Down) => State::Closed(Clicked(0, len)),
                        (Clicked(_, _),     Down) => State::Closed(Clicked(0, len)),
                        (Clicked(_, _),     Up)   => State::Open(Normal),
                    }
                },
                None => State::Closed(Normal),
            }
        },
        State::Open(draw_state) => {
            match is_over_idx {
                Some(idx) => {
                    match (draw_state, mouse.left) {
                        (Normal,            Down) => State::Open(Normal),
                        (Normal,            Up)   |
                        (Highlighted(_, _), Up)   => State::Open(Highlighted(idx, len)),
                        (Highlighted(_, _), Down) => State::Open(Clicked(idx, len)),
                        (Clicked(p_idx, _), Down) => State::Open(Clicked(p_idx, len)),
                        (Clicked(_, _),     Up)   => State::Closed(Normal),
                    }
                },
                None => {
                    match (draw_state, mouse.left) {
                        (Highlighted(p_idx, _), Up) => State::Open(Highlighted(p_idx, len)),
                        _ => State::Closed(Normal),
                    }
                },
            }
        }
    }
}

/// A context on which the builder pattern can be implemented.
pub struct DropDownList<'a, F> {
    ui_id: UIID,
    strings: &'a mut Vec<String>,
    selected: &'a mut Option<Idx>,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
}

impl<'a, F> DropDownList<'a, F> {
    pub fn new(ui_id: UIID,
               strings: &'a mut Vec<String>,
               selected: &'a mut Option<Idx>) -> DropDownList<'a, F> {
        DropDownList {
            ui_id: ui_id,
            strings: strings,
            selected: selected,
            pos: [0.0, 0.0],
            dim: [128.0, 32.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
        }
    }
}

impl<'a, F> Colorable for DropDownList<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, F> Frameable for DropDownList<'a, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

/*
quack! {
    list: DropDownList['a, F]
    get:
        fn () -> Size [] { Size(list.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(
                Widget::DropDownList(State::Closed(DrawState::Normal))
            )
        }
        fn () -> Id [] { Id(list.ui_id) }
    set:
        fn (val: Callback<F>) [where F: FnMut(&mut Option<Idx>, Idx, String) + 'a] {
            list.maybe_callback = Some(val.0)
        }
        fn (val: LabelText<'a>) [] { list.maybe_label = Some(val.0) }
        fn (val: LabelColor) [] { list.maybe_label_color = Some(val.0) }
        fn (val: LabelFontSize) [] { list.maybe_label_font_size = Some(val.0) }
        fn (val: Position) [] { list.pos = val.0 }
        fn (val: Size) [] { list.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for DropDownList<'a, F>
    where
        F: FnMut(&mut Option<Idx>, Idx, String) + 'a
{

    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {

        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over_idx = is_over(self.pos, mouse.pos, self.dim, state, self.strings.len());
        let new_state = get_new_state(is_over_idx, self.strings.len(), state, mouse);

        let sel = match *self.selected {
            Some(idx) if idx < self.strings.len() => { Some(idx) },
            _ => None,
        };
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let t_size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
        let t_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);

        // Call the `callback` closure if mouse was released
        // on one of the DropDownMenu items.
        match (state, new_state) {
            (State::Open(o_d_state), State::Closed(c_d_state)) => {
                match (o_d_state, c_d_state) {
                    (DrawState::Clicked(idx, _), DrawState::Normal) => {
                        match self.maybe_callback {
                            Some(ref mut callback) => (*callback)(self.selected, idx, (*self.strings)[idx].clone()),
                            None => (),
                        }
                    }, _ => (),
                }
            }, _ => (),
        }

        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };

        match new_state {

            State::Closed(_) => {
                let rect_state = new_state.as_rect_state();
                let text = match sel {
                    Some(idx) => &(*self.strings)[idx][..],
                    None => match self.maybe_label {
                        Some(text) => text,
                        None => &(*self.strings)[0][..],
                    },
                };
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, t_size, t_color
                )
            },

            State::Open(draw_state) => {
                for (i, string) in self.strings.iter().enumerate() {
                    let rect_state = match sel {
                        None => {
                            match draw_state {
                                DrawState::Normal => rectangle::State::Normal,
                                DrawState::Highlighted(idx, _) => {
                                    if i == idx { rectangle::State::Highlighted }
                                    else { rectangle::State::Normal }
                                },
                                DrawState::Clicked(idx, _) => {
                                    if i == idx { rectangle::State::Clicked }
                                    else { rectangle::State::Normal }
                                },
                            }
                        },
                        Some(sel_idx) => {
                            if sel_idx == i { rectangle::State::Clicked }
                            else {
                                match draw_state {
                                    DrawState::Normal => rectangle::State::Normal,
                                    DrawState::Highlighted(idx, _) => {
                                        if i == idx { rectangle::State::Highlighted }
                                        else { rectangle::State::Normal }
                                    },
                                    DrawState::Clicked(idx, _) => {
                                        if i == idx { rectangle::State::Clicked }
                                        else { rectangle::State::Normal }
                                    },
                                }
                            }
                        },
                    };
                    let idx_y = self.dim[1] * i as f64 - i as f64 * frame_w;
                    let idx_pos = vec2_add(self.pos, [0.0, idx_y]);
                    rectangle::draw_with_centered_label(
                        uic.win_w, uic.win_h, graphics, uic, rect_state, idx_pos,
                        self.dim, maybe_frame, color, &string,
                        t_size, t_color
                    )
                }
            },

        }

        set_state(uic, self.ui_id, Widget::DropDownList(new_state), self.pos, self.dim);

    }
}
<|endoftext|>


<commit_before>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::get_chat_action;
use bot::parse_mode::get_parse_mode;
use error::{Result, check_for_error};
use error::Error::{JsonNotFound, RequestFailed};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};

use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        debug!("Going to construct a new Bot...");
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
               id: id,
               first_name: first_name,
               last_name: last_name,
               username: username,
               client: client,
               bot_url: bot_url,
           })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<f32>)
                       -> Result<Option<Vec<Update>>> {
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        let network_delay = network_delay.unwrap_or(0.0);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let url = format!("{}?offset={}&limit={}&timeout={}&network_delay={}",
                          path_url,
                          offset,
                          limit,
                          timeout,
                          network_delay);
        let mut data = self.client.get(&url).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str =
            &disable_web_page_preview.unwrap_or(&false).to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup =
            &reply_markup
                 .map(|r| serde_json::to_string(r).unwrap_or("".to_string()))
                 .unwrap_or("".to_string());

        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_message(&self,
                           update: &Update,
                           chat_id: &i32,
                           disable_notification: Option<&bool>)
                           -> Result<Message> {
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will show the given chat action to the users.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id), ("action", action)];
        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let chat_action_succeeded: bool = serde_json::from_value(result_json.clone())?;
        Ok(chat_action_succeeded)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup = &reply_markup
                                .and_then(|r| serde_json::to_string(r).ok())
                                .unwrap_or("".to_string());
        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<commit_msg>Adding debug log to all bot actions<commit_after>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::get_chat_action;
use bot::parse_mode::get_parse_mode;
use error::{Result, check_for_error};
use error::Error::{JsonNotFound, RequestFailed};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};

use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        debug!("Going to construct a new Bot...");
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
               id: id,
               first_name: first_name,
               last_name: last_name,
               username: username,
               client: client,
               bot_url: bot_url,
           })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        debug!("Calling get_me...");
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<f32>)
                       -> Result<Option<Vec<Update>>> {
        debug!("Calling get_updates...");
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        let network_delay = network_delay.unwrap_or(0.0);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let url = format!("{}?offset={}&limit={}&timeout={}&network_delay={}",
                          path_url,
                          offset,
                          limit,
                          timeout,
                          network_delay);
        let mut data = self.client.get(&url).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        debug!("Calling send_message...");
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str =
            &disable_web_page_preview.unwrap_or(&false).to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup =
            &reply_markup
                 .map(|r| serde_json::to_string(r).unwrap_or("".to_string()))
                 .unwrap_or("".to_string());

        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        debug!("Calling reply_to_message...");
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_message(&self,
                           update: &Update,
                           chat_id: &i32,
                           disable_notification: Option<&bool>)
                           -> Result<Message> {
        debug!("Calling forward_message...");
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will show the given chat action to the users.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        debug!("Calling send_chat_action...");
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id), ("action", action)];
        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let chat_action_succeeded: bool = serde_json::from_value(result_json.clone())?;
        Ok(chat_action_succeeded)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        debug!("Calling send_contact...");
        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup = &reply_markup
                                .and_then(|r| serde_json::to_string(r).ok())
                                .unwrap_or("".to_string());
        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        debug!("Posting message...");
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Rendering time meter
//!
//! Used to track rendering times and provide moving averages.
//!
//! # Examples
//!
//! ```rust
//! // create a meter
//! let mut meter = alacritty::meter::Meter::new();
//!
//! // Sample something.
//! {
//!     let _sampler = meter.sampler();
//! }
//!
//! // Get the moving average. The meter tracks a fixed number of samles, and
//! // the average won't mean much until it's filled up at least once.
//! println!("Average time: {}", meter.average());

use std::time::{Instant, Duration};

const NUM_SAMPLES: usize = 10;

/// The meter
#[derive(Default)]
pub struct Meter {
    /// Track last 60 timestamps
    times: [f64; NUM_SAMPLES],

    /// Average sample time in microseconds
    avg: f64,

    /// Index of next time to update.
    index: usize,
}

/// Sampler
///
/// Samplers record how long they are "alive" for and update the meter on drop.
pub struct Sampler<'a> {
    /// Reference to meter that created the sampler
    meter: &'a mut Meter,

    // When the sampler was created
    created_at: Instant,
}

impl<'a> Sampler<'a> {
    fn new(meter: &'a mut Meter) -> Sampler<'a> {
        Sampler {
            meter: meter,
            created_at: Instant::now(),
        }
    }

    #[inline]
    fn alive_duration(&self) -> Duration {
        self.created_at.elapsed()
    }
}

impl<'a> Drop for Sampler<'a> {
    fn drop(&mut self) {
        // Work around borrowck
        let duration = self.alive_duration();
        self.meter.add_sample(duration);
    }
}

impl Meter {
    /// Create a meter
    pub fn new() -> Meter {
        Default::default()
    }

    /// Get a sampler
    pub fn sampler(&mut self) -> Sampler {
        Sampler::new(self)
    }

    /// Get the current average sample duration in microseconds
    pub fn average(&self) -> f64 {
        self.avg
    }

    /// Add a sample
    ///
    /// Used by Sampler::drop.
    fn add_sample(&mut self, sample: Duration) {
        let mut usec = 0f64;

        usec += (sample.subsec_nanos() as f64) / 1e3;
        usec += (sample.as_secs() as f64) * 1e6;

        let prev = self.times[self.index];
        self.times[self.index] = usec;
        self.avg -= prev / NUM_SAMPLES as f64;
        self.avg += usec / NUM_SAMPLES as f64;
        self.index = (self.index + 1) % NUM_SAMPLES;
    }
}
<commit_msg>Fix typo in rustdoc (#654)<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Rendering time meter
//!
//! Used to track rendering times and provide moving averages.
//!
//! # Examples
//!
//! ```rust
//! // create a meter
//! let mut meter = alacritty::meter::Meter::new();
//!
//! // Sample something.
//! {
//!     let _sampler = meter.sampler();
//! }
//!
//! // Get the moving average. The meter tracks a fixed number of samples, and
//! // the average won't mean much until it's filled up at least once.
//! println!("Average time: {}", meter.average());

use std::time::{Instant, Duration};

const NUM_SAMPLES: usize = 10;

/// The meter
#[derive(Default)]
pub struct Meter {
    /// Track last 60 timestamps
    times: [f64; NUM_SAMPLES],

    /// Average sample time in microseconds
    avg: f64,

    /// Index of next time to update.
    index: usize,
}

/// Sampler
///
/// Samplers record how long they are "alive" for and update the meter on drop.
pub struct Sampler<'a> {
    /// Reference to meter that created the sampler
    meter: &'a mut Meter,

    // When the sampler was created
    created_at: Instant,
}

impl<'a> Sampler<'a> {
    fn new(meter: &'a mut Meter) -> Sampler<'a> {
        Sampler {
            meter: meter,
            created_at: Instant::now(),
        }
    }

    #[inline]
    fn alive_duration(&self) -> Duration {
        self.created_at.elapsed()
    }
}

impl<'a> Drop for Sampler<'a> {
    fn drop(&mut self) {
        // Work around borrowck
        let duration = self.alive_duration();
        self.meter.add_sample(duration);
    }
}

impl Meter {
    /// Create a meter
    pub fn new() -> Meter {
        Default::default()
    }

    /// Get a sampler
    pub fn sampler(&mut self) -> Sampler {
        Sampler::new(self)
    }

    /// Get the current average sample duration in microseconds
    pub fn average(&self) -> f64 {
        self.avg
    }

    /// Add a sample
    ///
    /// Used by Sampler::drop.
    fn add_sample(&mut self, sample: Duration) {
        let mut usec = 0f64;

        usec += (sample.subsec_nanos() as f64) / 1e3;
        usec += (sample.as_secs() as f64) * 1e6;

        let prev = self.times[self.index];
        self.times[self.index] = usec;
        self.avg -= prev / NUM_SAMPLES as f64;
        self.avg += usec / NUM_SAMPLES as f64;
        self.index = (self.index + 1) % NUM_SAMPLES;
    }
}
<|endoftext|>


<commit_before>/// Filters operating on string
use std::collections::HashMap;

use serde_json::value::{Value, to_value};

use errors::{TeraResult, TeraError};

/// Convert a value to uppercase.
pub fn upper(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("upper", "value", String, value);

    Ok(to_value(&s.to_uppercase()))
}

/// Convert a value to lowercase.
pub fn lower(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("lower", "value", String, value);

    Ok(to_value(&s.to_lowercase()))
}

/// Strip leading and trailing whitespace.
pub fn trim(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("trim", "value", String, value);

    Ok(to_value(&s.trim()))
}

/// Truncates a string to the indicated length
pub fn truncate(value: Value, args: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("truncate", "value", String, value);
    let length = match args.get("length") {
        Some(l) => try_get_value!("truncate", "length", usize, l.clone()),
        None => 255
    };

    // Nothing to truncate?
    if length > s.len() {
        return Ok(to_value(&s));
    }

    let result = s[..s.char_indices().nth(length).unwrap().0].to_string() + "…";
    Ok(to_value(&result))
}

/// Gets the number of words in a string.
pub fn wordcount(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("wordcount", "value", String, value);

    Ok(to_value(&s.split_whitespace().count()))
}

/// Replaces given `from` substring with `to` string.
pub fn replace(value: Value, args: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("replace", "value", String, value);

    let from = match args.get("from") {
        Some(val) => try_get_value!("replace", "from", String, val.clone()),
        None => {
            return Err(TeraError::FilterMissingArg("replace".to_string(), "from".to_string()));
        }
    };

    let to = match args.get("to") {
        Some(val) => try_get_value!("replace", "to", String, val.clone()),
        None => {
            return Err(TeraError::FilterMissingArg("replace".to_string(), "to".to_string()));
        }
    };

    Ok(to_value(&s.replace(&from, &to)))
}

/// First letter of the string is uppercase rest is lowercase
pub fn capitalize(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("capitalize", "value", String, value);
    let mut chars = s.chars();
    match chars.next() {
        None => Ok(to_value("")),
        Some(f) => {
            let res = f.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase();
            Ok(to_value(&res))
        }
    }
}

/// Escapes quote chracters
pub fn addslashes(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("addslashes", "value", String, value);
    let result = s.replace("\\","\\\\").replace("\"", "\\\"");
    Ok(to_value(&result.replace("\'", "\\\'")))

}

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

    use serde_json::value::{to_value};

    use errors::TeraError::*;

    use super::*;

    #[test]
    fn test_upper() {
        let result = upper(to_value("hello"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("HELLO"));
    }

    #[test]
    fn test_upper_error() {
        let result = upper(to_value(&50), HashMap::new());
        assert!(result.is_err());
        assert_eq!(
            result.err().unwrap(),
            FilterIncorrectArgType("upper".to_string(), "value".to_string(), to_value(&50), "String".to_string())
        );
    }

    #[test]
    fn test_trim() {
        let result = trim(to_value("  hello  "), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_truncate_smaller_than_length() {
        let mut args = HashMap::new();
        args.insert("length".to_string(), to_value(&255));
        let result = truncate(to_value("hello"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_truncate_when_required() {
        let mut args = HashMap::new();
        args.insert("length".to_string(), to_value(&2));
        let result = truncate(to_value("日本語"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("日本…"));
    }

    #[test]
    fn test_lower() {
        let result = lower(to_value("HELLO"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_wordcount() {
        let result = wordcount(to_value("Joel is a slug"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(&4));
    }

    #[test]
    fn test_replace() {
        let mut args = HashMap::new();
        args.insert("from".to_string(), to_value(&"Hello"));
        args.insert("to".to_string(), to_value(&"Goodbye"));
        let result = replace(to_value(&"Hello world!"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("Goodbye world!"));
    }

    #[test]
    fn test_replace_missing_arg() {
        let mut args = HashMap::new();
        args.insert("from".to_string(), to_value(&"Hello"));
        let result = replace(to_value(&"Hello world!"), args);
        assert!(result.is_err());
        assert_eq!(
            result.err().unwrap(),
            FilterMissingArg("replace".to_string(), "to".to_string())
        );
    }

    #[test]
    fn test_capitalize() {
        let tests = vec![
            ("CAPITAL IZE", "Capital ize"),
            ("capital ize", "Capital ize"),
        ];
        for (input, expected) in tests {
            let result = capitalize(to_value(input), HashMap::new());
            assert!(result.is_ok());
            assert_eq!(result.unwrap(), to_value(expected));
        }
    }

    #[test]
    fn test_addslashes() {
        let tests = vec![
            (r#"I'm so happy"#, r#"I\'m so happy"#),
            (r#"Let "me" help you"#, r#"Let \"me\" help you"#),
            (r#"<a>'"#, r#"<a>\'"#),
            (r#""double quotes" and \'single quotes\'"#, r#"\"double quotes\" and \\\'single quotes\\\'"#),
            (r#"\ : backslashes too"#, r#"\\ : backslashes too"#)
        ];
        for (input, expected) in tests {
            let result = addslashes(to_value(input), HashMap::new());
            assert!(result.is_ok());
            assert_eq!(result.unwrap(), to_value(expected));
        }
    }
}
<commit_msg>one liner for addslashes<commit_after>/// Filters operating on string
use std::collections::HashMap;

use serde_json::value::{Value, to_value};

use errors::{TeraResult, TeraError};

/// Convert a value to uppercase.
pub fn upper(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("upper", "value", String, value);

    Ok(to_value(&s.to_uppercase()))
}

/// Convert a value to lowercase.
pub fn lower(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("lower", "value", String, value);

    Ok(to_value(&s.to_lowercase()))
}

/// Strip leading and trailing whitespace.
pub fn trim(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("trim", "value", String, value);

    Ok(to_value(&s.trim()))
}

/// Truncates a string to the indicated length
pub fn truncate(value: Value, args: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("truncate", "value", String, value);
    let length = match args.get("length") {
        Some(l) => try_get_value!("truncate", "length", usize, l.clone()),
        None => 255
    };

    // Nothing to truncate?
    if length > s.len() {
        return Ok(to_value(&s));
    }

    let result = s[..s.char_indices().nth(length).unwrap().0].to_string() + "…";
    Ok(to_value(&result))
}

/// Gets the number of words in a string.
pub fn wordcount(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("wordcount", "value", String, value);

    Ok(to_value(&s.split_whitespace().count()))
}

/// Replaces given `from` substring with `to` string.
pub fn replace(value: Value, args: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("replace", "value", String, value);

    let from = match args.get("from") {
        Some(val) => try_get_value!("replace", "from", String, val.clone()),
        None => {
            return Err(TeraError::FilterMissingArg("replace".to_string(), "from".to_string()));
        }
    };

    let to = match args.get("to") {
        Some(val) => try_get_value!("replace", "to", String, val.clone()),
        None => {
            return Err(TeraError::FilterMissingArg("replace".to_string(), "to".to_string()));
        }
    };

    Ok(to_value(&s.replace(&from, &to)))
}

/// First letter of the string is uppercase rest is lowercase
pub fn capitalize(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("capitalize", "value", String, value);
    let mut chars = s.chars();
    match chars.next() {
        None => Ok(to_value("")),
        Some(f) => {
            let res = f.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase();
            Ok(to_value(&res))
        }
    }
}

/// Escapes quote chracters
pub fn addslashes(value: Value, _: HashMap<String, Value>) -> TeraResult<Value> {
    let s = try_get_value!("addslashes", "value", String, value);
    Ok(to_value(&s.replace("\\","\\\\").replace("\"", "\\\"").replace("\'", "\\\'")))
}

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

    use serde_json::value::{to_value};

    use errors::TeraError::*;

    use super::*;

    #[test]
    fn test_upper() {
        let result = upper(to_value("hello"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("HELLO"));
    }

    #[test]
    fn test_upper_error() {
        let result = upper(to_value(&50), HashMap::new());
        assert!(result.is_err());
        assert_eq!(
            result.err().unwrap(),
            FilterIncorrectArgType("upper".to_string(), "value".to_string(), to_value(&50), "String".to_string())
        );
    }

    #[test]
    fn test_trim() {
        let result = trim(to_value("  hello  "), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_truncate_smaller_than_length() {
        let mut args = HashMap::new();
        args.insert("length".to_string(), to_value(&255));
        let result = truncate(to_value("hello"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_truncate_when_required() {
        let mut args = HashMap::new();
        args.insert("length".to_string(), to_value(&2));
        let result = truncate(to_value("日本語"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("日本…"));
    }

    #[test]
    fn test_lower() {
        let result = lower(to_value("HELLO"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("hello"));
    }

    #[test]
    fn test_wordcount() {
        let result = wordcount(to_value("Joel is a slug"), HashMap::new());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value(&4));
    }

    #[test]
    fn test_replace() {
        let mut args = HashMap::new();
        args.insert("from".to_string(), to_value(&"Hello"));
        args.insert("to".to_string(), to_value(&"Goodbye"));
        let result = replace(to_value(&"Hello world!"), args);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), to_value("Goodbye world!"));
    }

    #[test]
    fn test_replace_missing_arg() {
        let mut args = HashMap::new();
        args.insert("from".to_string(), to_value(&"Hello"));
        let result = replace(to_value(&"Hello world!"), args);
        assert!(result.is_err());
        assert_eq!(
            result.err().unwrap(),
            FilterMissingArg("replace".to_string(), "to".to_string())
        );
    }

    #[test]
    fn test_capitalize() {
        let tests = vec![
            ("CAPITAL IZE", "Capital ize"),
            ("capital ize", "Capital ize"),
        ];
        for (input, expected) in tests {
            let result = capitalize(to_value(input), HashMap::new());
            assert!(result.is_ok());
            assert_eq!(result.unwrap(), to_value(expected));
        }
    }

    #[test]
    fn test_addslashes() {
        let tests = vec![
            (r#"I'm so happy"#, r#"I\'m so happy"#),
            (r#"Let "me" help you"#, r#"Let \"me\" help you"#),
            (r#"<a>'"#, r#"<a>\'"#),
            (r#""double quotes" and \'single quotes\'"#, r#"\"double quotes\" and \\\'single quotes\\\'"#),
            (r#"\ : backslashes too"#, r#"\\ : backslashes too"#)
        ];
        for (input, expected) in tests {
            let result = addslashes(to_value(input), HashMap::new());
            assert!(result.is_ok());
            assert_eq!(result.unwrap(), to_value(expected));
        }
    }
}
<|endoftext|>


<commit_before>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};
use reply::ReplySender;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                      target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use std::fs::canonicalize() once stable (rust-lang/rust#27706)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| CString::new(s.as_bytes()).unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
#[derive(Debug)]
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy, Debug)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

impl ReplySender for ChannelSender {
    fn send(&self, data: &[&[u8]]) {
        if let Err(err) = ChannelSender::send(self, data) {
            error!("Failed to send FUSE reply: {}", err);
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.

    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
              target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }

    #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int {
        use fuse::fuse_unmount_compat22;
        use std::io::ErrorKind::PermissionDenied;

        let rc = unsafe { libc::umount(mnt.as_ptr()) };
        if rc < 0 && io::Error::last_os_error().kind() == PermissionDenied {
            // Linux always returns EPERM for non-root users.  We have to let the
            // library go through the setuid-root "fusermount -u" to unmount.
            unsafe { fuse_unmount_compat22(mnt.as_ptr()); }
            0
        } else {
            rc
        }
    }

    let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<commit_msg>Use Rust's Path::canonicalize instead of libc::realpath<commit_after>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};
use reply::ReplySender;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                      target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))]
        pub fn umount(dir: *const c_char) -> c_int;
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| CString::new(s.as_bytes()).unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
#[derive(Debug)]
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mountpoint = try!(mountpoint.canonicalize());
        with_fuse_args(options, |args| {
            let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
            let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
            if fd < 0 {
                Err(io::Error::last_os_error())
            } else {
                Ok(Channel { mountpoint: mountpoint, fd: fd })
            }
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy, Debug)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

impl ReplySender for ChannelSender {
    fn send(&self, data: &[&[u8]]) {
        if let Err(err) = ChannelSender::send(self, data) {
            error!("Failed to send FUSE reply: {}", err);
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.

    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
              target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }

    #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int {
        use fuse::fuse_unmount_compat22;
        use std::io::ErrorKind::PermissionDenied;

        let rc = unsafe { libc::umount(mnt.as_ptr()) };
        if rc < 0 && io::Error::last_os_error().kind() == PermissionDenied {
            // Linux always returns EPERM for non-root users.  We have to let the
            // library go through the setuid-root "fusermount -u" to unmount.
            unsafe { fuse_unmount_compat22(mnt.as_ptr()); }
            0
        } else {
            rc
        }
    }

    let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<|endoftext|>


<commit_before>pub mod xattr;
pub mod ignore;

#[cfg(feature="git")] pub mod git;

#[cfg(not(feature="git"))]
pub mod git {
    use std::iter::FromIterator;
    use std::path::{Path, PathBuf};

    use fs::fields as f;

    pub struct GitCache;

    impl FromIterator<PathBuf> for GitCache {
        fn from_iter<I: IntoIterator<Item=PathBuf>>(_iter: I) -> Self {
            GitCache
        }
    }

    impl GitCache {
        pub fn has_anything_for(&self, _index: &Path) -> bool {
            false
        }

        pub fn get(&self, _index: &Path, _prefix_lookup: bool) -> f::Git {
            panic!("Tried to query a Git cache, but Git support is disabled")
        }
    }
}
<commit_msg>Fix build without default features<commit_after>pub mod xattr;
pub mod ignore;

#[cfg(feature="git")] pub mod git;

#[cfg(not(feature="git"))]
pub mod git {
    use std::iter::FromIterator;
    use std::path::{Path, PathBuf};

    use crate::fs::fields as f;

    pub struct GitCache;

    impl FromIterator<PathBuf> for GitCache {
        fn from_iter<I: IntoIterator<Item=PathBuf>>(_iter: I) -> Self {
            GitCache
        }
    }

    impl GitCache {
        pub fn has_anything_for(&self, _index: &Path) -> bool {
            false
        }

        pub fn get(&self, _index: &Path, _prefix_lookup: bool) -> f::Git {
            panic!("Tried to query a Git cache, but Git support is disabled")
        }
    }
}
<|endoftext|>