<commit_before>use std::any::{Any, TypeId};
use std::error::Error;
use std::fmt;

use chrono::NaiveDateTime;
use conduit::Response;
use diesel::result::Error as DieselError;

use crate::util::json_response;

pub(super) mod concrete;
mod http;

/// Returns an error with status 200 and the provided description as JSON
///
/// This is for backwards compatibility with cargo endpoints.  For all other
/// endpoints, use helpers like `bad_request` or `server_error` which set a
/// correct status code.
pub fn cargo_err<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::Ok(error.to_string()))
}

// The following are intended to be used for errors being sent back to the Ember
// frontend, not to cargo as cargo does not handle non-200 response codes well
// (see <https://github.com/rust-lang/cargo/issues/3995>), but Ember requires
// non-200 response codes for its stores to work properly.

/// Return an error with status 400 and the provided description as JSON
pub fn bad_request<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::BadRequest(error.to_string()))
}

/// Returns an error with status 500 and the provided description as JSON
pub fn server_error<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::ServerError(error.to_string()))
}

#[derive(Serialize)]
struct StringError<'a> {
    detail: &'a str,
}
#[derive(Serialize)]
struct Bad<'a> {
    errors: Vec<StringError<'a>>,
}

/// Generates a response with the provided status and description as JSON
fn json_error(detail: &str, status: (u32, &'static str)) -> Response {
    let mut response = json_response(&Bad {
        errors: vec![StringError { detail }],
    });
    response.status = status;
    response
}

// =============================================================================
// AppError trait

pub trait AppError: Send + fmt::Display + fmt::Debug + 'static {
    /// Generate an HTTP response for the error
    ///
    /// If none is returned, the error will bubble up the middleware stack
    /// where it is eventually logged and turned into a status 500 response.
    fn response(&self) -> Option<Response>;

    fn get_type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

impl dyn AppError {
    pub fn is<T: Any>(&self) -> bool {
        self.get_type_id() == TypeId::of::<T>()
    }

    pub fn from_std_error(err: Box<dyn Error + Send>) -> Box<dyn AppError> {
        Self::try_convert(&*err).unwrap_or_else(|| internal(&err))
    }

    fn try_convert(err: &(dyn Error + Send + 'static)) -> Option<Box<Self>> {
        match err.downcast_ref() {
            Some(DieselError::NotFound) => Some(Box::new(NotFound)),
            Some(DieselError::DatabaseError(_, info))
                if info.message().ends_with("read-only transaction") =>
            {
                Some(Box::new(ReadOnlyMode))
            }
            _ => None,
        }
    }
}

impl AppError for Box<dyn AppError> {
    fn response(&self) -> Option<Response> {
        (**self).response()
    }
}

pub type AppResult<T> = Result<T, Box<dyn AppError>>;

// =============================================================================
// Chaining errors

pub trait ChainError<T> {
    fn chain_error<E, F>(self, callback: F) -> AppResult<T>
    where
        E: AppError,
        F: FnOnce() -> E;
}

#[derive(Debug)]
struct ChainedError<E> {
    error: E,
    cause: Box<dyn AppError>,
}

impl<T, F> ChainError<T> for F
where
    F: FnOnce() -> AppResult<T>,
{
    fn chain_error<E, C>(self, callback: C) -> AppResult<T>
    where
        E: AppError,
        C: FnOnce() -> E,
    {
        self().chain_error(callback)
    }
}

impl<T, E: AppError> ChainError<T> for Result<T, E> {
    fn chain_error<E2, C>(self, callback: C) -> AppResult<T>
    where
        E2: AppError,
        C: FnOnce() -> E2,
    {
        self.map_err(move |err| {
            Box::new(ChainedError {
                error: callback(),
                cause: Box::new(err),
            }) as Box<dyn AppError>
        })
    }
}

impl<T> ChainError<T> for Option<T> {
    fn chain_error<E, C>(self, callback: C) -> AppResult<T>
    where
        E: AppError,
        C: FnOnce() -> E,
    {
        match self {
            Some(t) => Ok(t),
            None => Err(Box::new(callback())),
        }
    }
}

impl<E: AppError> AppError for ChainedError<E> {
    fn response(&self) -> Option<Response> {
        self.error.response()
    }
}

impl<E: AppError> fmt::Display for ChainedError<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} caused by {}", self.error, self.cause)
    }
}

// =============================================================================
// Error impls

impl<E: Error + Send + 'static> AppError for E {
    fn response(&self) -> Option<Response> {
        None
    }
}

impl<E: Error + Send + 'static> From<E> for Box<dyn AppError> {
    fn from(err: E) -> Box<dyn AppError> {
        AppError::try_convert(&err).unwrap_or_else(|| Box::new(err))
    }
}
// =============================================================================
// Internal error for use with `chain_error`

#[derive(Debug)]
struct InternalAppError {
    description: String,
}

impl fmt::Display for InternalAppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.description)?;
        Ok(())
    }
}

impl AppError for InternalAppError {
    fn response(&self) -> Option<Response> {
        None
    }
}

// TODO: The remaining can probably move under `http`

#[derive(Debug, Clone, Copy)]
pub struct NotFound;

impl AppError for NotFound {
    fn response(&self) -> Option<Response> {
        Some(json_error("Not Found", (404, "Not Found")))
    }
}

impl fmt::Display for NotFound {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Not Found".fmt(f)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Unauthorized;

impl AppError for Unauthorized {
    fn response(&self) -> Option<Response> {
        let detail = "must be logged in to perform that action";
        Some(json_error(detail, (403, "Forbidden")))
    }
}

impl fmt::Display for Unauthorized {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "must be logged in to perform that action".fmt(f)
    }
}

pub fn internal<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(InternalAppError {
        description: error.to_string(),
    })
}

#[derive(Debug)]
struct AppErrToStdErr(pub Box<dyn AppError>);

impl Error for AppErrToStdErr {}

impl fmt::Display for AppErrToStdErr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

pub(crate) fn std_error(e: Box<dyn AppError>) -> Box<dyn Error + Send> {
    Box::new(AppErrToStdErr(e))
}

#[derive(Debug, Clone, Copy)]
pub struct ReadOnlyMode;

impl AppError for ReadOnlyMode {
    fn response(&self) -> Option<Response> {
        let detail = "Crates.io is currently in read-only mode for maintenance. \
                      Please try again later.";
        Some(json_error(detail, (503, "Service Unavailable")))
    }
}

impl fmt::Display for ReadOnlyMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Tried to write in read only mode".fmt(f)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct TooManyRequests {
    pub retry_after: NaiveDateTime,
}

impl AppError for TooManyRequests {
    fn response(&self) -> Option<Response> {
        const HTTP_DATE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
        let retry_after = self.retry_after.format(HTTP_DATE_FORMAT);

        let detail = format!(
            "You have published too many crates in a \
             short period of time. Please try again after {} or email \
             help@crates.io to have your limit increased.",
            retry_after
        );
        let mut response = json_error(&detail, (429, "TOO MANY REQUESTS"));
        response
            .headers
            .insert("Retry-After".into(), vec![retry_after.to_string()]);
        Some(response)
    }
}

impl fmt::Display for TooManyRequests {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Too many requests".fmt(f)
    }
}

