<commit_before>use std::{
    collections::BTreeMap,
    convert::{TryFrom, TryInto},
    fmt,
};

use js_int::Int;
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use serde_json::{to_string as to_json_string, Value as JsonValue};

use super::Error;

/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;

/// Represents a canonical JSON value as per the Matrix specification.
#[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue {
    /// Represents a JSON null value.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
    /// ```
    Null,

    /// Represents a JSON boolean.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
    /// ```
    Bool(bool),

    /// Represents a JSON integer.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
    /// ```
    Integer(Int),

    /// Represents a JSON string.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
    /// ```
    String(String),

    /// Represents a JSON array.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
    /// ```
    Array(Vec<CanonicalJsonValue>),

    /// Represents a JSON object.
    ///
    /// The map is backed by a BTreeMap to guarantee the sorting of keys.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
    /// ```
    Object(Object),
}

impl CanonicalJsonValue {
    /// If the `CanonicalJsonValue` is a `Bool`, return the inner value.
    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Self::Bool(b) => Some(*b),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Integer`, return the inner value.
    pub fn as_integer(&self) -> Option<Int> {
        match self {
            Self::Integer(i) => Some(*i),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is a `String`, return a reference to the inner value.
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::String(s) => Some(s),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a reference to the inner value.
    pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
    pub fn as_object(&self) -> Option<&Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a mutable reference to the inner value.
    pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `Bool`.
    pub fn is_bool(&self) -> bool {
        matches!(self, Self::Bool(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Integer`.
    pub fn is_integer(&self) -> bool {
        matches!(self, Self::Integer(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `String`.
    pub fn is_string(&self) -> bool {
        matches!(self, Self::String(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Array`.
    pub fn is_array(&self) -> bool {
        matches!(self, Self::Array(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Object`.
    pub fn is_object(&self) -> bool {
        matches!(self, Self::Object(_))
    }
}

impl Default for CanonicalJsonValue {
    fn default() -> Self {
        Self::Null
    }
}

impl fmt::Debug for CanonicalJsonValue {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Null => formatter.debug_tuple("Null").finish(),
            Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
            Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
            Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
            Self::Array(ref v) => {
                formatter.write_str("Array(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
            Self::Object(ref v) => {
                formatter.write_str("Object(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
        }
    }
}

impl fmt::Display for CanonicalJsonValue {
    /// Display this value as a string.
    ///
    /// This `Display` implementation is intentionally unaffected by any formatting parameters,
    /// because adding extra whitespace or otherwise pretty-printing it would make it not the
    /// canonical form anymore.
    ///
    /// If you want to pretty-print a `CanonicalJsonValue` for debugging purposes, use
    /// one of `serde_json::{to_string_pretty, to_vec_pretty, to_writer_pretty}`.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
    }
}

impl TryFrom<JsonValue> for CanonicalJsonValue {
    type Error = Error;

    fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
        Ok(match val {
            JsonValue::Bool(b) => Self::Bool(b),
            JsonValue::Number(num) => Self::Integer(
                Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
                    .map_err(|_| Error::IntConvert)?,
            ),
            JsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
            }
            JsonValue::String(string) => Self::String(string),
            JsonValue::Object(obj) => Self::Object(
                obj.into_iter()
                    .map(|(k, v)| Ok((k, v.try_into()?)))
                    .collect::<Result<Object, _>>()?,
            ),
            JsonValue::Null => Self::Null,
        })
    }
}

impl From<CanonicalJsonValue> for JsonValue {
    fn from(val: CanonicalJsonValue) -> Self {
        match val {
            CanonicalJsonValue::Bool(b) => Self::Bool(b),
            CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
            CanonicalJsonValue::String(string) => Self::String(string),
            CanonicalJsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(Into::into).collect())
            }
            CanonicalJsonValue::Object(obj) => {
                Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
            }
            CanonicalJsonValue::Null => Self::Null,
        }
    }
}

macro_rules! impl_partial_eq {
    ($variant:ident($ty:ty)) => {
        impl PartialEq<$ty> for CanonicalJsonValue {
            fn eq(&self, other: &$ty) -> bool {
                match self {
                    Self::$variant(val) => val == other,
                    _ => false,
                }
            }
        }

        impl PartialEq<CanonicalJsonValue> for $ty {
            fn eq(&self, other: &CanonicalJsonValue) -> bool {
                match other {
                    CanonicalJsonValue::$variant(val) => self == val,
                    _ => false,
                }
            }
        }
    };
}

impl_partial_eq!(Bool(bool));
impl_partial_eq!(Integer(Int));
impl_partial_eq!(String(String));
impl_partial_eq!(Array(Vec<CanonicalJsonValue>));
impl_partial_eq!(Object(Object));

impl Serialize for CanonicalJsonValue {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Null => serializer.serialize_unit(),
            Self::Bool(b) => serializer.serialize_bool(*b),
            Self::Integer(n) => n.serialize(serializer),
            Self::String(s) => serializer.serialize_str(s),
            Self::Array(v) => v.serialize(serializer),
            Self::Object(m) => {
                use serde::ser::SerializeMap;
                let mut map = serializer.serialize_map(Some(m.len()))?;
                for (k, v) in m {
                    map.serialize_entry(k, v)?;
                }
                map.end()
            }
        }
    }
}

impl<'de> Deserialize<'de> for CanonicalJsonValue {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = JsonValue::deserialize(deserializer)?;
        val.try_into().map_err(serde::de::Error::custom)
    }
}

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

    use serde_json::json;

    use super::CanonicalJsonValue;

    #[test]
    fn to_string() {
        const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;

        let json: CanonicalJsonValue =
            json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();

        assert_eq!(format!("{}", json), CANONICAL_STR);
        assert_eq!(format!("{:#}", json), CANONICAL_STR);
    }
}
<commit_msg>serde: Add From implementations for CanonicalJsonValue<commit_after>use std::{
    collections::BTreeMap,
    convert::{TryFrom, TryInto},
    fmt,
};

use js_int::Int;
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use serde_json::{to_string as to_json_string, Value as JsonValue};

use super::Error;

/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;

/// Represents a canonical JSON value as per the Matrix specification.
#[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue {
    /// Represents a JSON null value.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
    /// ```
    Null,

    /// Represents a JSON boolean.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
    /// ```
    Bool(bool),

    /// Represents a JSON integer.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
    /// ```
    Integer(Int),

    /// Represents a JSON string.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
    /// ```
    String(String),

    /// Represents a JSON array.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
    /// ```
    Array(Vec<CanonicalJsonValue>),

    /// Represents a JSON object.
    ///
    /// The map is backed by a BTreeMap to guarantee the sorting of keys.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
    /// ```
    Object(Object),
}