#[test]
fn chain_error_internal() {
    assert_eq!(
        None::<()>
            .chain_error(|| internal("inner"))
            .chain_error(|| internal("middle"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by middle caused by inner"
    );
    assert_eq!(
        Err::<(), _>(internal("inner"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner"
    );

    // Don't do this, the user will see a generic 500 error instead of the intended message
    assert_eq!(
        Err::<(), _>(cargo_err("inner"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner"
    );
    assert_eq!(
        Err::<(), _>(Unauthorized)
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by must be logged in to perform that action"
    );
}

#[test]
fn chain_error_user_facing() {
    // Do this rarely, the user will only see the outer error
    assert_eq!(
        Err::<(), _>(cargo_err("inner"))
            .chain_error(|| cargo_err("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner" // never logged
    );

    // The outer error is sent as a response to the client.
    // The inner error never bubbles up to the logging middleware
    assert_eq!(
        Err::<(), _>(std::io::Error::from(std::io::ErrorKind::PermissionDenied))
            .chain_error(|| cargo_err("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by permission denied" // never logged
    );
}
<commit_msg>Add module level documentation describing typical usage of error types<commit_after>//! This module implements several error types and traits.  The suggested usage in returned results
//! is as follows:
//!
//! * The concrete `util::concrete::Error` type (re-exported as `util::Error`) is great for code
//!   that is not part of the request/response lifecycle.  It avoids pulling in the unnecessary
//!   infrastructure to convert errors into a user facing JSON responses (relative to `AppError`).
//! * `diesel::QueryResult` - There is a lot of code that only deals with query errors.  If only
//!   one type of error is possible in a function, using that specific error is preferable to the
//!   more general `util::Error`.  This is especially common in model code.
//! * `util::errors::AppResult` - Some failures should be converted into user facing JSON
//!   responses.  This error type is more dynamic and is box allocated.  Low-level errors are
//!   typically not converted to user facing errors and most usage is within the models,
//!   controllers, and middleware layers.

use std::any::{Any, TypeId};
use std::error::Error;
use std::fmt;

use chrono::NaiveDateTime;
use conduit::Response;
use diesel::result::Error as DieselError;

use crate::util::json_response;

pub(super) mod concrete;
mod http;

/// Returns an error with status 200 and the provided description as JSON
///
/// This is for backwards compatibility with cargo endpoints.  For all other
/// endpoints, use helpers like `bad_request` or `server_error` which set a
/// correct status code.
pub fn cargo_err<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::Ok(error.to_string()))
}

// The following are intended to be used for errors being sent back to the Ember
// frontend, not to cargo as cargo does not handle non-200 response codes well
// (see <https://github.com/rust-lang/cargo/issues/3995>), but Ember requires
// non-200 response codes for its stores to work properly.

/// Return an error with status 400 and the provided description as JSON
pub fn bad_request<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::BadRequest(error.to_string()))
}

/// Returns an error with status 500 and the provided description as JSON
pub fn server_error<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(http::ServerError(error.to_string()))
}

#[derive(Serialize)]
struct StringError<'a> {
    detail: &'a str,
}
#[derive(Serialize)]
struct Bad<'a> {
    errors: Vec<StringError<'a>>,
}

/// Generates a response with the provided status and description as JSON
fn json_error(detail: &str, status: (u32, &'static str)) -> Response {
    let mut response = json_response(&Bad {
        errors: vec![StringError { detail }],
    });
    response.status = status;
    response
}

// =============================================================================
// AppError trait

pub trait AppError: Send + fmt::Display + fmt::Debug + 'static {
    /// Generate an HTTP response for the error
    ///
    /// If none is returned, the error will bubble up the middleware stack
    /// where it is eventually logged and turned into a status 500 response.
    fn response(&self) -> Option<Response>;

    fn get_type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

impl dyn AppError {
    pub fn is<T: Any>(&self) -> bool {
        self.get_type_id() == TypeId::of::<T>()
    }

    pub fn from_std_error(err: Box<dyn Error + Send>) -> Box<dyn AppError> {
        Self::try_convert(&*err).unwrap_or_else(|| internal(&err))
    }

    fn try_convert(err: &(dyn Error + Send + 'static)) -> Option<Box<Self>> {
        match err.downcast_ref() {
            Some(DieselError::NotFound) => Some(Box::new(NotFound)),
            Some(DieselError::DatabaseError(_, info))
                if info.message().ends_with("read-only transaction") =>
            {
                Some(Box::new(ReadOnlyMode))
            }
            _ => None,
        }
    }
}

impl AppError for Box<dyn AppError> {
    fn response(&self) -> Option<Response> {
        (**self).response()
    }
}

pub type AppResult<T> = Result<T, Box<dyn AppError>>;

// =============================================================================
// Chaining errors

pub trait ChainError<T> {
    fn chain_error<E, F>(self, callback: F) -> AppResult<T>
    where
        E: AppError,
        F: FnOnce() -> E;
}

#[derive(Debug)]
struct ChainedError<E> {
    error: E,
    cause: Box<dyn AppError>,
}

impl<T, F> ChainError<T> for F
where
    F: FnOnce() -> AppResult<T>,
{
    fn chain_error<E, C>(self, callback: C) -> AppResult<T>
    where
        E: AppError,
        C: FnOnce() -> E,
    {
        self().chain_error(callback)
    }
}

impl<T, E: AppError> ChainError<T> for Result<T, E> {
    fn chain_error<E2, C>(self, callback: C) -> AppResult<T>
    where
        E2: AppError,
        C: FnOnce() -> E2,
    {
        self.map_err(move |err| {
            Box::new(ChainedError {
                error: callback(),
                cause: Box::new(err),
            }) as Box<dyn AppError>
        })
    }
}

impl<T> ChainError<T> for Option<T> {
    fn chain_error<E, C>(self, callback: C) -> AppResult<T>
    where
        E: AppError,
        C: FnOnce() -> E,
    {
        match self {
            Some(t) => Ok(t),
            None => Err(Box::new(callback())),
        }
    }
}

impl<E: AppError> AppError for ChainedError<E> {
    fn response(&self) -> Option<Response> {
        self.error.response()
    }
}

impl<E: AppError> fmt::Display for ChainedError<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} caused by {}", self.error, self.cause)
    }
}

// =============================================================================
// Error impls

impl<E: Error + Send + 'static> AppError for E {
    fn response(&self) -> Option<Response> {
        None
    }
}

impl<E: Error + Send + 'static> From<E> for Box<dyn AppError> {
    fn from(err: E) -> Box<dyn AppError> {
        AppError::try_convert(&err).unwrap_or_else(|| Box::new(err))
    }
}
// =============================================================================
// Internal error for use with `chain_error`

#[derive(Debug)]
struct InternalAppError {
    description: String,
}

impl fmt::Display for InternalAppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.description)?;
        Ok(())
    }
}

impl AppError for InternalAppError {
    fn response(&self) -> Option<Response> {
        None
    }
}

// TODO: The remaining can probably move under `http`

#[derive(Debug, Clone, Copy)]
pub struct NotFound;

impl AppError for NotFound {
    fn response(&self) -> Option<Response> {
        Some(json_error("Not Found", (404, "Not Found")))
    }
}

impl fmt::Display for NotFound {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Not Found".fmt(f)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Unauthorized;

impl AppError for Unauthorized {
    fn response(&self) -> Option<Response> {
        let detail = "must be logged in to perform that action";
        Some(json_error(detail, (403, "Forbidden")))
    }
}

impl fmt::Display for Unauthorized {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "must be logged in to perform that action".fmt(f)
    }
}

pub fn internal<S: ToString + ?Sized>(error: &S) -> Box<dyn AppError> {
    Box::new(InternalAppError {
        description: error.to_string(),
    })
}

#[derive(Debug)]
struct AppErrToStdErr(pub Box<dyn AppError>);

impl Error for AppErrToStdErr {}

impl fmt::Display for AppErrToStdErr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

pub(crate) fn std_error(e: Box<dyn AppError>) -> Box<dyn Error + Send> {
    Box::new(AppErrToStdErr(e))
}

#[derive(Debug, Clone, Copy)]
pub struct ReadOnlyMode;

impl AppError for ReadOnlyMode {
    fn response(&self) -> Option<Response> {
        let detail = "Crates.io is currently in read-only mode for maintenance. \
                      Please try again later.";
        Some(json_error(detail, (503, "Service Unavailable")))
    }
}

impl fmt::Display for ReadOnlyMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Tried to write in read only mode".fmt(f)
    }
}

#[derive(Debug, Clone, Copy)]
pub struct TooManyRequests {
    pub retry_after: NaiveDateTime,
}

impl AppError for TooManyRequests {
    fn response(&self) -> Option<Response> {
        const HTTP_DATE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
        let retry_after = self.retry_after.format(HTTP_DATE_FORMAT);

        let detail = format!(
            "You have published too many crates in a \
             short period of time. Please try again after {} or email \
             help@crates.io to have your limit increased.",
            retry_after
        );
        let mut response = json_error(&detail, (429, "TOO MANY REQUESTS"));
        response
            .headers
            .insert("Retry-After".into(), vec![retry_after.to_string()]);
        Some(response)
    }
}

impl fmt::Display for TooManyRequests {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        "Too many requests".fmt(f)
    }
}

#[test]
fn chain_error_internal() {
    assert_eq!(
        None::<()>
            .chain_error(|| internal("inner"))
            .chain_error(|| internal("middle"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by middle caused by inner"
    );
    assert_eq!(
        Err::<(), _>(internal("inner"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner"
    );

    // Don't do this, the user will see a generic 500 error instead of the intended message
    assert_eq!(
        Err::<(), _>(cargo_err("inner"))
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner"
    );
    assert_eq!(
        Err::<(), _>(Unauthorized)
            .chain_error(|| internal("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by must be logged in to perform that action"
    );
}

#[test]
fn chain_error_user_facing() {
    // Do this rarely, the user will only see the outer error
    assert_eq!(
        Err::<(), _>(cargo_err("inner"))
            .chain_error(|| cargo_err("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by inner" // never logged
    );

    // The outer error is sent as a response to the client.
    // The inner error never bubbles up to the logging middleware
    assert_eq!(
        Err::<(), _>(std::io::Error::from(std::io::ErrorKind::PermissionDenied))
            .chain_error(|| cargo_err("outer"))
            .unwrap_err()
            .to_string(),
        "outer caused by permission denied" // never logged
    );
}
<|endoftext|>


<commit_before>// DOCS

#![crate_name = "vobject"]
#![crate_type = "lib"]
#![license = "MIT"]
#![comment = "Parser for VObject and iCalendar."]

#![feature(phase)]
#[phase(plugin)]
extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};

pub struct Property {
    params: HashMap<String, String>,
    raw_value: String,
}

impl Property {
    fn new(params: HashMap<String, String>, raw_value: String) -> Property {
        Property {
            params: params,
            raw_value: raw_value
        }
    }

    #[doc="Get parameters."]
    pub fn get_params(&self) -> &HashMap<String, String> {
        &self.params
    }

    #[doc="Get value as unparsed string."]
    pub fn get_raw_value(&self) -> &String {
        &self.raw_value
    }

    #[doc="Get value as unescaped string."]
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.get_raw_value())
    }
}

pub struct Component {
    #[doc="The name of the component, such as `VCARD` or `VEVENT`."]
    pub name: String,

    #[doc="The component's properties."]
    pub props: HashMap<String, Vec<Property>>,

    #[doc="The component's child- or sub-components."]
    pub subcomponents: Vec<Component>
}

impl Component {
    fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    #[doc="Retrieve one property (from many) by key.
        Returns `None` if nothing is found."]
    pub fn single_prop(&self, key: &String) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    #[doc="Retrieve a mutable vector of properties for this key.
        Creates one (and inserts it into the component) if none exists."]
    pub fn all_props_mut(&mut self, key: String) -> &mut Vec<Property> {
        match self.props.entry(key) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.set(vec![])
        }
    }

    #[doc="Retrieve properties by key.
        Returns an empty slice if key doesn't exist."]
    pub fn all_props(&self, key: &String) -> &[Property] {
        static EMPTY: &'static [Property] = [];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v }

component_end -> String
    = "END:" v:value __ { v }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(String, Property)>
    = ps:contentline ++ eols __ { ps }

contentline -> (String, Property)
    = k:name p:params ":" v:value {
        (k, Property::new(p, v))
    }

name -> String
    = !"BEGIN" !"END" iana_token+ { match_str.into_string() }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k, v);
        };
        rv
    }

param -> (String, String)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => "".into_string()
        })
    }

param_name -> String
    = iana_token+ { match_str.into_string() }

param_value -> String
    = x:(quoted_string / param_text) { x }

param_text -> String
    = safe_char* { match_str.into_string() }

value -> String
    = value_char+ { match_str.into_string() }

quoted_string -> String
    = dquote x:quoted_content dquote { x }

quoted_content -> String
    = qsafe_char* { match_str.into_string() }

iana_token = ([a-zA-Z0-9] / "-")+
safe_char = !";" !":" !"," value_char
qsafe_char = !dquote value_char // FIXME

value_char = !eol .

eol = "\n" / "\r\n" / "\r"
dquote = "\""
eols = eol+
whitespace = " " / "\t"
__ = (eol / whitespace)*

"#)

#[doc="Parse a component. The error value is a human-readable message."]
pub fn parse_component(s: &String) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = s
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\r ", "").replace("\r\t", "");

    parser::component(unfolded.as_slice())
}

#[doc="Escape text for a VObject property value."]
pub fn escape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

#[doc="Unescape text from a VObject property value."]
pub fn unescape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}
<commit_msg>Rust nightly updates<commit_after>// DOCS

#![crate_name = "vobject"]
#![crate_type = "lib"]
#![license = "MIT"]
#![comment = "Parser for VObject and iCalendar."]

#![feature(phase)]
#[phase(plugin)]
extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};

pub struct Property {
    params: HashMap<String, String>,
    raw_value: String,
}

impl Property {
    fn new(params: HashMap<String, String>, raw_value: String) -> Property {
        Property {
            params: params,
            raw_value: raw_value
        }
    }

    #[doc="Get parameters."]
    pub fn get_params(&self) -> &HashMap<String, String> {
        &self.params
    }

    #[doc="Get value as unparsed string."]
    pub fn get_raw_value(&self) -> &String {
        &self.raw_value
    }

    #[doc="Get value as unescaped string."]
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.get_raw_value())
    }
}

pub struct Component {
    #[doc="The name of the component, such as `VCARD` or `VEVENT`."]
    pub name: String,

    #[doc="The component's properties."]
    pub props: HashMap<String, Vec<Property>>,

    #[doc="The component's child- or sub-components."]
    pub subcomponents: Vec<Component>
}

impl Component {
    fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    #[doc="Retrieve one property (from many) by key.
        Returns `None` if nothing is found."]
    pub fn single_prop(&self, key: &String) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    #[doc="Retrieve a mutable vector of properties for this key.
        Creates one (and inserts it into the component) if none exists."]
    pub fn all_props_mut(&mut self, key: String) -> &mut Vec<Property> {
        match self.props.entry(key) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.set(vec![])
        }
    }

    #[doc="Retrieve properties by key.
        Returns an empty slice if key doesn't exist."]
    pub fn all_props(&self, key: &String) -> &[Property] {
        static EMPTY: &'static [Property] = &[];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v }

component_end -> String
    = "END:" v:value __ { v }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(String, Property)>
    = ps:contentline ++ eols __ { ps }

contentline -> (String, Property)
    = k:name p:params ":" v:value {
        (k, Property::new(p, v))
    }

name -> String
    = !"BEGIN" !"END" iana_token+ { match_str.into_string() }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k, v);
        };
        rv
    }

param -> (String, String)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => "".into_string()
        })
    }

param_name -> String
    = iana_token+ { match_str.into_string() }

param_value -> String
    = x:(quoted_string / param_text) { x }

param_text -> String
    = safe_char* { match_str.into_string() }

value -> String
    = value_char+ { match_str.into_string() }

quoted_string -> String
    = dquote x:quoted_content dquote { x }

quoted_content -> String
    = qsafe_char* { match_str.into_string() }

iana_token = ([a-zA-Z0-9] / "-")+
safe_char = !";" !":" !"," value_char
qsafe_char = !dquote value_char // FIXME

value_char = !eol .

eol = "\n" / "\r\n" / "\r"
dquote = "\""
eols = eol+
whitespace = " " / "\t"
__ = (eol / whitespace)*

"#)

#[doc="Parse a component. The error value is a human-readable message."]
pub fn parse_component(s: &String) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = s
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\r ", "").replace("\r\t", "");

    parser::component(unfolded.as_slice())
}

#[doc="Escape text for a VObject property value."]
pub fn escape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

#[doc="Unescape text from a VObject property value."]
pub fn unescape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}
<|endoftext|>


<commit_before>use libc::{c_int, c_ushort, c_uchar, size_t};

use symbols::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: c_int = 5;
const HASH_MASK: c_int = 32767;

#[repr(C)]
pub struct ZopfliHash {
    head: Vec<c_int>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<c_ushort>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<c_int>,  /* Index to hash value at this index. */
    val: c_int,  /* Current hash value. */

    /* Fields with similar purpose as the above hash, but for the second hash with
    a value that is calculated differently.  */
    head2: Vec<c_int>,  /* Hash value to index of its most recent occurrence. */
    prev2: Vec<c_ushort>,  /* Index to index of prev. occurrence of same hash. */
    hashval2: Vec<c_int>,  /* Index to hash value at this index. */
    val2: c_int,  /* Current hash value. */