impl CanonicalJsonValue {
    /// If the `CanonicalJsonValue` is a `Bool`, return the inner value.
    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Self::Bool(b) => Some(*b),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Integer`, return the inner value.
    pub fn as_integer(&self) -> Option<Int> {
        match self {
            Self::Integer(i) => Some(*i),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is a `String`, return a reference to the inner value.
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::String(s) => Some(s),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a reference to the inner value.
    pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
    pub fn as_object(&self) -> Option<&Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a mutable reference to the inner value.
    pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `Bool`.
    pub fn is_bool(&self) -> bool {
        matches!(self, Self::Bool(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Integer`.
    pub fn is_integer(&self) -> bool {
        matches!(self, Self::Integer(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `String`.
    pub fn is_string(&self) -> bool {
        matches!(self, Self::String(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Array`.
    pub fn is_array(&self) -> bool {
        matches!(self, Self::Array(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Object`.
    pub fn is_object(&self) -> bool {
        matches!(self, Self::Object(_))
    }
}

impl Default for CanonicalJsonValue {
    fn default() -> Self {
        Self::Null
    }
}

impl fmt::Debug for CanonicalJsonValue {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Null => formatter.debug_tuple("Null").finish(),
            Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
            Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
            Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
            Self::Array(ref v) => {
                formatter.write_str("Array(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
            Self::Object(ref v) => {
                formatter.write_str("Object(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
        }
    }
}

impl fmt::Display for CanonicalJsonValue {
    /// Display this value as a string.
    ///
    /// This `Display` implementation is intentionally unaffected by any formatting parameters,
    /// because adding extra whitespace or otherwise pretty-printing it would make it not the
    /// canonical form anymore.
    ///
    /// If you want to pretty-print a `CanonicalJsonValue` for debugging purposes, use
    /// one of `serde_json::{to_string_pretty, to_vec_pretty, to_writer_pretty}`.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
    }
}

impl TryFrom<JsonValue> for CanonicalJsonValue {
    type Error = Error;

    fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
        Ok(match val {
            JsonValue::Bool(b) => Self::Bool(b),
            JsonValue::Number(num) => Self::Integer(
                Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
                    .map_err(|_| Error::IntConvert)?,
            ),
            JsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
            }
            JsonValue::String(string) => Self::String(string),
            JsonValue::Object(obj) => Self::Object(
                obj.into_iter()
                    .map(|(k, v)| Ok((k, v.try_into()?)))
                    .collect::<Result<Object, _>>()?,
            ),
            JsonValue::Null => Self::Null,
        })
    }
}

impl From<CanonicalJsonValue> for JsonValue {
    fn from(val: CanonicalJsonValue) -> Self {
        match val {
            CanonicalJsonValue::Bool(b) => Self::Bool(b),
            CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
            CanonicalJsonValue::String(string) => Self::String(string),
            CanonicalJsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(Into::into).collect())
            }
            CanonicalJsonValue::Object(obj) => {
                Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
            }
            CanonicalJsonValue::Null => Self::Null,
        }
    }
}

macro_rules! variant_impls {
    ($variant:ident($ty:ty)) => {
        impl From<$ty> for CanonicalJsonValue {
            fn from(val: $ty) -> Self {
                Self::$variant(val)
            }
        }

        impl PartialEq<$ty> for CanonicalJsonValue {
            fn eq(&self, other: &$ty) -> bool {
                match self {
                    Self::$variant(val) => val == other,
                    _ => false,
                }
            }
        }

        impl PartialEq<CanonicalJsonValue> for $ty {
            fn eq(&self, other: &CanonicalJsonValue) -> bool {
                match other {
                    CanonicalJsonValue::$variant(val) => self == val,
                    _ => false,
                }
            }
        }
    };
}

variant_impls!(Bool(bool));
variant_impls!(Integer(Int));
variant_impls!(String(String));
variant_impls!(Array(Vec<CanonicalJsonValue>));
variant_impls!(Object(Object));

impl Serialize for CanonicalJsonValue {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Null => serializer.serialize_unit(),
            Self::Bool(b) => serializer.serialize_bool(*b),
            Self::Integer(n) => n.serialize(serializer),
            Self::String(s) => serializer.serialize_str(s),
            Self::Array(v) => v.serialize(serializer),
            Self::Object(m) => {
                use serde::ser::SerializeMap;
                let mut map = serializer.serialize_map(Some(m.len()))?;
                for (k, v) in m {
                    map.serialize_entry(k, v)?;
                }
                map.end()
            }
        }
    }
}

impl<'de> Deserialize<'de> for CanonicalJsonValue {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = JsonValue::deserialize(deserializer)?;
        val.try_into().map_err(serde::de::Error::custom)
    }
}

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

    use serde_json::json;

    use super::CanonicalJsonValue;

    #[test]
    fn to_string() {
        const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;

        let json: CanonicalJsonValue =
            json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();

        assert_eq!(format!("{}", json), CANONICAL_STR);
        assert_eq!(format!("{:#}", json), CANONICAL_STR);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016-2018 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use glib::Cast;
use webkit2gtk_webextension::{
    DOMCSSStyleDeclarationExt,
    DOMDocumentExt,
    DOMDOMWindowExt,
    DOMElement,
    DOMElementExt,
    DOMNodeExt,
    WebPage,
};

use titanium_common::LAST_MARK;
use titanium_common::Percentage::{self, All, Percent};

use dom::{ElementIter, get_body, get_document};
use executor::Executor;

impl Executor {
    /// Initialize the scroll element.
    pub fn init_scroll_element(&mut self) {
        self.model.scroll_element = find_scrollable_element(&self.model.page);
    }

    /// Initialize the scroll element if needed.
    fn init_scroll_element_if_needed(&mut self) {
        if self.model.scroll_element.is_none() {
            // FIXME: if the page is not scrollable, no scrollable element is found.
            self.init_scroll_element();
        }
    }

    /// Reset the scroll element.
    pub fn reset_scroll_element(&mut self) {
        self.model.scroll_element = None;
    }

    /// Scroll the web page vertically by the specified amount of pixels.
    /// A negative value scroll towards to top.
    pub fn scroll_by(&mut self, pixels: i64) {
        self.init_scroll_element_if_needed();
        let element = wtry_opt_no_ret!(self.model.scroll_element.as_ref());
        element.set_scroll_top(element.get_scroll_top() + pixels);
    }

    /// Scroll the web page horizontally by the specified amount of pixels.
    /// A negative value scroll towards left.
    pub fn scroll_by_x(&mut self, pixels: i64) {
        self.init_scroll_element_if_needed();
        let element = wtry_opt_no_ret!(self.model.scroll_element.as_ref());
        element.set_scroll_left(element.get_scroll_left() + pixels);
    }

    /// Get the current vertical scroll position of the web page as a percentage.
    pub fn scroll_percentage(&mut self) -> Percentage {
        info!("scroll_percentage");
        let default = All;
        let element = unwrap_opt_or_ret!(self.model.scroll_element.as_ref(), default);
        let document = unwrap_opt_or_ret!(get_document(&self.model.page), default);
        let height = document.get_client_height();
        let scroll_height = element.get_scroll_height();
        info!("height: {}", height);
        info!("scroll_height: {}", scroll_height);
        if scroll_height <= height as i64 {
            info!("returned {:?}", default);
            default
        }
        else {
            info!("scroll_top: {}", element.get_scroll_top());
            Percent((element.get_scroll_top() as f64 / (scroll_height as f64 - height) * 100.0).round() as i64)
        }
    }

    /// Scroll to the top of the web page.
    pub fn scroll_top(&mut self) {
        self.init_scroll_element_if_needed();
        self.add_mark(LAST_MARK);
        let element = wtry_opt_no_ret!(self.model.scroll_element.as_ref());
        element.set_scroll_top(0);
    }

    /// Scroll to the specified percent of the web page.
    pub fn scroll_to_percent(&mut self, percent: u32) {
        self.init_scroll_element_if_needed();
        self.add_mark(LAST_MARK);
        let element = wtry_opt_no_ret!(self.model.scroll_element.as_ref());
        let document = wtry_opt_no_ret!(get_document(&self.model.page));
        let height = document.get_client_height() as i64;
        let scroll_height = document.get_scroll_height();
        let scroll_height = (percent as i64) * (scroll_height - height) / 100;
        element.set_scroll_top(scroll_height);
    }
}

pub fn find_scrollable_element(page: &WebPage) -> Option<DOMElement> {
    let body = wtry_opt!(get_body(page)).upcast();
    if body_scrollable(&body) {
        Some(body)
    }
    else {
        let children = ElementIter::new(body.get_children());
        let mut max_area = 0;
        let mut best_child = None;
        for child in children {
            if may_scroll(&child) {
                let area = child.get_client_width() as i64 * child.get_client_height() as i64;
                if area > max_area {
                    max_area = area;
                    best_child = Some(child);
                }
            }
        }
        if let Some(child) = best_child.take() {
            if is_scrollable(&child) {
                best_child = Some(child);
            }
            else {
                let children = ElementIter::new(child.get_children());
                for child in children {
                    // TODO: pick the biggest?
                    if is_scrollable(&child) {
                        best_child = Some(child);
                    }
                }
            }
        }
        Some(best_child.unwrap_or(body))
    }
}

fn body_scrollable(element: &DOMElement) -> bool {
    let document = unwrap_opt_or_ret!(element.get_owner_document(), false);
    let document = unwrap_opt_or_ret!(document.get_document_element(), false);
    let height = document.get_client_height() as i64;
    element.get_scroll_height() > height
}

fn is_scrollable(element: &DOMElement) -> bool {
    may_scroll(element) && element.get_scroll_height() > element.get_client_height() as i64
}

fn may_scroll(element: &DOMElement) -> bool {
    let document = unwrap_opt_or_ret!(element.get_owner_document(), false);
    let window = unwrap_opt_or_ret!(document.get_default_view(), false);
    let style = unwrap_opt_or_ret!(window.get_computed_style(element, None), false);
    let overflow_y = style.get_property_value("overflow-y");
    let overflow_y = unwrap_opt_or_ret!(overflow_y, false);
    overflow_y == "scroll" || overflow_y == "auto"
}
<commit_msg>Fix scrolling<commit_after>/*
 * Copyright (c) 2016-2018 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use glib::Cast;
use webkit2gtk_webextension::{
    DOMCSSStyleDeclarationExt,
    DOMDocumentExt,
    DOMDOMWindowExt,
    DOMElement,
    DOMElementExt,
    DOMNodeExt,
    WebPage,
    WebPageExt,
};

use titanium_common::LAST_MARK;
use titanium_common::Percentage::{self, All, Percent};

use dom::{ElementIter, get_body, get_document};
use executor::Executor;

impl Executor {
    /// Initialize the scroll element.
    pub fn init_scroll_element(&mut self) {
        self.model.scroll_element = find_scrollable_element(&self.model.page);
    }

    /// Initialize the scroll element if needed.
    fn init_scroll_element_if_needed(&mut self) {
        if self.model.scroll_element.is_none() {
            // FIXME: if the page is not scrollable, no scrollable element is found.
            self.init_scroll_element();
        }
    }

    /// Reset the scroll element.
    pub fn reset_scroll_element(&mut self) {
        self.model.scroll_element = None;
    }

    /// Scroll the web page vertically by the specified amount of pixels.
    /// A negative value scroll towards to top.
    pub fn scroll_by(&mut self, pixels: i64) {
        let document = wtry_opt_no_ret!(self.model.page.get_dom_document());
        let window = wtry_opt_no_ret!(document.get_default_view());
        window.scroll_by(0.0, pixels as f64);
    }

    /// Scroll the web page horizontally by the specified amount of pixels.
    /// A negative value scroll towards left.
    pub fn scroll_by_x(&mut self, pixels: i64) {
        let document = wtry_opt_no_ret!(self.model.page.get_dom_document());
        let window = wtry_opt_no_ret!(document.get_default_view());
        window.scroll_by(pixels as f64, 0.0);
    }

    /// Get the current vertical scroll position of the web page as a percentage.
    pub fn scroll_percentage(&mut self) -> Percentage {
        info!("scroll_percentage");
        let default = All;
        let document = unwrap_opt_or_ret!(self.model.page.get_dom_document(), default);
        let window = unwrap_opt_or_ret!(document.get_default_view(), default);
        let document = unwrap_opt_or_ret!(get_document(&self.model.page), default);
        let height = document.get_client_height();
        let scroll_height = document.get_scroll_height();
        info!("height: {}", height);
        info!("scroll_height: {}", scroll_height);
        if scroll_height <= height as i64 {
            info!("returned {:?}", default);
            default
        }
        else {
            info!("scroll_y: {}", window.get_scroll_y());
            Percent((window.get_scroll_y() as f64 / (scroll_height as f64 - height) * 100.0).round() as i64)
        }
    }

    /// Scroll to the top of the web page.
    pub fn scroll_top(&mut self) {
        self.add_mark(LAST_MARK);
        let document = wtry_opt_no_ret!(self.model.page.get_dom_document());
        let window = wtry_opt_no_ret!(document.get_default_view());
        window.scroll_to(0.0, 0.0);
    }

    /// Scroll to the specified percent of the web page.
    pub fn scroll_to_percent(&mut self, percent: u32) {
        self.add_mark(LAST_MARK);
        let document = wtry_opt_no_ret!(self.model.page.get_dom_document());
        let window = wtry_opt_no_ret!(document.get_default_view());
        let element = wtry_opt_no_ret!(self.model.scroll_element.as_ref());
        let document = wtry_opt_no_ret!(get_document(&self.model.page));
        let height = document.get_client_height() as i64;
        let scroll_height = document.get_scroll_height();
        let scroll_height = (percent as i64) * (scroll_height - height) / 100;
        window.scroll_to(0.0, scroll_height as f64);
    }
}

pub fn find_scrollable_element(page: &WebPage) -> Option<DOMElement> {
    let body = wtry_opt!(get_body(page)).upcast();
    if body_scrollable(&body) {
        Some(body)
    }
    else {
        let children = ElementIter::new(body.get_children());
        let mut max_area = 0;
        let mut best_child = None;
        for child in children {
            if may_scroll(&child) {
                let area = child.get_client_width() as i64 * child.get_client_height() as i64;
                if area > max_area {
                    max_area = area;
                    best_child = Some(child);
                }
            }
        }
        if let Some(child) = best_child.take() {
            if is_scrollable(&child) {
                best_child = Some(child);
            }
            else {
                let children = ElementIter::new(child.get_children());
                for child in children {
                    // TODO: pick the biggest?
                    if is_scrollable(&child) {
                        best_child = Some(child);
                    }
                }
            }
        }
        Some(best_child.unwrap_or(body))
    }
}

fn body_scrollable(element: &DOMElement) -> bool {
    let document = unwrap_opt_or_ret!(element.get_owner_document(), false);
    let document = unwrap_opt_or_ret!(document.get_document_element(), false);
    let height = document.get_client_height() as i64;
    element.get_scroll_height() > height
}

fn is_scrollable(element: &DOMElement) -> bool {
    may_scroll(element) && element.get_scroll_height() > element.get_client_height() as i64
}

fn may_scroll(element: &DOMElement) -> bool {
    let document = unwrap_opt_or_ret!(element.get_owner_document(), false);
    let window = unwrap_opt_or_ret!(document.get_default_view(), false);
    let style = unwrap_opt_or_ret!(window.get_computed_style(element, None), false);
    let overflow_y = style.get_property_value("overflow-y");
    let overflow_y = unwrap_opt_or_ret!(overflow_y, false);
    overflow_y == "scroll" || overflow_y == "auto"
}
<|endoftext|>


<commit_before>use super::ElfWord;

/// The type field of an ELF program header
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub enum Type {
    /// `PT_NULL`: Program header table entry unused
    Null = 0
  , /// `PT_LOAD`: Loadable program segment
    Loadable = 1
  , /// `PT_DYNAMIC`: Dynamic linking information
    Dynamic = 2
  , /// `PT_INTERP`: Program interpreter
    Interpreter = 3
  , /// `PT_NOTE`: Auxiliary information
    Note = 4
  , /// `PT_SHLIB`
    ShLib = 5
  , /// `PT_PHDR`: Program Header table
    HeaderTable = 6
  , /// `PT_TLS`: Thread-local storage
    ThreadLocal = 7
  , /// GCC `.eh_frame_hdr` segment
    GnuEhFrame = 0x6474e550
  , /// Indicates stack executability
    GnuStack = 0x6474e551
  , /// Read-only after relocation
    GnuRelRo = 0x6474e552
}

bitflags! {
    pub flags Flags: u32 {
        const NONE = 0
      , const EXECUTABLE = 1 << 0
      , const WRITABLE = 1 << 1
      , const READABLE = 1 << 2
    }
}

/// An ELF Program Header
#[derive(Copy, Clone, Debug)]
pub struct HeaderRepr64 {
    pub ty: Type
  , pub flags: Flags
  ,
}
<commit_msg>[elf] remove unused import<commit_after>/// The type field of an ELF program header
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub enum Type {
    /// `PT_NULL`: Program header table entry unused
    Null = 0
  , /// `PT_LOAD`: Loadable program segment
    Loadable = 1
  , /// `PT_DYNAMIC`: Dynamic linking information
    Dynamic = 2
  , /// `PT_INTERP`: Program interpreter
    Interpreter = 3
  , /// `PT_NOTE`: Auxiliary information
    Note = 4
  , /// `PT_SHLIB`
    ShLib = 5
  , /// `PT_PHDR`: Program Header table
    HeaderTable = 6
  , /// `PT_TLS`: Thread-local storage
    ThreadLocal = 7
  , /// GCC `.eh_frame_hdr` segment
    GnuEhFrame = 0x6474e550
  , /// Indicates stack executability
    GnuStack = 0x6474e551
  , /// Read-only after relocation
    GnuRelRo = 0x6474e552
}

bitflags! {
    pub flags Flags: u32 {
        const NONE = 0
      , const EXECUTABLE = 1 << 0
      , const WRITABLE = 1 << 1
      , const READABLE = 1 << 2
    }
}

/// An ELF Program Header
#[derive(Copy, Clone, Debug)]
pub struct HeaderRepr64 {
    pub ty: Type
  , pub flags: Flags
  ,
}
<|endoftext|>


<commit_before>#![allow(dead_code)] // false positive
use std::io::{Error, ErrorKind, Read, Result, Write};
use std::mem;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FilterType {
	None = 0,
	Sub = 1,
	Up = 2,
	Avg = 3,
	Paeth = 4,
}

impl FilterType {
	/// u8 -> Self. Temporary solution until Rust provides a canonical one.
	pub fn from_u8(n: u8) -> Option<FilterType> {
		match n {
			n if n <= 4 => Some(unsafe { mem::transmute(n) }),
			_ => None,
		}
	}
}

fn paeth_predict(left: u8, above: u8, upperleft: u8) -> u8 {
	let expand_left = i16::from(left);
	let expand_above = i16::from(above);
	let expand_upperleft = i16::from(upperleft);

	let initial_estimate = expand_left + expand_above - expand_upperleft;

	let dist_left = (initial_estimate - expand_left).abs();
	let dist_above = (initial_estimate - expand_above).abs();
	let dist_upperleft = (initial_estimate - expand_upperleft).abs();

	if dist_left <= dist_above && dist_left <= dist_upperleft {
		left
	} else if dist_above <= dist_upperleft {
		above
	} else {
		upperleft
	}
}

pub fn decode_row(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
	use self::FilterType::*;
	let len = current.len();

	match filter {
		None => (),
		Sub => {
			for i in bpp..len {
				current[i] = current[i].wrapping_add(current[i - bpp]);
			}
		}
		Up => {
			for i in 0..len {
				current[i] = current[i].wrapping_add(previous[i]);
			}
		}
		Avg => {
			for i in 0..bpp {
				current[i] = current[i].wrapping_add(previous[i] / 2);
			}

			for i in bpp..len {
				current[i] = current[i].wrapping_add((i16::from(current[i - bpp]) + i16::from(previous[i]) / 2) as u8);
			}
		}
		Paeth => {
			for i in 0..bpp {
				current[i] = current[i].wrapping_add(paeth_predict(0, previous[i], 0));
			}

			for i in bpp..len {
				current[i] = current[i].wrapping_add(paeth_predict(current[i - bpp], previous[i], previous[i - bpp]));
			}
		}
	}
}

pub fn decode_frame(content: &[u8], bytes_per_pixel: usize, pixels_per_row: usize) -> Result<Vec<u8>> {
	let bytes_per_row = bytes_per_pixel * pixels_per_row;
	let mut previous = vec![0_u8; bytes_per_row];
	let mut current = vec![0_u8; bytes_per_row];
	let mut decoded = Vec::new();
	let mut pos = 0;
	while pos < content.len() {
		if let Some(filter) = FilterType::from_u8(content[pos]) {
			pos += 1;
			(&content[pos..]).read_exact(current.as_mut_slice())?;
			pos += bytes_per_row;

			decode_row(filter, bytes_per_pixel, previous.as_slice(), current.as_mut_slice());
			decoded.write_all(current.as_slice())?;
			mem::swap(&mut previous, &mut current);
		} else {
			return Err(Error::new(ErrorKind::InvalidData, format!("invalid PNG filter type ({})", content[pos])));
		}
	}
	Ok(decoded)
}

pub fn encode_row(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
	use self::FilterType::*;
	let len = current.len();

	match method {
		None => (),
		Sub => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(current[i - bpp]);
			}
		}
		Up => {
			for i in 0..len {
				current[i] = current[i].wrapping_sub(previous[i]);
			}
		}
		Avg => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(current[i - bpp].wrapping_add(previous[i]) / 2);
			}

			for i in 0..bpp {
				current[i] = current[i].wrapping_sub(previous[i] / 2);
			}
		}
		Paeth => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(paeth_predict(current[i - bpp], previous[i], previous[i - bpp]));
			}

			for i in 0..bpp {
				current[i] = current[i].wrapping_sub(paeth_predict(0, previous[i], 0));
			}
		}
	}
}
<commit_msg>Remove unsafe code around FilterType.<commit_after>#![allow(dead_code)] // false positive
use std::convert::{TryInto, TryFrom};
use std::io::{Error, ErrorKind, Read, Result, Write};
use std::mem;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterType {
	None,
	Sub,
	Up,
	Avg,
	Paeth,
}

impl TryFrom<u8> for FilterType {
	type Error = ();

	fn try_from(n: u8) -> std::result::Result<FilterType, ()> {
		match n {
			0 => Ok(FilterType::None),
			1 => Ok(FilterType::Sub),
			2 => Ok(FilterType::Up),
			3 => Ok(FilterType::Avg),
			4 => Ok(FilterType::Paeth),
			_ => Err(()),
		}
	}
}

fn paeth_predict(left: u8, above: u8, upperleft: u8) -> u8 {
	let expand_left = i16::from(left);
	let expand_above = i16::from(above);
	let expand_upperleft = i16::from(upperleft);

	let initial_estimate = expand_left + expand_above - expand_upperleft;

	let dist_left = (initial_estimate - expand_left).abs();
	let dist_above = (initial_estimate - expand_above).abs();
	let dist_upperleft = (initial_estimate - expand_upperleft).abs();

	if dist_left <= dist_above && dist_left <= dist_upperleft {
		left
	} else if dist_above <= dist_upperleft {
		above
	} else {
		upperleft
	}
}

pub fn decode_row(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
	use self::FilterType::*;
	let len = current.len();

	match filter {
		None => (),
		Sub => {
			for i in bpp..len {
				current[i] = current[i].wrapping_add(current[i - bpp]);
			}
		}
		Up => {
			for i in 0..len {
				current[i] = current[i].wrapping_add(previous[i]);
			}
		}
		Avg => {
			for i in 0..bpp {
				current[i] = current[i].wrapping_add(previous[i] / 2);
			}

			for i in bpp..len {
				current[i] = current[i].wrapping_add((i16::from(current[i - bpp]) + i16::from(previous[i]) / 2) as u8);
			}
		}
		Paeth => {
			for i in 0..bpp {
				current[i] = current[i].wrapping_add(paeth_predict(0, previous[i], 0));
			}

			for i in bpp..len {
				current[i] = current[i].wrapping_add(paeth_predict(current[i - bpp], previous[i], previous[i - bpp]));
			}
		}
	}
}

pub fn decode_frame(content: &[u8], bytes_per_pixel: usize, pixels_per_row: usize) -> Result<Vec<u8>> {
	let bytes_per_row = bytes_per_pixel * pixels_per_row;
	let mut previous = vec![0_u8; bytes_per_row];
	let mut current = vec![0_u8; bytes_per_row];
	let mut decoded = Vec::new();
	let mut pos = 0;
	while pos < content.len() {
		if let Ok(filter) = content[pos].try_into() {
			pos += 1;
			(&content[pos..]).read_exact(current.as_mut_slice())?;
			pos += bytes_per_row;

			decode_row(filter, bytes_per_pixel, previous.as_slice(), current.as_mut_slice());
			decoded.write_all(current.as_slice())?;
			mem::swap(&mut previous, &mut current);
		} else {
			return Err(Error::new(ErrorKind::InvalidData, format!("invalid PNG filter type ({})", content[pos])));
		}
	}
	Ok(decoded)
}

pub fn encode_row(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
	use self::FilterType::*;
	let len = current.len();

	match method {
		None => (),
		Sub => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(current[i - bpp]);
			}
		}
		Up => {
			for i in 0..len {
				current[i] = current[i].wrapping_sub(previous[i]);
			}
		}
		Avg => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(current[i - bpp].wrapping_add(previous[i]) / 2);
			}

			for i in 0..bpp {
				current[i] = current[i].wrapping_sub(previous[i] / 2);
			}
		}
		Paeth => {
			for i in (bpp..len).rev() {
				current[i] = current[i].wrapping_sub(paeth_predict(current[i - bpp], previous[i], previous[i - bpp]));
			}

			for i in 0..bpp {
				current[i] = current[i].wrapping_sub(paeth_predict(0, previous[i], 0));
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::hir::{def_id, Body, FnDecl};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::mir::{
    self, traversal,
    visit::{PlaceContext, Visitor},
    TerminatorKind,
};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::{
    ast::NodeId,
    source_map::{BytePos, Span},
};
use crate::utils::{
    in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then,
    walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use std::convert::TryFrom;

/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
/// value that is going to be dropped without further use.
///
/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
/// allocations and deallocations generated by redundant `clone()`s.
///
/// **Known problems:**
///
/// * Suggestions made by this lint could require NLL to be enabled.
/// * False-positive if there is a borrow preventing the value from moving out.
///
/// ```rust
/// let x = String::new();
///
/// let y = &x;
///
/// foo(x.clone()); // This lint suggests to remove this `clone()`
/// ```
///
/// **Example:**
/// ```rust
/// {
///     let x = Foo::new();
///     call(x.clone());
///     call(x.clone()); // this can just pass `x`
/// }
///
/// ["lorem", "ipsum"].join(" ").to_string()
///
/// Path::new("/a/b").join("c").to_path_buf()
/// ```
declare_clippy_lint! {
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

pub struct RedundantClone;

impl LintPass for RedundantClone {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        let def_id = cx.tcx.hir.body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        // Looks for `call(x: &T)` where `T: !Copy`
        let call = |kind: &mir::TerminatorKind<'tcx>| -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>)> {
            if_chain! {
                if let TerminatorKind::Call { func, args, .. } = kind;
                if args.len() == 1;
                if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
                if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
                if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
                if !is_copy(cx, inner_ty);
                then {
                    Some((def_id, *local, inner_ty))
                } else {
                    None
                }
            }
        };

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = if let Some(terminator) = &bbdata.terminator {
                terminator
            } else {
                continue;
            };

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty) = if let Some(t) = call(&terminator.kind) {
                t
            } else {
                continue;
            };

            let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
                    && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); }
            let cloned = if let Some(referent) = bbdata
                .statements
                .iter()
                .rev()
                .filter_map(|stmt| {
                    if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
                        if *local == arg {
                            if from_deref {
                                // `r` is already a reference.
                                if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
                                    return Some(r);
                                }
                            } else if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
                                return Some(r);
                            }
                        }
                    }

                    None
                })
                .next()
            {
                referent
            } else {
                continue;
            };

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                let pred_arg = if_chain! {
                    if ps.len() == 1;
                    if let Some(pred_terminator) = &mir[ps[0]].terminator;
                    if let mir::TerminatorKind::Call { destination: Some((res, _)), .. } = &pred_terminator.kind;
                    if *res == mir::Place::Local(cloned);
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty)) = call(&pred_terminator.kind);
                    if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                if let Some(referent) = mir[ps[0]]
                    .statements
                    .iter()
                    .rev()
                    .filter_map(|stmt| {
                        if let mir::StatementKind::Assign(mir::Place::Local(l), v) = &stmt.kind {
                            if *l == pred_arg {
                                if let mir::Rvalue::Ref(_, _, mir::Place::Local(referent)) = **v {
                                    return Some(referent);
                                }
                            }
                        }

                        None
                    })
                    .next()
                {
                    referent
                } else {
                    continue;
                }
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                if let Some(term) = &tdata.terminator {
                    // Give up on loops
                    if term.successors().any(|s| *s == bb) {
                        return true;
                    }
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                let node = if let mir::ClearCrossCrate::Set(scope_local_data) = &mir.source_scope_local_data {
                    scope_local_data[terminator.source_info.scope].lint_root
                } else {
                    unreachable!()
                };

                if_chain! {
                    if !in_macro(span);
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_node_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
                            db.span_suggestion_with_applicability(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint_node(cx, REDUNDANT_CLONE, node, span, "redundant clone");
                    }
                }
            }
        }
    }
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
        let mir::BasicBlockData {
            statements,
            terminator,
            is_cleanup: _,
        } = data;

        for (statement_index, statement) in statements.iter().enumerate() {
            self.visit_statement(block, statement, mir::Location { block, statement_index });

            // Once flagged, skip remaining statements
            if self.used_other_than_drop {
                return;
            }
        }

        if let Some(terminator) = terminator {
            self.visit_terminator(
                block,
                terminator,
                mir::Location {
                    block,
                    statement_index: statements.len(),
                },
            );
        }
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
        match ctx {
            PlaceContext::Drop | PlaceContext::StorageDead => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<commit_msg>Refactor<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::intravisit::FnKind;
use crate::rustc::hir::{def_id, Body, FnDecl};
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::mir::{
    self, traversal,
    visit::{PlaceContext, Visitor},
    TerminatorKind,
};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::{
    ast::NodeId,
    source_map::{BytePos, Span},
};
use crate::utils::{
    in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then,
    walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use std::convert::TryFrom;

macro_rules! unwrap_or_continue {
    ($x:expr) => {
        match $x {
            Some(x) => x,
            None => continue,
        }
    };
}

/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
/// value that is going to be dropped without further use.
///
/// **Why is this bad?** It is not always possible for the compiler to eliminate useless
/// allocations and deallocations generated by redundant `clone()`s.
///
/// **Known problems:**
///
/// * Suggestions made by this lint could require NLL to be enabled.
/// * False-positive if there is a borrow preventing the value from moving out.
///
/// ```rust
/// let x = String::new();
///
/// let y = &x;
///
/// foo(x.clone()); // This lint suggests to remove this `clone()`
/// ```
///
/// **Example:**
/// ```rust
/// {
///     let x = Foo::new();
///     call(x.clone());
///     call(x.clone()); // this can just pass `x`
/// }
///
/// ["lorem", "ipsum"].join(" ").to_string()
///
/// Path::new("/a/b").join("c").to_path_buf()
/// ```
declare_clippy_lint! {
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

pub struct RedundantClone;

impl LintPass for RedundantClone {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        let def_id = cx.tcx.hir.body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = unwrap_or_continue!(&bbdata.terminator);

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));

            let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx.tcx, fn_def_id, &paths::TO_STRING_METHOD)
                    && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx.tcx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx.tcx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
            // In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
            // block.
            let cloned = unwrap_or_continue!(find_stmt_assigns_to(arg, from_borrow, bbdata.statements.iter().rev()));

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                if ps.len() != 1 {
                    continue;
                }
                let pred_terminator = unwrap_or_continue!(&mir[ps[0]].terminator);

                let pred_arg = if_chain! {
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
                    if *res == mir::Place::Local(cloned);
                    if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                unwrap_or_continue!(find_stmt_assigns_to(pred_arg, true, mir[ps[0]].statements.iter().rev()))
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                if let Some(term) = &tdata.terminator {
                    // Give up on loops
                    if term.successors().any(|s| *s == bb) {
                        return true;
                    }
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                let node = if let mir::ClearCrossCrate::Set(scope_local_data) = &mir.source_scope_local_data {
                    scope_local_data[terminator.source_info.scope].lint_root
                } else {
                    unreachable!()
                };

                if_chain! {
                    if !in_macro(span);
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_node_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
                            db.span_suggestion_with_applicability(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint_node(cx, REDUNDANT_CLONE, node, span, "redundant clone");
                    }
                }
            }
        }
    }
}

/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
fn is_call_with_ref_arg<'tcx>(
    cx: &LateContext<'_, 'tcx>,
    mir: &'tcx mir::Mir<'tcx>,
    kind: &'tcx mir::TerminatorKind<'tcx>,
) -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
    if_chain! {
        if let TerminatorKind::Call { func, args, destination, .. } = kind;
        if args.len() == 1;
        if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
        if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
        if !is_copy(cx, inner_ty);
        then {
            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)))
        } else {
            None
        }
    }
}

/// Finds the first `to = (&)from`, and returns `Some(from)`.
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
    to: mir::Local,
    by_ref: bool,
    mut stmts: impl Iterator<Item = &'a mir::Statement<'tcx>>,
) -> Option<mir::Local> {
    stmts.find_map(|stmt| {
        if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
            if *local == to {
                if by_ref {
                    if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
                        return Some(r);
                    }
                } else if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
                    return Some(r);
                }
            }
        }

        None
    })
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
        let mir::BasicBlockData {
            statements,
            terminator,
            is_cleanup: _,
        } = data;

        for (statement_index, statement) in statements.iter().enumerate() {
            self.visit_statement(block, statement, mir::Location { block, statement_index });

            // Once flagged, skip remaining statements
            if self.used_other_than_drop {
                return;
            }
        }

        if let Some(terminator) = terminator {
            self.visit_terminator(
                block,
                terminator,
                mir::Location {
                    block,
                    statement_index: statements.len(),
                },
            );
        }
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
        match ctx {
            PlaceContext::Drop | PlaceContext::StorageDead => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The host wrapper for communicating with a remote host.

use czmq::{ZCert, ZMsg, ZSock, ZSockType};
use error::Error;
use Result;
use std::path::Path;
use super::*;
use zfilexfer;

impl Host {
    /// Create a new Host to represent your managed host.
    pub fn new() -> Host {
        Host {
            hostname: None,
            api_sock: None,
            file_sock: None,
        }
    }

    #[cfg(test)]
    pub fn test_new(hostname: Option<String>, api_sock: Option<ZSock>, file_sock: Option<ZSock>) -> Host {
        let host = Host {
            hostname: hostname,
            api_sock: api_sock,
            file_sock: file_sock,
        };

        host
    }

    pub fn connect(&mut self, hostname: &str, api_port: u32, file_port: u32, auth_server: &str) -> Result<()> {
        self.hostname = Some(hostname.to_string());

        let user_cert = try!(ZCert::load("user.crt"));
        let server_cert = try!(self.lookup_server_cert(hostname, auth_server, &user_cert));

        let api_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&api_sock);
        api_sock.set_curve_serverkey(server_cert.public_txt());
        api_sock.set_sndtimeo(Some(5000));
        api_sock.set_rcvtimeo(Some(5000));
        try!(api_sock.connect(&format!("tcp://{}:{}", hostname, api_port)));
        self.api_sock = Some(api_sock);

        let file_sock = ZSock::new(ZSockType::DEALER);
        user_cert.apply(&file_sock);
        file_sock.set_curve_serverkey(server_cert.public_txt());
        file_sock.set_sndtimeo(Some(5000));
        file_sock.set_rcvtimeo(Some(5000));
        try!(file_sock.connect(&format!("tcp://{}:{}", hostname, file_port)));
        self.file_sock = Some(file_sock);

        Ok(())
    }

    fn lookup_server_cert(&self, hostname: &str, auth_server: &str, user_cert: &ZCert) -> Result<ZCert> {
        let auth_cert = try!(ZCert::load("auth.crt"));

        let auth_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&auth_sock);
        auth_sock.set_curve_serverkey(auth_cert.public_txt());
        auth_sock.set_sndtimeo(Some(5000));
        auth_sock.set_rcvtimeo(Some(5000));
        try!(auth_sock.connect(&format!("tcp://{}", auth_server)));

        // Get server cert from Auth server
        let msg = ZMsg::new();
        try!(msg.addstr("cert::lookup"));
        try!(msg.addstr(hostname));
        try!(msg.send(&auth_sock));

        let reply = try!(ZMsg::recv(&auth_sock));

        if reply.size() != 2 {
            return Err(Error::HostResponse);
        }

        match try!(reply.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => {
                let pk = try!(reply.popstr().unwrap().or(Err(Error::HostResponse)));
                Ok(ZCert::from_txt(&pk, "0000000000000000000000000000000000000000"))
            },
            "Err" => Err(Error::Auth(try!(reply.popstr().unwrap().or(Err(Error::HostResponse))))),
            _ => unreachable!(),
        }
    }

    pub fn close(&mut self) -> Result<()> {
        if self.api_sock.is_some() {
            self.api_sock.take().unwrap();
        }

        if self.file_sock.is_some() {
            self.file_sock.take().unwrap();
        }

        Ok(())
    }

    #[doc(hidden)]
    pub fn send(&mut self, msg: ZMsg) -> Result<()> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.api_sock.as_mut().unwrap()));
        Ok(())
    }

    #[doc(hidden)]
    pub fn send_file<P: AsRef<Path>>(&mut self, file: &zfilexfer::File, remote_path: P) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(file.send(self.file_sock.as_ref().unwrap(), remote_path));
        Ok(())
    }

    #[doc(hidden)]
    pub fn recv(&self, min: usize, max: Option<usize>) -> Result<ZMsg> {
        let msg = try!(ZMsg::recv(self.api_sock.as_ref().unwrap()));
        try!(Self::extract_header(&msg));

        // Check msg size
        if msg.size() < min || (max.is_some() && msg.size() > max.unwrap()) {
            Err(Error::HostResponse)
        } else {
            Ok(msg)
        }
    }

    fn extract_header(msg: &ZMsg) -> Result<()> {
        if msg.size() == 0 {
            return Err(Error::HostResponse);
        }

        match try!(msg.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => Ok(()),
            "Err" => {
                if msg.size() == 0 {
                    Err(Error::HostResponse)
                } else {
                    Err(Error::Agent(try!(msg.popstr().unwrap().or(Err(Error::HostResponse)))))
                }
            },
            _ => Err(Error::HostResponse),
        }
    }
}

#[cfg(test)]
mod tests {
    use {create_project_fs, Host, mock_auth_server};
    use czmq::{ZMsg, ZSys};
    use std::fs;
    use std::thread::spawn;
    use tempdir::TempDir;
    use zfilexfer::File;

    #[test]
    fn test_connect_close() {
        create_project_fs();
        let (handle, auth_server) = mock_auth_server();

        let mut host = Host::new();
        assert!(host.connect("localhost", 7101, 7102, &auth_server).is_ok());
        assert!(host.close().is_ok());

        handle.join().unwrap();
    }

    #[test]
    fn test_send_recv() {
        ZSys::init();

        let (client, server) = ZSys::create_pipe().unwrap();

        let mut host1 = Host::test_new(None, Some(client), None);
        let mut host2 = Host::test_new(None, Some(server), None);

        let msg = ZMsg::new();
        msg.addstr("Ok").unwrap();
        msg.addstr("moo").unwrap();
        msg.addstr("cow").unwrap();
        host1.send(msg).unwrap();

        let reply = host2.recv(2, Some(2)).unwrap();
        assert_eq!(reply.popstr().unwrap().unwrap(), "moo");
        assert_eq!(reply.popstr().unwrap().unwrap(), "cow");

        let msg = ZMsg::new();
        msg.addstr("No header").unwrap();
        host2.send(msg).unwrap();

        assert!(host1.recv(0, None).is_err());
        let msg = ZMsg::new();
        msg.addstr("Err").unwrap();
        host1.send(msg).unwrap();

        assert!(host2.recv(0, None).is_err());
    }

    #[test]
    fn test_send_file() {
        ZSys::init();

        let tempdir = TempDir::new("host_test_send_file").unwrap();
        let path = format!("{}/file.txt", tempdir.path().to_str().unwrap());
        fs::File::create(&path).unwrap();
        let file = File::open(&path, None).unwrap();

        let (client, server) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let handle = spawn(move|| {
            let msg = ZMsg::recv(&server).unwrap();
            assert_eq!(msg.popstr().unwrap().unwrap(), "NEW");

            server.flush();
            server.send_str("Ok").unwrap();
        });

        let mut host = Host::test_new(None, None, Some(client));
        assert!(host.send_file(&file, &path).is_ok());

        handle.join().unwrap();
    }
}
<commit_msg>Increase timeout on hosts to 60 seconds<commit_after>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The host wrapper for communicating with a remote host.

use czmq::{ZCert, ZMsg, ZSock, ZSockType};
use error::Error;
use Result;
use std::path::Path;
use super::*;
use zfilexfer;

impl Host {
    /// Create a new Host to represent your managed host.
    pub fn new() -> Host {
        Host {
            hostname: None,
            api_sock: None,
            file_sock: None,
        }
    }

    #[cfg(test)]
    pub fn test_new(hostname: Option<String>, api_sock: Option<ZSock>, file_sock: Option<ZSock>) -> Host {
        let host = Host {
            hostname: hostname,
            api_sock: api_sock,
            file_sock: file_sock,
        };

        host
    }

    pub fn connect(&mut self, hostname: &str, api_port: u32, file_port: u32, auth_server: &str) -> Result<()> {
        self.hostname = Some(hostname.to_string());

        let user_cert = try!(ZCert::load("user.crt"));
        let server_cert = try!(self.lookup_server_cert(hostname, auth_server, &user_cert));

        let api_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&api_sock);
        api_sock.set_curve_serverkey(server_cert.public_txt());
        api_sock.set_sndtimeo(Some(60000));
        api_sock.set_rcvtimeo(Some(60000));
        try!(api_sock.connect(&format!("tcp://{}:{}", hostname, api_port)));
        self.api_sock = Some(api_sock);

        let file_sock = ZSock::new(ZSockType::DEALER);
        user_cert.apply(&file_sock);
        file_sock.set_curve_serverkey(server_cert.public_txt());
        file_sock.set_sndtimeo(Some(60000));
        file_sock.set_rcvtimeo(Some(60000));
        try!(file_sock.connect(&format!("tcp://{}:{}", hostname, file_port)));
        self.file_sock = Some(file_sock);

        Ok(())
    }

    fn lookup_server_cert(&self, hostname: &str, auth_server: &str, user_cert: &ZCert) -> Result<ZCert> {
        let auth_cert = try!(ZCert::load("auth.crt"));

        let auth_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&auth_sock);
        auth_sock.set_curve_serverkey(auth_cert.public_txt());
        auth_sock.set_sndtimeo(Some(10000));
        auth_sock.set_rcvtimeo(Some(10000));
        try!(auth_sock.connect(&format!("tcp://{}", auth_server)));

        // Get server cert from Auth server
        let msg = ZMsg::new();
        try!(msg.addstr("cert::lookup"));
        try!(msg.addstr(hostname));
        try!(msg.send(&auth_sock));

        let reply = try!(ZMsg::recv(&auth_sock));

        if reply.size() != 2 {
            return Err(Error::HostResponse);
        }

        match try!(reply.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => {
                let pk = try!(reply.popstr().unwrap().or(Err(Error::HostResponse)));
                Ok(ZCert::from_txt(&pk, "0000000000000000000000000000000000000000"))
            },
            "Err" => Err(Error::Auth(try!(reply.popstr().unwrap().or(Err(Error::HostResponse))))),
            _ => unreachable!(),
        }
    }

    pub fn close(&mut self) -> Result<()> {
        if self.api_sock.is_some() {
            self.api_sock.take().unwrap();
        }

        if self.file_sock.is_some() {
            self.file_sock.take().unwrap();
        }

        Ok(())
    }

    #[doc(hidden)]
    pub fn send(&mut self, msg: ZMsg) -> Result<()> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.api_sock.as_mut().unwrap()));
        Ok(())
    }

    #[doc(hidden)]
    pub fn send_file<P: AsRef<Path>>(&mut self, file: &zfilexfer::File, remote_path: P) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(file.send(self.file_sock.as_ref().unwrap(), remote_path));
        Ok(())
    }

    #[doc(hidden)]
    pub fn recv(&self, min: usize, max: Option<usize>) -> Result<ZMsg> {
        let msg = try!(ZMsg::recv(self.api_sock.as_ref().unwrap()));
        try!(Self::extract_header(&msg));

        // Check msg size
        if msg.size() < min || (max.is_some() && msg.size() > max.unwrap()) {
            Err(Error::HostResponse)
        } else {
            Ok(msg)
        }
    }

    fn extract_header(msg: &ZMsg) -> Result<()> {
        if msg.size() == 0 {
            return Err(Error::HostResponse);
        }

        match try!(msg.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => Ok(()),
            "Err" => {
                if msg.size() == 0 {
                    Err(Error::HostResponse)
                } else {
                    Err(Error::Agent(try!(msg.popstr().unwrap().or(Err(Error::HostResponse)))))
                }
            },
            _ => Err(Error::HostResponse),
        }
    }
}

#[cfg(test)]
mod tests {
    use {create_project_fs, Host, mock_auth_server};
    use czmq::{ZMsg, ZSys};
    use std::fs;
    use std::thread::spawn;
    use tempdir::TempDir;
    use zfilexfer::File;

    #[test]
    fn test_connect_close() {
        create_project_fs();
        let (handle, auth_server) = mock_auth_server();

        let mut host = Host::new();
        assert!(host.connect("localhost", 7101, 7102, &auth_server).is_ok());
        assert!(host.close().is_ok());

        handle.join().unwrap();
    }

    #[test]
    fn test_send_recv() {
        ZSys::init();

        let (client, server) = ZSys::create_pipe().unwrap();

        let mut host1 = Host::test_new(None, Some(client), None);
        let mut host2 = Host::test_new(None, Some(server), None);

        let msg = ZMsg::new();
        msg.addstr("Ok").unwrap();
        msg.addstr("moo").unwrap();
        msg.addstr("cow").unwrap();
        host1.send(msg).unwrap();

        let reply = host2.recv(2, Some(2)).unwrap();
        assert_eq!(reply.popstr().unwrap().unwrap(), "moo");
        assert_eq!(reply.popstr().unwrap().unwrap(), "cow");

        let msg = ZMsg::new();
        msg.addstr("No header").unwrap();
        host2.send(msg).unwrap();

        assert!(host1.recv(0, None).is_err());
        let msg = ZMsg::new();
        msg.addstr("Err").unwrap();
        host1.send(msg).unwrap();

        assert!(host2.recv(0, None).is_err());
    }

    #[test]
    fn test_send_file() {
        ZSys::init();

        let tempdir = TempDir::new("host_test_send_file").unwrap();
        let path = format!("{}/file.txt", tempdir.path().to_str().unwrap());
        fs::File::create(&path).unwrap();
        let file = File::open(&path, None).unwrap();

        let (client, server) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let handle = spawn(move|| {
            let msg = ZMsg::recv(&server).unwrap();
            assert_eq!(msg.popstr().unwrap().unwrap(), "NEW");

            server.flush();
            server.send_str("Ok").unwrap();
        });

        let mut host = Host::test_new(None, None, Some(client));
        assert!(host.send_file(&file, &path).is_ok());

        handle.join().unwrap();
    }
}
<|endoftext|>


<commit_before>use token;

pub struct Interpreter {
    tokenizer: token::Tokenizer,
    current: Option<token::Token>
}

impl Interpreter {
    pub fn new(text: token::Tokenizer) -> Self {
        Interpreter {
            tokenizer: text,
            current: None
        }
    }

    pub fn next(&mut self) -> Option<token::Token> {
        if self.current.is_some() { return self.current.clone() }
        self.current = self.tokenizer.next();
        self.current.clone()
    }

    pub fn consume_next(&mut self, expected_kind: token::Kind) -> Option<token::Token> {
        let token = match self.current {
            Some(_) => {
                let curr = self.current.clone();
                self.current = None;
                curr
            },
            None => {
                let next = self.next();
                self.current = None;
                next.clone()
            }
        };

        if token.clone().unwrap().kind == expected_kind {
            return token;
        } else {
            panic!(
                "Sintax error: expected token kind {:?} found {:?} at position {}",
                expected_kind,
                token,
                self.tokenizer.position
                )
        }
    }

    fn factor(&mut self) -> token::Token {
        match self.next() {
            Some(token::Token{ kind: token::Kind::GroupStart , .. }) => {
                self.consume_next(token::Kind::GroupStart);
                return token::Token::build(token::Kind::Integer, self.expr());
            },
            Some(token::Token{ kind: token::Kind::Integer, .. }) => {
                return self.consume_next(token::Kind::Integer).unwrap();
            },
            _ => panic!("Error factor")
        }
    }

    fn term(&mut self) -> token::Token {
        let mut result = self.factor();

        if let Some(operator) = self.next() {
            result.value = match operator.value.as_ref() {
                "*" | "/" => {
                    self.consume_next(token::Kind::Operator);
                    let right = self.factor();
                    binary_operation(
                        &result,
                        &operator,
                        &right
                    ).to_string()
                },
                _ => result.clone().value
            };
        }
        return result;
    }

    pub fn expr(&mut self) -> String {
        let mut result = self.term();
        while let Some(operator) = self.next() {
            if operator.kind == token::Kind::GroupEnd {
                self.consume_next(token::Kind::GroupEnd);
                break;
            }
            self.consume_next(token::Kind::Operator);
            let right = self.term();

            let operation_result = match operator.value.as_ref() {
                "+" | "-" => binary_operation(&result, &operator, &right),
                _ => result.clone().as_integer()
            };

            result.value = operation_result.to_string();
        }

        format!("{}", result.value)
    }
}

fn binary_operation(first: &token::Token, operator: &token::Token, second: &token::Token) -> i32 {
    if first.kind == token::Kind::Integer {
        let operand = first.clone().as_integer();
        let operand2 = second.clone().as_integer();
        match &*operator.value {
            "+" => operand + operand2,
            "-" => operand - operand2,
            "*" => operand * operand2,
            "/" => operand / operand2,
            _ => panic!("Sintax error: invalid operator {}", operator.value)
        }
    } else {
        panic!("Sintax error: invalid binary operation using {} {} {}.",
               first.value,
               operator.value,
               second.value)
    }
}

#[test]
fn it_sums() {
    let text = "5+1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("6", interpreter.expr());
}

#[test]
fn it_substract() {
    let text = "5-1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("4", interpreter.expr());
}

#[test]
fn it_multiplies() {
    let text = "5*2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}

#[test]
fn it_divide() {
    let text = "4/2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("2", interpreter.expr());
}

#[test]
fn it_accepts_multiples_operation() {
    let text = "10+5-4-1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}

#[test]
fn it_respect_precedence() {
    let text = "1+1*2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("3", interpreter.expr());
}

#[test]
fn it_respects_grouped_expression() {
    let text = "4+(1+(1+1)*2)+1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}
<commit_msg>refactor: more elegant way to advance through the tokens<commit_after>use token;

pub struct Interpreter {
    tokenizer: token::Tokenizer,
    current: Option<token::Token>
}

impl Interpreter {
    pub fn new(text: token::Tokenizer) -> Self {
        Interpreter {
            tokenizer: text,
            current: None
        }
    }

    pub fn next(&mut self) -> &mut Self {
        if self.current.is_none() { self.current = self.tokenizer.next() }
        self
    }

    pub fn get(&mut self) -> Option<token::Token> {
        self.current.clone()
    }

    pub fn consume(&mut self, expected_kind: token::Kind) -> Option<token::Token> {
        let mut cosumed = self.current.clone();
        if let Some(token) = self.current.clone() {
            self.current = None;
            if token.kind != expected_kind {
                panic!(
                    "Sintax error: expected token kind {:?} found {:?} at position {}",
                    expected_kind,
                    token,
                    self.tokenizer.position
                    )
            }
        }
        return cosumed;
    }

    fn factor(&mut self) -> token::Token {
        match self.next().get() {
            Some(token::Token{ kind: token::Kind::GroupStart , .. }) => {
                self.consume(token::Kind::GroupStart);
                return token::Token::build(token::Kind::Integer, self.expr());
            },
            Some(token::Token{ kind: token::Kind::Integer, .. }) => {
                return self.next().consume(token::Kind::Integer).unwrap();
            },
            _ => panic!("Error factor")
        }
    }

    fn term(&mut self) -> token::Token {
        let mut result = self.factor();

        if let Some(operator) = self.next().get() {
            result.value = match operator.value.as_ref() {
                "*" | "/" => {
                    self.consume(token::Kind::Operator);
                    let right = self.factor();
                    binary_operation(
                        &result,
                        &operator,
                        &right
                    ).to_string()
                },
                _ => result.clone().value
            };
        }
        return result;
    }

    pub fn expr(&mut self) -> String {
        let mut result = self.term();
        while let Some(operator) = self.next().get() {
            if operator.kind == token::Kind::GroupEnd {
                self.consume(token::Kind::GroupEnd);
                break;
            }
            self.consume(token::Kind::Operator);
            let right = self.term();

            let operation_result = match operator.value.as_ref() {
                "+" | "-" => binary_operation(&result, &operator, &right),
                _ => result.clone().as_integer()
            };

            result.value = operation_result.to_string();
        }

        format!("{}", result.value)
    }
}

fn binary_operation(first: &token::Token, operator: &token::Token, second: &token::Token) -> i32 {
    if first.kind == token::Kind::Integer {
        let operand = first.clone().as_integer();
        let operand2 = second.clone().as_integer();
        match &*operator.value {
            "+" => operand + operand2,
            "-" => operand - operand2,
            "*" => operand * operand2,
            "/" => operand / operand2,
            _ => panic!("Sintax error: invalid operator {}", operator.value)
        }
    } else {
        panic!("Sintax error: invalid binary operation using {} {} {}.",
               first.value,
               operator.value,
               second.value)
    }
}

#[test]
fn it_sums() {
    let text = "5+1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("6", interpreter.expr());
}

#[test]
fn it_substract() {
    let text = "5-1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("4", interpreter.expr());
}

#[test]
fn it_multiplies() {
    let text = "5*2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}

#[test]
fn it_divide() {
    let text = "4/2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("2", interpreter.expr());
}

#[test]
fn it_accepts_multiples_operation() {
    let text = "10+5-4-1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}

#[test]
fn it_respect_precedence() {
    let text = "1+1*2";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("3", interpreter.expr());
}

#[test]
fn it_respects_grouped_expression() {
    let text = "4+(1+(1+1)*2)+1";
    let tokenizer = token::Tokenizer::new(String::from(text));
    let mut interpreter = Interpreter::new(tokenizer);

    assert_eq!("10", interpreter.expr());
}
<|endoftext|>