    same: Vec<c_ushort>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: size_t) -> ZopfliHash {
        ZopfliHash {
           head: vec![-1; 65536],
           prev: (0..window_size as c_ushort).collect::<Vec<_>>(),
           hashval: vec![-1; window_size],
           val: 0,

           /* Fields with similar purpose as the above hash, but for the second hash with
           a value that is calculated differently.  */
           head2: vec![-1; 65536],
           prev2: (0..window_size as c_ushort).collect::<Vec<_>>(),
           hashval2: vec![-1; window_size],
           val2: 0,

           same: vec![0; window_size],
       }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    pub fn update_val(&mut self, c: c_uchar) {
        self.val = ((self.val << HASH_SHIFT) ^ c as c_int) & HASH_MASK;
    }

    pub fn update(&mut self, array: *const c_uchar, pos: size_t, end: size_t) {
        let hpos = (pos & ZOPFLI_WINDOW_MASK) as usize;
        let mut amount: c_int = 0;

        let hash_value = if pos + ZOPFLI_MIN_MATCH as size_t <= end {
            unsafe { *array.offset((pos + ZOPFLI_MIN_MATCH as size_t - 1) as isize) }
        } else {
            0
        };
        self.update_val(hash_value);

        self.hashval[hpos] = self.val;

        let index = self.val as usize;

        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as c_ushort;
        } else {
            self.prev[hpos] = hpos as c_ushort;
        }

        self.head[index] = hpos as c_int;

        // Update "same".
        if self.same[((pos - 1) & ZOPFLI_WINDOW_MASK) as usize] > 1 {
            amount = self.same[((pos - 1) & ZOPFLI_WINDOW_MASK) as usize] as c_int - 1;
        }

        unsafe {
            while pos + amount as size_t + 1 < end && *array.offset(pos as isize) == *array.offset((pos + amount as size_t + 1) as isize) && amount < -1 {
                amount += 1;
            }
        }
        self.same[hpos] = amount as c_ushort;

        self.val2 = (((self.same[hpos] - ZOPFLI_MIN_MATCH) & 255) ^ self.val as c_ushort) as c_int;
        self.hashval2[hpos] = self.val2;

        let index2 = self.val2 as usize;
        if self.head2[index2] != -1 as i32 && self.hashval2[self.head2[index2] as usize] == self.val2 {
            self.prev2[hpos] = self.head2[index2] as c_ushort;
        } else {
            self.prev2[hpos] = hpos as c_ushort;
        }
        self.head2[index2] = hpos as c_int;
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn UpdateHashValue(h_ptr: *mut ZopfliHash, c: c_uchar) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.update_val(c);
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliWarmupHash(array: *const c_uchar, pos: size_t, end: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    let c = unsafe { *array.offset((pos + 0) as isize) };
    h.update_val(c);

    if pos + 1 < end {
        let c = unsafe { *array.offset((pos + 1) as isize) };
        h.update_val(c);
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliUpdateHash(array: *const c_uchar, pos: size_t, end: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.update(array, pos, end);
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHead(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.head.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashPrev(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.prev.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHashval(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.hashval.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashVal(h_ptr: *mut ZopfliHash) -> c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.val
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHead2(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.head2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashPrev2(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.prev2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHashval2(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.hashval2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashVal2(h_ptr: *mut ZopfliHash) -> c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.val2
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashSame(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.same.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCleanHash(ptr: *mut ZopfliHash) {
    if ptr.is_null() { return }
    unsafe { Box::from_raw(ptr); }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliInitHash(window_size: size_t) -> *mut ZopfliHash {
    Box::into_raw(Box::new(ZopfliHash::new(window_size)))
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliResetHash(window_size: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };

    h.val = 0;
    h.head = vec![-1; 65536];
    h.prev = (0..window_size as c_ushort).collect::<Vec<_>>();
    h.hashval = vec![-1; window_size];

    h.same = vec![0; window_size];

    h.val2 = 0;
    h.head2 = vec![-1; 65536];
    h.prev2 = (0..window_size as c_ushort).collect::<Vec<_>>();
    h.hashval2 = vec![-1; window_size];
}
<commit_msg>Turn array into a slice instead of pointer math<commit_after>use std::slice;

use libc::{c_int, c_ushort, c_uchar, size_t};

use symbols::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH};

const HASH_SHIFT: c_int = 5;
const HASH_MASK: c_int = 32767;

#[repr(C)]
pub struct ZopfliHash {
    head: Vec<c_int>,  /* Hash value to index of its most recent occurrence. */
    prev: Vec<c_ushort>,  /* Index to index of prev. occurrence of same hash. */
    hashval: Vec<c_int>,  /* Index to hash value at this index. */
    val: c_int,  /* Current hash value. */

    /* Fields with similar purpose as the above hash, but for the second hash with
    a value that is calculated differently.  */
    head2: Vec<c_int>,  /* Hash value to index of its most recent occurrence. */
    prev2: Vec<c_ushort>,  /* Index to index of prev. occurrence of same hash. */
    hashval2: Vec<c_int>,  /* Index to hash value at this index. */
    val2: c_int,  /* Current hash value. */

    same: Vec<c_ushort>,  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new(window_size: size_t) -> ZopfliHash {
        ZopfliHash {
           head: vec![-1; 65536],
           prev: (0..window_size as c_ushort).collect::<Vec<_>>(),
           hashval: vec![-1; window_size],
           val: 0,

           /* Fields with similar purpose as the above hash, but for the second hash with
           a value that is calculated differently.  */
           head2: vec![-1; 65536],
           prev2: (0..window_size as c_ushort).collect::<Vec<_>>(),
           hashval2: vec![-1; window_size],
           val2: 0,

           same: vec![0; window_size],
       }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    pub fn update_val(&mut self, c: c_uchar) {
        self.val = ((self.val << HASH_SHIFT) ^ c as c_int) & HASH_MASK;
    }

    pub fn update(&mut self, array: *const c_uchar, pos: size_t, end: size_t) {
        let hpos = (pos & ZOPFLI_WINDOW_MASK) as usize;
        let mut amount: c_int = 0;

        let hash_value = if pos + ZOPFLI_MIN_MATCH as size_t <= end {
            unsafe { *array.offset((pos + ZOPFLI_MIN_MATCH as size_t - 1) as isize) }
        } else {
            0
        };
        self.update_val(hash_value);

        self.hashval[hpos] = self.val;

        let index = self.val as usize;

        if self.head[index] != -1 && self.hashval[self.head[index] as usize] == self.val {
            self.prev[hpos] = self.head[index] as c_ushort;
        } else {
            self.prev[hpos] = hpos as c_ushort;
        }

        self.head[index] = hpos as c_int;

        // Update "same".
        if self.same[((pos - 1) & ZOPFLI_WINDOW_MASK) as usize] > 1 {
            amount = self.same[((pos - 1) & ZOPFLI_WINDOW_MASK) as usize] as c_int - 1;
        }

        unsafe {
            while pos + amount as size_t + 1 < end && *array.offset(pos as isize) == *array.offset((pos + amount as size_t + 1) as isize) && amount < -1 {
                amount += 1;
            }
        }
        self.same[hpos] = amount as c_ushort;

        self.val2 = (((self.same[hpos] - ZOPFLI_MIN_MATCH) & 255) ^ self.val as c_ushort) as c_int;
        self.hashval2[hpos] = self.val2;

        let index2 = self.val2 as usize;
        if self.head2[index2] != -1 as i32 && self.hashval2[self.head2[index2] as usize] == self.val2 {
            self.prev2[hpos] = self.head2[index2] as c_ushort;
        } else {
            self.prev2[hpos] = hpos as c_ushort;
        }
        self.head2[index2] = hpos as c_int;
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn UpdateHashValue(h_ptr: *mut ZopfliHash, c: c_uchar) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.update_val(c);
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliWarmupHash(array: *const c_uchar, pos: size_t, end: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    let arr = unsafe { slice::from_raw_parts(array, end) };
    let c = arr[pos];
    h.update_val(c);

    if pos + 1 < end {
        let c = arr[pos + 1];
        h.update_val(c);
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliUpdateHash(array: *const c_uchar, pos: size_t, end: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.update(array, pos, end);
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHead(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.head.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashPrev(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.prev.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHashval(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.hashval.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashVal(h_ptr: *mut ZopfliHash) -> c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.val
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHead2(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.head2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashPrev2(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.prev2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashHashval2(h_ptr: *mut ZopfliHash) -> *mut c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.hashval2.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashVal2(h_ptr: *mut ZopfliHash) -> c_int {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.val2
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliHashSame(h_ptr: *mut ZopfliHash) -> *mut c_ushort {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };
    h.same.as_mut_ptr()
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliCleanHash(ptr: *mut ZopfliHash) {
    if ptr.is_null() { return }
    unsafe { Box::from_raw(ptr); }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliInitHash(window_size: size_t) -> *mut ZopfliHash {
    Box::into_raw(Box::new(ZopfliHash::new(window_size)))
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliResetHash(window_size: size_t, h_ptr: *mut ZopfliHash) {
    let h = unsafe {
        assert!(!h_ptr.is_null());
        &mut *h_ptr
    };

    h.val = 0;
    h.head = vec![-1; 65536];
    h.prev = (0..window_size as c_ushort).collect::<Vec<_>>();
    h.hashval = vec![-1; window_size];

    h.same = vec![0; window_size];

    h.val2 = 0;
    h.head2 = vec![-1; 65536];
    h.prev2 = (0..window_size as c_ushort).collect::<Vec<_>>();
    h.hashval2 = vec![-1; window_size];
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
#[macro_use]
extern crate rux;
extern crate num_cpus;
extern crate env_logger;

use rux::{RawFd, Reset};
use rux::buf::ByteBuffer;
use rux::handler::*;
use rux::mux::*;
use rux::poll::*;
use rux::sys::socket::*;
use rux::prop::server::*;
use rux::system::System;

const BUF_SIZE: usize = 2048;
const EPOLL_BUF_CAP: usize = 2048;
const EPOLL_LOOP_MS: isize = -1;
const MAX_CONN: usize = 2048;

/// Handler that echoes incoming bytes
///
/// For benchmarking I/O throuput and latency
pub struct EchoHandler;

impl<'a> Handler<MuxEvent<'a, ByteBuffer>, MuxCmd> for EchoHandler {
  fn on_next(&mut self, event: MuxEvent<'a, ByteBuffer>) -> MuxCmd {

    let fd = event.fd;
    let kind = event.kind;
    let buffer = event.resource;

    if kind.contains(EPOLLHUP) {
      trace!("socket's fd {}: EPOLLHUP", fd);
      return MuxCmd::Close;
    }

    if kind.contains(EPOLLERR) {
      error!("socket's fd {}: EPOLERR", fd);
      return MuxCmd::Close;
    }

    if kind.contains(EPOLLIN) {
      if let Some(n) = eintr!(recv(fd, From::from(&mut *buffer), MSG_DONTWAIT)).unwrap() {
        buffer.extend(n);
      }
    }

    if kind.contains(EPOLLOUT) {
      if buffer.is_readable() {
        if let Some(cnt) = eintr!(send(fd, From::from(&*buffer), MSG_DONTWAIT)).unwrap() {
          buffer.consume(cnt);
        }
      }
    }

    MuxCmd::Keep
  }
}

impl EpollHandler for EchoHandler {
  fn interests() -> EpollEventKind {
    EPOLLIN | EPOLLOUT | EPOLLET
  }

  fn with_epfd(&mut self, _: EpollFd) {

  }
}

impl Reset for EchoHandler {
  fn reset(&mut self) {}
}

#[derive(Clone, Debug)]
struct EchoFactory;

impl<'a> HandlerFactory<'a, EchoHandler, ByteBuffer> for EchoFactory {

  fn new_resource(&self) -> ByteBuffer {
    ByteBuffer::with_capacity(BUF_SIZE)
  }

  fn new_handler(&mut self, _: EpollFd, _: RawFd) -> EchoHandler {
    EchoHandler
  }
}

fn main() {

  ::env_logger::init().unwrap();

  info!("BUF_SIZE: {}; EPOLL_BUF_CAP: {}; EPOLL_LOOP_MS: {}; MAX_CONN: {}",
        BUF_SIZE,
        EPOLL_BUF_CAP,
        EPOLL_LOOP_MS,
        MAX_CONN);

  let config = ServerConfig::tcp(("127.0.0.1", 9999))
    .unwrap()
    .max_conn(MAX_CONN)
    .io_threads(::std::cmp::max(1, ::num_cpus::get() / 2))
    // .io_threads(1)
    .epoll_config(EpollConfig {
      loop_ms: EPOLL_LOOP_MS,
      buffer_capacity: EPOLL_BUF_CAP,
    });

  let server = Server::new_with(config, |epfd| {
      SyncMux::new(MAX_CONN, epfd, EchoFactory)
    })
    .unwrap();

  System::build(server).start().unwrap();
}
<commit_msg>fixed example<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate rux;
extern crate num_cpus;
extern crate env_logger;

use rux::{RawFd, Reset};
use rux::error::*;
use rux::buf::ByteBuffer;
use rux::handler::*;
use rux::mux::*;
use rux::epoll::*;
use rux::sys::socket::*;
use rux::prop::server::*;
use rux::system::System;

const BUF_SIZE: usize = 2048;
const EPOLL_BUF_CAP: usize = 2048;
const EPOLL_LOOP_MS: isize = -1;
const MAX_CONN: usize = 2048;

/// Handler that echoes incoming bytes
///
/// For benchmarking I/O throuput and latency
pub struct EchoHandler;

impl<'a> Handler<MuxEvent<'a, ByteBuffer>, MuxCmd> for EchoHandler {
  fn on_next(&mut self, event: MuxEvent<'a, ByteBuffer>) -> MuxCmd {

    let fd = event.fd;
    let kind = event.kind;
    let buffer = event.resource;

    if kind.contains(EPOLLHUP) {
      trace!("socket's fd {}: EPOLLHUP", fd);
      return MuxCmd::Close;
    }

    if kind.contains(EPOLLERR) {
      error!("socket's fd {}: EPOLERR", fd);
      return MuxCmd::Close;
    }

    if kind.contains(EPOLLIN) {
      if let Some(n) = eintr!(recv(fd, From::from(&mut *buffer), MSG_DONTWAIT)).unwrap() {
        buffer.extend(n);
      }
    }

    if kind.contains(EPOLLOUT) {
      if buffer.is_readable() {
        if let Some(cnt) = eintr!(send(fd, From::from(&*buffer), MSG_DONTWAIT)).unwrap() {
          buffer.consume(cnt);
        }
      }
    }

    MuxCmd::Keep
  }
}

impl EpollHandler for EchoHandler {
  fn interests() -> EpollEventKind {
    EPOLLIN | EPOLLOUT | EPOLLET
  }

  fn with_epfd(&mut self, _: EpollFd) {

  }
}

impl Reset for EchoHandler {
  fn reset(&mut self) {}
}

#[derive(Clone, Debug)]
struct EchoFactory;

impl<'a> HandlerFactory<'a, EchoHandler, ByteBuffer> for EchoFactory {

  fn new_resource(&self) -> ByteBuffer {
    ByteBuffer::with_capacity(BUF_SIZE)
  }

  fn new_handler(&mut self, _: EpollFd, _: RawFd) -> EchoHandler {
    EchoHandler
  }
}

fn main() {

  ::env_logger::init().unwrap();

  info!("BUF_SIZE: {}; EPOLL_BUF_CAP: {}; EPOLL_LOOP_MS: {}; MAX_CONN: {}",
        BUF_SIZE,
        EPOLL_BUF_CAP,
        EPOLL_LOOP_MS,
        MAX_CONN);

  let config = ServerConfig::tcp(("127.0.0.1", 9999))
    .unwrap()
    .max_conn(MAX_CONN)
    //.io_threads(::std::cmp::max(1, ::num_cpus::get() / 2))
    .io_threads(1)
    .epoll_config(EpollConfig {
      loop_ms: EPOLL_LOOP_MS,
      buffer_capacity: EPOLL_BUF_CAP,
    });

  let server = Server::new_with(config, |epfd| {
      SyncMux::new(MAX_CONN, epfd, EchoFactory)
    })
    .unwrap();

  System::build(server).start().unwrap();
}
<|endoftext|>


<commit_before>extern crate time;
#[cfg(test)]
extern crate quickcheck;

use std::cmp;
use std::fmt;

// Implementation of Hybrid Logical Clocks, based on the paper "Logical Physical Clocks
// and Consistent Snapshots in Globally Distributed Databases".
//

trait ClockSource {
    type Time : Ord + Copy;
    fn now(&mut self) -> Self::Time;
}

#[derive(Debug,Clone,Copy,PartialEq,Eq,PartialOrd,Ord)]
struct Timestamp<T>(T, u32);

struct Clock<S: ClockSource> {
    src: S,
    latest: Timestamp<S::Time>,
}

impl<S: ClockSource> Clock<S> {
    pub fn new(mut src: S) -> Self {
        let init = src.now();
        Clock {
            src: src,
            latest: Timestamp(init, 0),
        }
    }

    pub fn on_send(&mut self) -> Timestamp<S::Time> {
        let pt = self.src.now();
        let lp = self.latest.clone();
        self.latest.0 = cmp::max(lp.0, pt);
        self.latest.1 = if lp.0 == self.latest.0 {
            lp.1 + 1
        } else {
            0
        };

        self.latest
    }
    pub fn on_recv(&mut self, msg: &Timestamp<S::Time>) -> Timestamp<S::Time> {
        let pt = self.src.now();
        let lp = self.latest.clone();

        self.latest.0 = cmp::max(cmp::max(lp.0, msg.0), pt);
        self.latest.1 = match (self.latest.0 == lp.0, self.latest.0 == msg.0) {
            (true, true) => cmp::max(self.latest.1, msg.1) + 1,
            (true, false) => self.latest.1 + 1,
            (false, true) => msg.1 + 1,
            (false, false) => 0,
        };

        self.latest.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::{Clock, ClockSource, Timestamp};
    use std::cell::Cell;
    use std::cmp::{Ord, Ordering};
    use quickcheck;

    struct ManualClock(Cell<u64>);

    impl<'a> ClockSource for &'a ManualClock {
        type Time = u64;
        fn now(&mut self) -> Self::Time {
            self.0.get()
        }
    }

    #[test]
    fn fig_6_proc_0_a() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        src.0.set(10);
        assert_eq!(clock.on_send(), Timestamp(10, 0))
    }

    #[test]
    fn fig_6_proc_1_a() {
        let src = ManualClock(Cell::new(1));
        let mut clock = Clock::new(&src);
        assert_eq!(clock.on_recv(&Timestamp(10, 0)), Timestamp(10, 1))
    }

    #[test]
    fn fig_6_proc_1_b() {
        let src = ManualClock(Cell::new(1));
        let mut clock = Clock::new(&src);
        let _ = clock.on_recv(&Timestamp(10, 0));
        src.0.set(2);
        assert_eq!(clock.on_send(), Timestamp(10, 2))
    }

    #[test]
    fn fig_6_proc_2_b() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        clock.latest = Timestamp(1, 0);
        src.0.set(2);
        assert_eq!(clock.on_recv(&Timestamp(10, 2)), Timestamp(10, 3))
    }

    #[test]
    fn fig_6_proc_2_c() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        src.0.set(2);
        let _ = clock.on_recv(&Timestamp(10, 2));
        src.0.set(3);
        assert_eq!(clock.on_send(), Timestamp(10, 4))
    }

    #[test]
    fn all_sources_same() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        assert_eq!(clock.on_recv(&Timestamp(0, 5)), Timestamp(0, 6))
    }

    #[test]
    fn handles_time_going_backwards_on_send() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(9);
        assert_eq!(clock.on_send(), Timestamp(10, 2))
    }

    #[test]
    fn handles_time_going_backwards_on_recv() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(9);
        assert_eq!(clock.on_recv(&Timestamp(0, 0)), Timestamp(10, 2))
    }

    #[test]
    fn handles_time_going_forwards_on_send() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(12);
        assert_eq!(clock.on_send(), Timestamp(12, 0))
    }

    #[test]
    fn handles_time_going_forwards_on_recv() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(12);
        assert_eq!(clock.on_recv(&Timestamp(0, 0)), Timestamp(12, 0))
    }
}
<commit_msg>Add wall clock.<commit_after>extern crate time;
#[cfg(test)]
extern crate quickcheck;

use std::cmp;
use std::fmt;

// Implementation of Hybrid Logical Clocks, based on the paper "Logical Physical Clocks
// and Consistent Snapshots in Globally Distributed Databases".
//

pub trait ClockSource {
    type Time : Ord + Copy;
    fn now(&mut self) -> Self::Time;
}

#[derive(Debug,Clone,Copy,PartialEq,Eq,PartialOrd,Ord)]
pub struct Timestamp<T>(T, u32);

#[derive(Debug,Clone,Copy,PartialEq,Eq,PartialOrd,Ord)]
pub struct Wall;

pub struct Clock<S: ClockSource> {
    src: S,
    latest: Timestamp<S::Time>,
}

impl Clock<Wall> {
    pub fn wall() -> Clock<Wall> {
        Clock::new(Wall)
    }
}

impl<S: ClockSource> Clock<S> {
    pub fn new(mut src: S) -> Self {
        let init = src.now();
        Clock {
            src: src,
            latest: Timestamp(init, 0),
        }
    }

    pub fn on_send(&mut self) -> Timestamp<S::Time> {
        let pt = self.src.now();
        let lp = self.latest.clone();
        self.latest.0 = cmp::max(lp.0, pt);
        self.latest.1 = if lp.0 == self.latest.0 {
            lp.1 + 1
        } else {
            0
        };

        self.latest
    }
    pub fn on_recv(&mut self, msg: &Timestamp<S::Time>) -> Timestamp<S::Time> {
        let pt = self.src.now();
        let lp = self.latest.clone();

        self.latest.0 = cmp::max(cmp::max(lp.0, msg.0), pt);
        self.latest.1 = match (self.latest.0 == lp.0, self.latest.0 == msg.0) {
            (true, true) => cmp::max(self.latest.1, msg.1) + 1,
            (true, false) => self.latest.1 + 1,
            (false, true) => msg.1 + 1,
            (false, false) => 0,
        };

        self.latest.clone()
    }
}

impl ClockSource for Wall {
    type Time = time::Timespec;
    fn now(&mut self) -> Self::Time {
        time::get_time()
    }
}

#[cfg(test)]
mod tests {
    use super::{Clock, ClockSource, Timestamp};
    use std::cell::Cell;
    use std::cmp::{Ord, Ordering};
    use quickcheck;

    struct ManualClock(Cell<u64>);

    impl<'a> ClockSource for &'a ManualClock {
        type Time = u64;
        fn now(&mut self) -> Self::Time {
            self.0.get()
        }
    }

    #[test]
    fn fig_6_proc_0_a() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        src.0.set(10);
        assert_eq!(clock.on_send(), Timestamp(10, 0))
    }

    #[test]
    fn fig_6_proc_1_a() {
        let src = ManualClock(Cell::new(1));
        let mut clock = Clock::new(&src);
        assert_eq!(clock.on_recv(&Timestamp(10, 0)), Timestamp(10, 1))
    }

    #[test]
    fn fig_6_proc_1_b() {
        let src = ManualClock(Cell::new(1));
        let mut clock = Clock::new(&src);
        let _ = clock.on_recv(&Timestamp(10, 0));
        src.0.set(2);
        assert_eq!(clock.on_send(), Timestamp(10, 2))
    }

    #[test]
    fn fig_6_proc_2_b() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        clock.latest = Timestamp(1, 0);
        src.0.set(2);
        assert_eq!(clock.on_recv(&Timestamp(10, 2)), Timestamp(10, 3))
    }

    #[test]
    fn fig_6_proc_2_c() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        src.0.set(2);
        let _ = clock.on_recv(&Timestamp(10, 2));
        src.0.set(3);
        assert_eq!(clock.on_send(), Timestamp(10, 4))
    }

    #[test]
    fn all_sources_same() {
        let src = ManualClock(Cell::new(0));
        let mut clock = Clock::new(&src);
        assert_eq!(clock.on_recv(&Timestamp(0, 5)), Timestamp(0, 6))
    }

    #[test]
    fn handles_time_going_backwards_on_send() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(9);
        assert_eq!(clock.on_send(), Timestamp(10, 2))
    }

    #[test]
    fn handles_time_going_backwards_on_recv() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(9);
        assert_eq!(clock.on_recv(&Timestamp(0, 0)), Timestamp(10, 2))
    }

    #[test]
    fn handles_time_going_forwards_on_send() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(12);
        assert_eq!(clock.on_send(), Timestamp(12, 0))
    }

    #[test]
    fn handles_time_going_forwards_on_recv() {
        let src = ManualClock(Cell::new(10));
        let mut clock = Clock::new(&src);
        let _ = clock.on_send();
        src.0.set(12);
        assert_eq!(clock.on_recv(&Timestamp(0, 0)), Timestamp(12, 0))
    }
}
<|endoftext|>


<commit_before>import ptr::addr_of;
import comm::{port, chan, methods};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::uv_global_loop;
import comm::{methods};
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};

const timeout: uint = 2000;

/**
A quick hack URI type
*/
type Uri = {
    host: str,
    path: str
};

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+uri: Uri) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, uri)
}

class HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let uri: Uri;
    let parser: Parser;

    new(resolver: DnsResolver, +connection_factory: CF, +uri: Uri) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.uri = uri;

        self.parser = Parser();
    }

    fn begin(cb: fn(+RequestEvent)) {
        #debug("http_client: looking up uri %?", self.uri);
        let ip_addr = {
            let ip_addrs = self.resolve_ip_addr(self.uri.host);
            if ip_addrs.is_ok() {
                let ip_addrs = result::unwrap(ip_addrs);
                // FIXME: This log crashes
                //#debug("http_client: got IP addresses for %?: %?", self.uri, ip_addrs);
                if ip_addrs.is_not_empty() {
                    // FIXME: Which address should we really pick?
                    let best_ip = do ip_addrs.find |ip| {
                        alt ip {
                          ipv4(*) { true }
                          ipv6(*) { false }
                        }
                    };

                    if best_ip.is_some() {
                        option::unwrap(best_ip)
                    } else {
                        // FIXME: Need test
                        cb(Error(ErrorMisc));
                        ret;
                    }
                } else {
                    #debug("http_client: got no IP addresses for %?", self.uri);
                    // FIXME: Need test
                    cb(Error(ErrorMisc));
                    ret;
                }
            } else {
                #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
                cb(Error(ErrorDnsResolution));
                ret;
            }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.uri);

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                ret;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = #fmt("GET %s HTTP/1.0\u000D\u000AHost: %s\u000D\u000A\u000D\u000A",
                                  self.uri.path, self.uri.host);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::bytes(request_header);
        alt socket.write(request_header_bytes) {
          result::ok(*) { }
          result::err(e) {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            ret;
          }
        }

        let read_port = {
            let read_port = socket.read_start();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                ret;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = result::unwrap(next_data);
                #debug("next_data: %?", next_data);
                self.parser.execute(next_data, &callbacks);
                let the_payload = Payload(~mut some(next_data));
                cb(the_payload);
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                alt next_data {
                  result::err({err_name: "EOF", _}) {
                    break;
                  }
                  _ {
                    // FIXME: Need tests and error handling
                    socket.read_stop(read_port);
                    cb(Error(ErrorMisc));
                    ret;
                  }
                }
            }
        }
        socket.read_stop(read_port);
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        #debug("on_header_field");
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        #debug("on_header_value");
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) -> ~[RequestEvent] {
    let mut events = ~[];
    do request.begin |event| {
        vec::push(events, event)
    }
    ret events;
}

#[test]
fn test_resolve_error() {
    let uri = {
        host: "example.com_not_real",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
fn test_connect_error() {
    let uri = {
        // This address is invalid because the first octet
        // of a class A address cannot be 0
        host: "0.42.42.42",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
fn test_connect_success() {
    let uri = {
        host: "example.com",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    for events.each |ev| {
        alt ev {
          Error(*) { fail }
          _ { }
        }
    }
}

#[test]
#[ignore(reason = "ICE")]
fn test_simple_response() {
    let uri = {
        host: "whatever",
        path: "/"
    };

    let mock_connection: MockConnection = {
        write_fn: |data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = "HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let mock_connection_factory: MockConnectionFactory = {
        connect_fn: |ip, port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}<commit_msg>Remove some logging<commit_after>import ptr::addr_of;
import comm::{port, chan, methods};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::uv_global_loop;
import comm::{methods};
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};

const timeout: uint = 2000;

/**
A quick hack URI type
*/
type Uri = {
    host: str,
    path: str
};

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+uri: Uri) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, uri)
}

class HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let uri: Uri;
    let parser: Parser;

    new(resolver: DnsResolver, +connection_factory: CF, +uri: Uri) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.uri = uri;

        self.parser = Parser();
    }

    fn begin(cb: fn(+RequestEvent)) {
        #debug("http_client: looking up uri %?", self.uri);
        let ip_addr = {
            let ip_addrs = self.resolve_ip_addr(self.uri.host);
            if ip_addrs.is_ok() {
                let ip_addrs = result::unwrap(ip_addrs);
                // FIXME: This log crashes
                //#debug("http_client: got IP addresses for %?: %?", self.uri, ip_addrs);
                if ip_addrs.is_not_empty() {
                    // FIXME: Which address should we really pick?
                    let best_ip = do ip_addrs.find |ip| {
                        alt ip {
                          ipv4(*) { true }
                          ipv6(*) { false }
                        }
                    };

                    if best_ip.is_some() {
                        option::unwrap(best_ip)
                    } else {
                        // FIXME: Need test
                        cb(Error(ErrorMisc));
                        ret;
                    }
                } else {
                    #debug("http_client: got no IP addresses for %?", self.uri);
                    // FIXME: Need test
                    cb(Error(ErrorMisc));
                    ret;
                }
            } else {
                #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
                cb(Error(ErrorDnsResolution));
                ret;
            }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.uri);

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                ret;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = #fmt("GET %s HTTP/1.0\u000D\u000AHost: %s\u000D\u000A\u000D\u000A",
                                  self.uri.path, self.uri.host);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::bytes(request_header);
        alt socket.write(request_header_bytes) {
          result::ok(*) { }
          result::err(e) {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            ret;
          }
        }

        let read_port = {
            let read_port = socket.read_start();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                ret;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = result::unwrap(next_data);
                self.parser.execute(next_data, &callbacks);
                let the_payload = Payload(~mut some(next_data));
                cb(the_payload);
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                alt next_data {
                  result::err({err_name: "EOF", _}) {
                    break;
                  }
                  _ {
                    // FIXME: Need tests and error handling
                    socket.read_stop(read_port);
                    cb(Error(ErrorMisc));
                    ret;
                  }
                }
            }
        }
        socket.read_stop(read_port);
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        #debug("on_header_field");
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        #debug("on_header_value");
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) -> ~[RequestEvent] {
    let mut events = ~[];
    do request.begin |event| {
        vec::push(events, event)
    }
    ret events;
}

#[test]
fn test_resolve_error() {
    let uri = {
        host: "example.com_not_real",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
fn test_connect_error() {
    let uri = {
        // This address is invalid because the first octet
        // of a class A address cannot be 0
        host: "0.42.42.42",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
fn test_connect_success() {
    let uri = {
        host: "example.com",
        path: "/"
    };

    let request = uv_http_request(uri);
    let events = sequence(request);

    for events.each |ev| {
        alt ev {
          Error(*) { fail }
          _ { }
        }
    }
}

#[test]
#[ignore(reason = "ICE")]
fn test_simple_response() {
    let uri = {
        host: "whatever",
        path: "/"
    };

    let mock_connection: MockConnection = {
        write_fn: |data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = "HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let mock_connection_factory: MockConnectionFactory = {
        connect_fn: |ip, port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}<|endoftext|>


<commit_before>//! [POST /_matrix/identity/v2/lookup](https://matrix.org/docs/spec/identity_service/0.3.0#post-matrix-identity-v2-lookup)

use std::collections::BTreeMap;

use ruma_api::ruma_api;
use ruma_identifiers::UserId;

use crate::lookup::IdentifierHashingAlgorithm;

ruma_api! {
    metadata: {
        description: "Looks up the set of Matrix User IDs which have bound the 3PIDs given, if bindings are available.",
        method: POST,
        name: "lookup_3pid",
        path: "/_matrix/identity/v2/lookup",
        authentication: AccessToken,
        rate_limited: false,
    }

    request: {
        /// The algorithm the client is using to encode the `addresses`. This should be one of the
        /// available options from `/hash_details`.
        pub algorithm: &'a IdentifierHashingAlgorithm,

        /// The pepper from `/hash_details`. This is required even when the `algorithm` does not
        /// make use of it.
        pub pepper: &'a str,

        /// The addresses to look up. The format of the entries here depend on the `algorithm`
        /// used. Note that queries which have been incorrectly hashed or formatted will lead to no
        /// matches.
        pub addresses: &'a [String],
    }

    response: {
        /// Any applicable mappings of `addresses` to Matrix User IDs. Addresses which do not have
        /// associations will not be included, which can make this property be an empty object.
        pub mappings: BTreeMap<String, UserId>,
    }
}

impl<'a> Request<'a> {
    /// Create a `Request` with algorithm, pepper and addresses to loop up.
    pub fn new(
        algorithm: &'a IdentifierHashingAlgorithm,
        pepper: &'a str,
        addresses: &'a [String],
    ) -> Self {
        Self { algorithm, pepper, addresses }
    }
}

impl Response {
    /// Create a `Response` with the BTreeMap which map addresses from the request which were
    /// found to their corresponding User IDs.
    pub fn new(mappings: BTreeMap<String, UserId>) -> Self {
        Self { mappings }
    }
}
<commit_msg>Fix identity-service spec link<commit_after>//! [POST /_matrix/identity/v2/lookup](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-lookup)

use std::collections::BTreeMap;

use ruma_api::ruma_api;
use ruma_identifiers::UserId;

use crate::lookup::IdentifierHashingAlgorithm;

ruma_api! {
    metadata: {
        description: "Looks up the set of Matrix User IDs which have bound the 3PIDs given, if bindings are available.",
        method: POST,
        name: "lookup_3pid",
        path: "/_matrix/identity/v2/lookup",
        authentication: AccessToken,
        rate_limited: false,
    }

    request: {
        /// The algorithm the client is using to encode the `addresses`. This should be one of the
        /// available options from `/hash_details`.
        pub algorithm: &'a IdentifierHashingAlgorithm,

        /// The pepper from `/hash_details`. This is required even when the `algorithm` does not
        /// make use of it.
        pub pepper: &'a str,

        /// The addresses to look up. The format of the entries here depend on the `algorithm`
        /// used. Note that queries which have been incorrectly hashed or formatted will lead to no
        /// matches.
        pub addresses: &'a [String],
    }

    response: {
        /// Any applicable mappings of `addresses` to Matrix User IDs. Addresses which do not have
        /// associations will not be included, which can make this property be an empty object.
        pub mappings: BTreeMap<String, UserId>,
    }
}

impl<'a> Request<'a> {
    /// Create a `Request` with algorithm, pepper and addresses to loop up.
    pub fn new(
        algorithm: &'a IdentifierHashingAlgorithm,
        pepper: &'a str,
        addresses: &'a [String],
    ) -> Self {
        Self { algorithm, pepper, addresses }
    }
}

impl Response {
    /// Create a `Response` with the BTreeMap which map addresses from the request which were
    /// found to their corresponding User IDs.
    pub fn new(mappings: BTreeMap<String, UserId>) -> Self {
        Self { mappings }
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface, MatrixTrait, PatternTrait, Rectangle};
use markup5ever::local_name;

use crate::allowed_url::{Fragment, Href};
use crate::aspect_ratio::AspectRatio;
use crate::drawing_ctx::DrawingCtx;
use crate::error::{NodeError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{ParseError, ParseValue};
use crate::property_bag::PropertyBag;
use crate::rect::{IRect, RectangleExt};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::viewbox::ViewBox;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: Cell<AspectRatio>,
    href: RefCell<Option<Href>>,
}

impl Default for Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    fn default() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: Cell::new(AspectRatio::default()),
            href: RefCell::new(None),
        }
    }
}

impl Image {
    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .acquired_nodes()
            .get_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &DrawingCtx,
        bounds: &IRect,
        unclipped_bounds: &IRect,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let surface = if let Href::PlainUrl(ref url) = *href {
            // FIXME: translate the error better here
            draw_ctx
                .lookup_image(&url)
                .map_err(|_| FilterError::InvalidInput)?
        } else {
            unreachable!();
        };

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let aspect = self.aspect.get();
        let (x, y, w, h) = aspect.compute(
            &ViewBox::new(
                0.0,
                0.0,
                f64::from(surface.width()),
                f64::from(surface.height()),
            ),
            &Rectangle::new(
                f64::from(unclipped_bounds.x0),
                f64::from(unclipped_bounds.y0),
                f64::from(unclipped_bounds.x1 - unclipped_bounds.x0),
                f64::from(unclipped_bounds.y1 - unclipped_bounds.y0),
            ),
        );

        if w.approx_eq_cairo(&0.0) || h.approx_eq_cairo(&0.0) {
            return Ok(output_surface);
        }

        let ptn = surface.to_cairo_pattern();
        let mut matrix = cairo::Matrix::new(
            w / f64::from(surface.width()),
            0f64,
            0f64,
            h / f64::from(surface.height()),
            x,
            y,
        );
        matrix.invert();
        ptn.set_matrix(matrix);

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source(&ptn);
        cr.paint();

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("preserveAspectRatio") => self.aspect.set(attr.parse(value)?),

                // "path" is used by some older Adobe Illustrator versions
                local_name!("xlink:href") | local_name!("path") => {
                    let href = Href::parse(value).map_err(|_| {
                        NodeError::parse_error(attr, ParseError::new("could not parse href"))
                    })?;

                    *self.href.borrow_mut() = Some(href);
                }

                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        let href_borrow = self.href.borrow();
        let href_opt = href_borrow.as_ref();

        if let Some(href) = href_opt {
            let output_surface = match *href {
                Href::PlainUrl(_) => {
                    let unclipped_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
                    self.render_external_image(ctx, draw_ctx, &bounds, &unclipped_bounds, href)?
                }
                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
            };

            Ok(FilterResult {
                name: self.base.result.borrow().clone(),
                output: FilterOutput {
                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                    bounds,
                },
            })
        } else {
            Err(FilterError::InvalidInput)
        }
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<commit_msg>filters/image: remove interior mutability<commit_after>use cairo::{self, ImageSurface, MatrixTrait, PatternTrait, Rectangle};
use markup5ever::local_name;

use crate::allowed_url::{Fragment, Href};
use crate::aspect_ratio::AspectRatio;
use crate::drawing_ctx::DrawingCtx;
use crate::error::{NodeError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{ParseError, ParseValue};
use crate::property_bag::PropertyBag;
use crate::rect::{IRect, RectangleExt};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::viewbox::ViewBox;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: AspectRatio,
    href: Option<Href>,
}

impl Default for Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    fn default() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: AspectRatio::default(),
            href: None,
        }
    }
}

impl Image {
    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .acquired_nodes()
            .get_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &DrawingCtx,
        bounds: &IRect,
        unclipped_bounds: &IRect,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let surface = if let Href::PlainUrl(ref url) = *href {
            // FIXME: translate the error better here
            draw_ctx
                .lookup_image(&url)
                .map_err(|_| FilterError::InvalidInput)?
        } else {
            unreachable!();
        };

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let (x, y, w, h) = self.aspect.compute(
            &ViewBox::new(
                0.0,
                0.0,
                f64::from(surface.width()),
                f64::from(surface.height()),
            ),
            &Rectangle::new(
                f64::from(unclipped_bounds.x0),
                f64::from(unclipped_bounds.y0),
                f64::from(unclipped_bounds.x1 - unclipped_bounds.x0),
                f64::from(unclipped_bounds.y1 - unclipped_bounds.y0),
            ),
        );

        if w.approx_eq_cairo(&0.0) || h.approx_eq_cairo(&0.0) {
            return Ok(output_surface);
        }

        let ptn = surface.to_cairo_pattern();
        let mut matrix = cairo::Matrix::new(
            w / f64::from(surface.width()),
            0f64,
            0f64,
            h / f64::from(surface.height()),
            x,
            y,
        );
        matrix.invert();
        ptn.set_matrix(matrix);

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source(&ptn);
        cr.paint();

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    impl_node_as_filter!();

    fn set_atts(&mut self, parent: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(parent, pbag)?;

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("preserveAspectRatio") => self.aspect = attr.parse(value)?,

                // "path" is used by some older Adobe Illustrator versions
                local_name!("xlink:href") | local_name!("path") => {
                    let href = Href::parse(value).map_err(|_| {
                        NodeError::parse_error(attr, ParseError::new("could not parse href"))
                    })?;

                    self.href = Some(href);
                }

                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        if let Some(href) = self.href.as_ref() {
            let output_surface = match href {
                Href::PlainUrl(_) => {
                    let unclipped_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
                    self.render_external_image(ctx, draw_ctx, &bounds, &unclipped_bounds, href)?
                }
                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
            };

            Ok(FilterResult {
                name: self.base.result.borrow().clone(),
                output: FilterOutput {
                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                    bounds,
                },
            })
        } else {
            Err(FilterError::InvalidInput)
        }
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<|endoftext|>