<commit_before>use std::collections::BTreeMap;
use std::default::Default;
use std::error::Error;
use std::marker::PhantomData;
use std::fmt;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record::Record;

use calendar::Calendar;
use calendar_event::CalendarEvent;
use contact::Contact;
use contact_group::ContactGroup;
use mailbox::Mailbox;
use message::Message;

use message_list::*;
use message_import::*;
use message_copy::*;
use message_report::*;

use self::RequestMethod::*;
use self::ResponseMethod::*;

make_record_method_args_type!(GetRequestArgs, "GetRequestArgs",
    ids:         Presence<Vec<String>> => "ids",
    properties:  Presence<Vec<String>> => "properties",
    since_state: Presence<String>      => "sinceState"
);

make_record_method_args_type!(GetResponseArgs, "GetResponseArgs",
    state:     String                  => "state",
    list:      Option<Vec<R::Partial>> => "list",
    not_found: Option<Vec<String>>     => "notFound"
);

make_record_method_args_type!(GetUpdatesRequestArgs, "GetUpdatesRequestArgs",
    since_state:             String                => "sinceState",
    max_changes:             Presence<u64>         => "maxChanges",
    fetch_records:           Presence<bool>        => "fetchRecords",
    fetch_record_properties: Presence<Vec<String>> => "fetchRecordProperties"
);

make_record_method_args_type!(GetUpdatesResponseArgs, "GetUpdatesResponseArgs",
    old_state:        String      => "oldState",
    new_state:        String      => "newState",
    has_more_updates: bool        => "hasMoreUpdates",
    changed:          Vec<String> => "changed",
    removed:          Vec<String> => "removed"
);

make_record_method_args_type!(SetRequestArgs, "SetRequestArgs",
    if_in_state: Presence<String>                      => "ifInState",
    create:      Presence<BTreeMap<String,R::Partial>> => "create",
    update:      Presence<BTreeMap<String,R::Partial>> => "update",
    destroy:     Presence<Vec<String>>                 => "destroy"
);

make_record_method_args_type!(SetResponseArgs, "SetResponseArgs",
    old_state:     Option<String>              => "oldState",
    new_state:     String                      => "newState",
    created:       BTreeMap<String,R::Partial> => "created",
    updated:       Vec<String>                 => "updated",
    destroyed:     Vec<String>                 => "destroyed",
    not_created:   BTreeMap<String,SetError>   => "notCreated",
    not_updated:   BTreeMap<String,SetError>   => "notUpdated",
    not_destroyed: BTreeMap<String,SetError>   => "notDestroyed"
);

make_prop_type!(SetError, "SetError",
    typ:         String         => "type",
    description: Option<String> => "description"
);

#[derive(Clone, PartialEq, Debug)]
pub struct ErrorDescription(pub String);

impl ToJson for ErrorDescription {
    fn to_json(&self) -> Json {
        self.0.to_json()
    }
}

impl FromJson for ErrorDescription {
    fn from_json(json: &Json) -> Result<ErrorDescription,ParseError> {
        match String::from_json(json) {
            Ok(v) => Ok(ErrorDescription(v)),
            Err(e) => Err(e),
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
pub enum MethodError {
    UnknownMethod(Presence<ErrorDescription>),
    InvalidArguments(Presence<ErrorDescription>),
    TooManyChanges,
    CannotCalculateChanges,
    StateMismatch,
    AccountNotFound,
    AccountNoMail,
    AccountReadOnly,
    AnchorNotFound,
    NotFound,
    InvalidMailboxes,
    MaxQuotaReached,
    FromAccountNotFound,
    ToAccountNotFound,
    FromAccountNoMail,
    ToAccountNoMail,
    AccountNoContacts,
    AccountNoCalendars,
    UnsupportedSort,
    InternalError(Presence<ErrorDescription>), // XXX not in spec
}

impl Error for MethodError {
    fn description(&self) -> &str {
        match *self {
            MethodError::UnknownMethod(_)       => "unknown method",
            MethodError::InvalidArguments(_)    => "invalid arguments for method",
            MethodError::TooManyChanges         => "number of available changes is higher than requested max",
            MethodError::CannotCalculateChanges => "can't calculate changes from supplied state",
            MethodError::StateMismatch          => "supplied state does not match current state",
            MethodError::AccountNotFound        => "account not found",
            MethodError::AccountNoMail          => "account does not contain any mail data",
            MethodError::AccountReadOnly        => "account is read-only",
            MethodError::AnchorNotFound         => "requested anchor not found in message list",
            MethodError::NotFound               => "requested file not found",
            MethodError::InvalidMailboxes       => "mailbox not found or invalid mailbox combination",
            MethodError::MaxQuotaReached        => "max quota reached",
            MethodError::FromAccountNotFound    => "from account not found",
            MethodError::ToAccountNotFound      => "to account not found",
            MethodError::FromAccountNoMail      => "from account does not contain any mail data",
            MethodError::ToAccountNoMail        => "to account does not contain any mail data",
            MethodError::AccountNoContacts      => "account does not contain any contact data",
            MethodError::AccountNoCalendars     => "account does not contain any calendar data",
            MethodError::UnsupportedSort        => "unable to sort on requested properties",
            MethodError::InternalError(_)       => "internal error",
        }
    }
}

impl fmt::Display for MethodError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            MethodError::UnknownMethod(Present(ref d)) => format!("unknown method ({})", d.0),
            MethodError::InvalidArguments(Present(ref d)) => format!("invalid arguments for method ({})", d.0),
            MethodError::InternalError(Present(ref d)) => format!("internal error ({})", d.0),
            ref e => e.description().to_string(),
        })
    }
}

impl ToJson for MethodError {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::<String,Json>::new();

        match *self {
            MethodError::UnknownMethod(_)       => "unknownMethod",
            MethodError::InvalidArguments(_)    => "invalidArguments",
            MethodError::TooManyChanges         => "tooManyChanges",
            MethodError::CannotCalculateChanges => "cannotCalculateChanges",
            MethodError::StateMismatch          => "stateMismatch",
            MethodError::AccountNotFound        => "accountNotFound",
            MethodError::AccountNoMail          => "accountNoMail",
            MethodError::AccountReadOnly        => "accountReadOnly",
            MethodError::AnchorNotFound         => "anchorNotFound",
            MethodError::NotFound               => "notFound",
            MethodError::InvalidMailboxes       => "invalidMailboxes",
            MethodError::MaxQuotaReached        => "maxQuotaReached",
            MethodError::FromAccountNotFound    => "fromAccountNotFound",
            MethodError::ToAccountNotFound      => "toAccountNotFound",
            MethodError::FromAccountNoMail      => "fromAccountNoMail",
            MethodError::ToAccountNoMail        => "toAccountNoMail",
            MethodError::AccountNoContacts      => "accountNoContacts",
            MethodError::AccountNoCalendars     => "accountNoCalendars",
            MethodError::UnsupportedSort        => "unsupportedSort",
            MethodError::InternalError(_)       => "internalError",
        }.to_string().to_json_field(&mut d, "type");

        match *self {
            MethodError::UnknownMethod(ref desc)    |
            MethodError::InvalidArguments(ref desc) |
            MethodError::InternalError(ref desc) =>
                desc.to_json_field(&mut d, "description"),
            _ => (),
        };

        Json::Object(d)
    }
}

impl FromJson for MethodError {
    fn from_json(json: &Json) -> Result<MethodError,ParseError> {
        match *json {
            Json::Object(ref o) => {
                let typ: String = try!(FromJsonField::from_json_field(o, "type"));
                match typ.as_ref() {
                    "unknownMethod"          => Ok(MethodError::UnknownMethod(try!(FromJsonField::from_json_field(o, "description")))),
                    "invalidArguments"       => Ok(MethodError::InvalidArguments(try!(FromJsonField::from_json_field(o, "description")))),
                    "tooManyChanges"         => Ok(MethodError::TooManyChanges),
                    "cannotCalculateChanges" => Ok(MethodError::CannotCalculateChanges),
                    "stateMismatch"          => Ok(MethodError::StateMismatch),
                    "accountNotFound"        => Ok(MethodError::AccountNotFound),
                    "accountNoMail"          => Ok(MethodError::AccountNoMail),
                    "accountReadOnly"        => Ok(MethodError::AccountReadOnly),
                    "anchorNotFound"         => Ok(MethodError::AnchorNotFound),
                    "notFound"               => Ok(MethodError::NotFound),
                    "invalidMailboxes"       => Ok(MethodError::InvalidMailboxes),
                    "maxQuotaReached"        => Ok(MethodError::MaxQuotaReached),
                    "fromAccountNotFound"    => Ok(MethodError::FromAccountNotFound),
                    "toAccountNotFound"      => Ok(MethodError::ToAccountNotFound),
                    "fromAccountNoMail"      => Ok(MethodError::FromAccountNoMail),
                    "toAccountNoMail"        => Ok(MethodError::ToAccountNoMail),
                    "accountNoContacts"      => Ok(MethodError::AccountNoContacts),
                    "accountNoCalendars"     => Ok(MethodError::AccountNoCalendars),
                    "unsupportedSort"        => Ok(MethodError::UnsupportedSort),
                    "internalError"          => Ok(MethodError::InternalError(try!(FromJsonField::from_json_field(o, "description")))),

                    _                        => Err(ParseError::InvalidStructure("MethodError".to_string())),
                }
            },
            _ => Err(ParseError::InvalidJsonType("MethodError".to_string())),
        }
    }
}

pub trait ClientId {
    fn client_id(&self) -> String;
}

make_methods!(RequestMethod, "RequestMethod", RequestError,
    GetCalendars,            GetRequestArgs<Calendar>             => "getCalendars",
    GetCalendarUpdates,      GetUpdatesRequestArgs<Calendar>      => "getCalendarUpdates",
    SetCalendars,            SetRequestArgs<Calendar>             => "setCalendars",

    GetCalendarEvents,       GetRequestArgs<CalendarEvent>        => "getCalendarEvents",
    GetCalendarEventUpdates, GetUpdatesRequestArgs<CalendarEvent> => "getCalendarEventUpdates",
    SetCalendarEvents,       SetRequestArgs<CalendarEvent>        => "setCalendarEvents",

    GetContacts,             GetRequestArgs<Contact>              => "getContacts",
    GetContactUpdates,       GetUpdatesRequestArgs<Contact>       => "getContactUpdates",
    SetContacts,             SetRequestArgs<Contact>              => "setContacts",

    GetContactGroups,        GetRequestArgs<ContactGroup>         => "getContactGroups",
    GetContactGroupUpdates,  GetUpdatesRequestArgs<ContactGroup>  => "getContactGroupUpdates",
    SetContactGroups,        SetRequestArgs<ContactGroup>         => "setContactGroups",

    GetMailboxes,            GetRequestArgs<Mailbox>              => "getMailboxes",
    GetMailboxUpdates,       GetUpdatesRequestArgs<Mailbox>       => "getMailboxUpdates",
    SetMailboxes,            SetRequestArgs<Mailbox>              => "setMailboxes",

    GetMessages,             GetRequestArgs<Message>              => "getMessages",
    GetMessageUpdates,       GetUpdatesRequestArgs<Message>       => "getMessageUpdates",
    SetMessages,             SetRequestArgs<Message>              => "setMessages",

    ImportMessages,          ImportMessagesRequestArgs            => "importMessages",
    CopyMessages,            CopyMessagesRequestArgs              => "copyMessages",
    ReportMessages,          ReportMessagesRequestArgs            => "reportMessages",

    GetMessageList,          GetMessageListRequestArgs            => "getMessageList",
    GetMessageListUpdates,   GetMessageListUpdatesRequestArgs     => "getMessageListUpdates",

    RequestError,            MethodError                          => "error"
);

make_methods!(ResponseMethod, "ResponseMethod", ResponseError,
    Calendars,            GetResponseArgs<Calendar>             => "calendars",
    CalendarUpdates,      GetUpdatesResponseArgs<Calendar>      => "calendarUpdates",
    CalendarsSet,         SetResponseArgs<Calendar>             => "calendarsSet",

    CalendarEvents,       GetResponseArgs<CalendarEvent>        => "calendarEvents",
    CalendarEventUpdates, GetUpdatesResponseArgs<CalendarEvent> => "calendarEventUpdates",
    CalendarEventsSet,    SetResponseArgs<CalendarEvent>        => "calendarEventsSet",

    Contacts,             GetResponseArgs<Contact>              => "contacts",
    ContactUpdates,       GetUpdatesResponseArgs<Contact>       => "contactUpdates",
    ContactsSet,          SetResponseArgs<Contact>              => "contactsSet",

    ContactGroups,        GetResponseArgs<ContactGroup>         => "contactGroups",
    ContactGroupUpdates,  GetUpdatesResponseArgs<ContactGroup>  => "contactGroupUpdates",
    ContactGroupsSet,     SetResponseArgs<ContactGroup>         => "contactGroupsSet",

    Mailboxes,            GetResponseArgs<Mailbox>              => "mailboxes",
    MailboxUpdates,       GetUpdatesResponseArgs<Mailbox>       => "mailboxUpdates",
    MailboxesSet,         SetResponseArgs<Mailbox>              => "mailboxesSet",

    Messages,             GetResponseArgs<Message>              => "messages",
    MessageUpdates,       GetUpdatesResponseArgs<Message>       => "messageUpdates",
    MessagesSet,          SetResponseArgs<Message>              => "messagesSet",

    MessageImported,      ImportMessagesResponseArgs            => "messagesImported",
    MessageCopied,        CopyMessagesResponseArgs              => "messagesCopied",
    MessageReported,      ReportMessagesResponseArgs            => "messagesReported",

    MessageList,          GetMessageListUpdatesRequestArgs      => "messageList",
    MessageListUpdates,   GetMessageListUpdatesResponseArgs     => "messageListUpdates",

    ResponseError,        MethodError                           => "error"
);

make_batch!(RequestBatch,  RequestMethod);
make_batch!(ResponseBatch, ResponseMethod);
<commit_msg>Response plurals<commit_after>use std::collections::BTreeMap;
use std::default::Default;
use std::error::Error;
use std::marker::PhantomData;
use std::fmt;
use rustc_serialize::json::{Json,ToJson};

use parse::*;
use parse::Presence::*;
use record::Record;

use calendar::Calendar;
use calendar_event::CalendarEvent;
use contact::Contact;
use contact_group::ContactGroup;
use mailbox::Mailbox;
use message::Message;

use message_list::*;
use message_import::*;
use message_copy::*;
use message_report::*;

use self::RequestMethod::*;
use self::ResponseMethod::*;

make_record_method_args_type!(GetRequestArgs, "GetRequestArgs",
    ids:         Presence<Vec<String>> => "ids",
    properties:  Presence<Vec<String>> => "properties",
    since_state: Presence<String>      => "sinceState"
);

make_record_method_args_type!(GetResponseArgs, "GetResponseArgs",
    state:     String                  => "state",
    list:      Option<Vec<R::Partial>> => "list",
    not_found: Option<Vec<String>>     => "notFound"
);

make_record_method_args_type!(GetUpdatesRequestArgs, "GetUpdatesRequestArgs",
    since_state:             String                => "sinceState",
    max_changes:             Presence<u64>         => "maxChanges",
    fetch_records:           Presence<bool>        => "fetchRecords",
    fetch_record_properties: Presence<Vec<String>> => "fetchRecordProperties"
);

make_record_method_args_type!(GetUpdatesResponseArgs, "GetUpdatesResponseArgs",
    old_state:        String      => "oldState",
    new_state:        String      => "newState",
    has_more_updates: bool        => "hasMoreUpdates",
    changed:          Vec<String> => "changed",
    removed:          Vec<String> => "removed"
);

make_record_method_args_type!(SetRequestArgs, "SetRequestArgs",
    if_in_state: Presence<String>                      => "ifInState",
    create:      Presence<BTreeMap<String,R::Partial>> => "create",
    update:      Presence<BTreeMap<String,R::Partial>> => "update",
    destroy:     Presence<Vec<String>>                 => "destroy"
);

make_record_method_args_type!(SetResponseArgs, "SetResponseArgs",
    old_state:     Option<String>              => "oldState",
    new_state:     String                      => "newState",
    created:       BTreeMap<String,R::Partial> => "created",
    updated:       Vec<String>                 => "updated",
    destroyed:     Vec<String>                 => "destroyed",
    not_created:   BTreeMap<String,SetError>   => "notCreated",
    not_updated:   BTreeMap<String,SetError>   => "notUpdated",
    not_destroyed: BTreeMap<String,SetError>   => "notDestroyed"
);

make_prop_type!(SetError, "SetError",
    typ:         String         => "type",
    description: Option<String> => "description"
);

#[derive(Clone, PartialEq, Debug)]
pub struct ErrorDescription(pub String);

impl ToJson for ErrorDescription {
    fn to_json(&self) -> Json {
        self.0.to_json()
    }
}

impl FromJson for ErrorDescription {
    fn from_json(json: &Json) -> Result<ErrorDescription,ParseError> {
        match String::from_json(json) {
            Ok(v) => Ok(ErrorDescription(v)),
            Err(e) => Err(e),
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
pub enum MethodError {
    UnknownMethod(Presence<ErrorDescription>),
    InvalidArguments(Presence<ErrorDescription>),
    TooManyChanges,
    CannotCalculateChanges,
    StateMismatch,
    AccountNotFound,
    AccountNoMail,
    AccountReadOnly,
    AnchorNotFound,
    NotFound,
    InvalidMailboxes,
    MaxQuotaReached,
    FromAccountNotFound,
    ToAccountNotFound,
    FromAccountNoMail,
    ToAccountNoMail,
    AccountNoContacts,
    AccountNoCalendars,
    UnsupportedSort,
    InternalError(Presence<ErrorDescription>), // XXX not in spec
}

impl Error for MethodError {
    fn description(&self) -> &str {
        match *self {
            MethodError::UnknownMethod(_)       => "unknown method",
            MethodError::InvalidArguments(_)    => "invalid arguments for method",
            MethodError::TooManyChanges         => "number of available changes is higher than requested max",
            MethodError::CannotCalculateChanges => "can't calculate changes from supplied state",
            MethodError::StateMismatch          => "supplied state does not match current state",
            MethodError::AccountNotFound        => "account not found",
            MethodError::AccountNoMail          => "account does not contain any mail data",
            MethodError::AccountReadOnly        => "account is read-only",
            MethodError::AnchorNotFound         => "requested anchor not found in message list",
            MethodError::NotFound               => "requested file not found",
            MethodError::InvalidMailboxes       => "mailbox not found or invalid mailbox combination",
            MethodError::MaxQuotaReached        => "max quota reached",
            MethodError::FromAccountNotFound    => "from account not found",
            MethodError::ToAccountNotFound      => "to account not found",
            MethodError::FromAccountNoMail      => "from account does not contain any mail data",
            MethodError::ToAccountNoMail        => "to account does not contain any mail data",
            MethodError::AccountNoContacts      => "account does not contain any contact data",
            MethodError::AccountNoCalendars     => "account does not contain any calendar data",
            MethodError::UnsupportedSort        => "unable to sort on requested properties",
            MethodError::InternalError(_)       => "internal error",
        }
    }
}

impl fmt::Display for MethodError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            MethodError::UnknownMethod(Present(ref d)) => format!("unknown method ({})", d.0),
            MethodError::InvalidArguments(Present(ref d)) => format!("invalid arguments for method ({})", d.0),
            MethodError::InternalError(Present(ref d)) => format!("internal error ({})", d.0),
            ref e => e.description().to_string(),
        })
    }
}

impl ToJson for MethodError {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::<String,Json>::new();

        match *self {
            MethodError::UnknownMethod(_)       => "unknownMethod",
            MethodError::InvalidArguments(_)    => "invalidArguments",
            MethodError::TooManyChanges         => "tooManyChanges",
            MethodError::CannotCalculateChanges => "cannotCalculateChanges",
            MethodError::StateMismatch          => "stateMismatch",
            MethodError::AccountNotFound        => "accountNotFound",
            MethodError::AccountNoMail          => "accountNoMail",
            MethodError::AccountReadOnly        => "accountReadOnly",
            MethodError::AnchorNotFound         => "anchorNotFound",
            MethodError::NotFound               => "notFound",
            MethodError::InvalidMailboxes       => "invalidMailboxes",
            MethodError::MaxQuotaReached        => "maxQuotaReached",
            MethodError::FromAccountNotFound    => "fromAccountNotFound",
            MethodError::ToAccountNotFound      => "toAccountNotFound",
            MethodError::FromAccountNoMail      => "fromAccountNoMail",
            MethodError::ToAccountNoMail        => "toAccountNoMail",
            MethodError::AccountNoContacts      => "accountNoContacts",
            MethodError::AccountNoCalendars     => "accountNoCalendars",
            MethodError::UnsupportedSort        => "unsupportedSort",
            MethodError::InternalError(_)       => "internalError",
        }.to_string().to_json_field(&mut d, "type");

        match *self {
            MethodError::UnknownMethod(ref desc)    |
            MethodError::InvalidArguments(ref desc) |
            MethodError::InternalError(ref desc) =>
                desc.to_json_field(&mut d, "description"),
            _ => (),
        };

        Json::Object(d)
    }
}

impl FromJson for MethodError {
    fn from_json(json: &Json) -> Result<MethodError,ParseError> {
        match *json {
            Json::Object(ref o) => {
                let typ: String = try!(FromJsonField::from_json_field(o, "type"));
                match typ.as_ref() {
                    "unknownMethod"          => Ok(MethodError::UnknownMethod(try!(FromJsonField::from_json_field(o, "description")))),
                    "invalidArguments"       => Ok(MethodError::InvalidArguments(try!(FromJsonField::from_json_field(o, "description")))),
                    "tooManyChanges"         => Ok(MethodError::TooManyChanges),
                    "cannotCalculateChanges" => Ok(MethodError::CannotCalculateChanges),
                    "stateMismatch"          => Ok(MethodError::StateMismatch),
                    "accountNotFound"        => Ok(MethodError::AccountNotFound),
                    "accountNoMail"          => Ok(MethodError::AccountNoMail),
                    "accountReadOnly"        => Ok(MethodError::AccountReadOnly),
                    "anchorNotFound"         => Ok(MethodError::AnchorNotFound),
                    "notFound"               => Ok(MethodError::NotFound),
                    "invalidMailboxes"       => Ok(MethodError::InvalidMailboxes),
                    "maxQuotaReached"        => Ok(MethodError::MaxQuotaReached),
                    "fromAccountNotFound"    => Ok(MethodError::FromAccountNotFound),
                    "toAccountNotFound"      => Ok(MethodError::ToAccountNotFound),
                    "fromAccountNoMail"      => Ok(MethodError::FromAccountNoMail),
                    "toAccountNoMail"        => Ok(MethodError::ToAccountNoMail),
                    "accountNoContacts"      => Ok(MethodError::AccountNoContacts),
                    "accountNoCalendars"     => Ok(MethodError::AccountNoCalendars),
                    "unsupportedSort"        => Ok(MethodError::UnsupportedSort),
                    "internalError"          => Ok(MethodError::InternalError(try!(FromJsonField::from_json_field(o, "description")))),

                    _                        => Err(ParseError::InvalidStructure("MethodError".to_string())),
                }
            },
            _ => Err(ParseError::InvalidJsonType("MethodError".to_string())),
        }
    }
}

pub trait ClientId {
    fn client_id(&self) -> String;
}

make_methods!(RequestMethod, "RequestMethod", RequestError,
    GetCalendars,            GetRequestArgs<Calendar>             => "getCalendars",
    GetCalendarUpdates,      GetUpdatesRequestArgs<Calendar>      => "getCalendarUpdates",
    SetCalendars,            SetRequestArgs<Calendar>             => "setCalendars",

    GetCalendarEvents,       GetRequestArgs<CalendarEvent>        => "getCalendarEvents",
    GetCalendarEventUpdates, GetUpdatesRequestArgs<CalendarEvent> => "getCalendarEventUpdates",
    SetCalendarEvents,       SetRequestArgs<CalendarEvent>        => "setCalendarEvents",

    GetContacts,             GetRequestArgs<Contact>              => "getContacts",
    GetContactUpdates,       GetUpdatesRequestArgs<Contact>       => "getContactUpdates",
    SetContacts,             SetRequestArgs<Contact>              => "setContacts",

    GetContactGroups,        GetRequestArgs<ContactGroup>         => "getContactGroups",
    GetContactGroupUpdates,  GetUpdatesRequestArgs<ContactGroup>  => "getContactGroupUpdates",
    SetContactGroups,        SetRequestArgs<ContactGroup>         => "setContactGroups",

    GetMailboxes,            GetRequestArgs<Mailbox>              => "getMailboxes",
    GetMailboxUpdates,       GetUpdatesRequestArgs<Mailbox>       => "getMailboxUpdates",
    SetMailboxes,            SetRequestArgs<Mailbox>              => "setMailboxes",

    GetMessages,             GetRequestArgs<Message>              => "getMessages",
    GetMessageUpdates,       GetUpdatesRequestArgs<Message>       => "getMessageUpdates",
    SetMessages,             SetRequestArgs<Message>              => "setMessages",

    ImportMessages,          ImportMessagesRequestArgs            => "importMessages",
    CopyMessages,            CopyMessagesRequestArgs              => "copyMessages",
    ReportMessages,          ReportMessagesRequestArgs            => "reportMessages",

    GetMessageList,          GetMessageListRequestArgs            => "getMessageList",
    GetMessageListUpdates,   GetMessageListUpdatesRequestArgs     => "getMessageListUpdates",

    RequestError,            MethodError                          => "error"
);

make_methods!(ResponseMethod, "ResponseMethod", ResponseError,
    Calendars,            GetResponseArgs<Calendar>             => "calendars",
    CalendarUpdates,      GetUpdatesResponseArgs<Calendar>      => "calendarUpdates",
    CalendarsSet,         SetResponseArgs<Calendar>             => "calendarsSet",

    CalendarEvents,       GetResponseArgs<CalendarEvent>        => "calendarEvents",
    CalendarEventUpdates, GetUpdatesResponseArgs<CalendarEvent> => "calendarEventUpdates",
    CalendarEventsSet,    SetResponseArgs<CalendarEvent>        => "calendarEventsSet",

    Contacts,             GetResponseArgs<Contact>              => "contacts",
    ContactUpdates,       GetUpdatesResponseArgs<Contact>       => "contactUpdates",
    ContactsSet,          SetResponseArgs<Contact>              => "contactsSet",

    ContactGroups,        GetResponseArgs<ContactGroup>         => "contactGroups",
    ContactGroupUpdates,  GetUpdatesResponseArgs<ContactGroup>  => "contactGroupUpdates",
    ContactGroupsSet,     SetResponseArgs<ContactGroup>         => "contactGroupsSet",

    Mailboxes,            GetResponseArgs<Mailbox>              => "mailboxes",
    MailboxUpdates,       GetUpdatesResponseArgs<Mailbox>       => "mailboxUpdates",
    MailboxesSet,         SetResponseArgs<Mailbox>              => "mailboxesSet",

    Messages,             GetResponseArgs<Message>              => "messages",
    MessageUpdates,       GetUpdatesResponseArgs<Message>       => "messageUpdates",
    MessagesSet,          SetResponseArgs<Message>              => "messagesSet",

    MessagesImported,     ImportMessagesResponseArgs            => "messagesImported",
    MessagesCopied,       CopyMessagesResponseArgs              => "messagesCopied",
    MessagesReported,     ReportMessagesResponseArgs            => "messagesReported",

    MessageList,          GetMessageListUpdatesRequestArgs      => "messageList",
    MessageListUpdates,   GetMessageListUpdatesResponseArgs     => "messageListUpdates",

    ResponseError,        MethodError                           => "error"
);

make_batch!(RequestBatch,  RequestMethod);
make_batch!(ResponseBatch, ResponseMethod);
<|endoftext|>


<commit_before>#![feature(core,collections)]

use nom::{IResult,Needed,not_line_ending};
use nom::IResult::*;
use std::str;

#[derive(Debug,PartialEq,Eq)]
pub struct Base {
  pub key: Nucleobase,
  pub score: u8,
}

#[derive(Debug,PartialEq,Eq)]
pub struct Sequence<'a> {
  pub id: &'a str,
  bases: Vec<Base>,
}

#[derive(Debug,PartialEq,Eq)]
pub enum Nucleobase {
  A,
  C,
  G,
  T,
  U,
  N
}

pub fn quality_scores(input:&[u8]) -> IResult<&[u8], &str> {
  chain!(input,
    tag!("+") ~
    tag!("\n") ~
    scores: map_res!(not_line_ending, str::from_utf8) ~
    tag!("\n")?,
    ||{scores}
  )
}

pub fn many_reads(input:&[u8]) -> IResult<&[u8], Vec<Sequence>> {
  many1!(input,
    chain!(
      tag!("@") ~
      id: map_res!(not_line_ending, str::from_utf8) ~ tag!("\n") ~
      bases: map_res!(not_line_ending, str::from_utf8) ~ tag!("\n")? ~
      scores: opt!(quality_scores),
      ||{
         Sequence {
           id: id,
           bases:
             bases.chars().zip(
               scores.unwrap().chars()
             ).map(|x| Base{key:
               match x.0 {
                 'A' => Nucleobase::A,
                 'C' => Nucleobase::C,
                 'G' => Nucleobase::G,
                 'T' => Nucleobase::T,
                 'U' => Nucleobase::U,
                 _ => Nucleobase::N,
               }
             , score: ('~' as u8) - (x.1 as u8)})
             .collect::<Vec<Base>>()
         }
      }
    )
  )
}

<commit_msg>Parser updated to hand trailing newline<commit_after>#![feature(core,collections)]

use nom::{IResult,Needed,not_line_ending,line_ending};
use nom::IResult::*;
use std::str;

#[derive(Debug,PartialEq,Eq)]
pub struct Base {
  pub key: Nucleobase,
  pub score: u8,
}

#[derive(Debug,PartialEq,Eq)]
pub struct Sequence<'a> {
  pub id: &'a str,
  bases: Vec<Base>,
}

#[derive(Debug,PartialEq,Eq)]
pub enum Nucleobase {
  A,
  C,
  G,
  T,
  U,
  N
}

pub fn quality_scores(input:&[u8]) -> IResult<&[u8], &str> {
  chain!(input,
    tag!("+") ~
    line_ending ~
    scores: map_res!(not_line_ending, str::from_utf8) ~
    alt!(eof | line_ending),
    ||{scores}
  )
}

pub fn many_reads(input:&[u8]) -> IResult<&[u8], Vec<Sequence>> {
  many1!(input,
    chain!(
      tag!("@") ~
      id: map_res!(not_line_ending, str::from_utf8) ~ line_ending ~
      bases: map_res!(not_line_ending, str::from_utf8) ~ alt!(line_ending | eof) ~
      scores: opt!(quality_scores),
      ||{
         Sequence {
           id: id,
           bases:
             bases.chars().zip(
               scores.unwrap().chars()
             ).map(|x| Base{key:
               match x.0 {
                 'A' => Nucleobase::A,
                 'C' => Nucleobase::C,
                 'G' => Nucleobase::G,
                 'T' => Nucleobase::T,
                 'U' => Nucleobase::U,
                 _ => Nucleobase::N,
               }
             , score: ('~' as u8) - (x.1 as u8)})
             .collect::<Vec<Base>>()
         }
      }
    )
  )
}

// https://github.com/filipegoncalves/rust-config/blob/407b9539f4efefae3703ef86b887a0c55611a51e/src/parser.rs#L580
// This parser is successful only if the input is over
fn eof(input:&[u8]) -> IResult<&[u8], &[u8]> {
    if input.len() == 0 {
        Done(input, input)
    } else {
        Error(0)
    }
}
<|endoftext|>


<commit_before>use super::{LzfResult,LzfError};

pub fn decompress(data: &[u8]) -> LzfResult<Vec<u8>> {
    let mut current_offset = 0;

    let mut output = vec![];

    let in_len = data.len();

    while current_offset < in_len {
        let mut ctrl = data[current_offset] as usize;
        current_offset += 1;

        if ctrl < (1<<5) {
            ctrl += 1;

            if current_offset+ctrl > in_len {
                return Err(LzfError::DataCorrupted);
            }

            while ctrl > 0 {
                output.push(data[current_offset]);
                current_offset += 1;
                ctrl -= 1;
            }
        } else {
            let mut len = ctrl >> 5;

            let mut ref_offset = (((ctrl & 0x1f) << 8) + 1) as i32;

            if len == 7 {
                len += data[current_offset] as usize;
                current_offset += 1;
            }

            ref_offset += data[current_offset] as i32;
            current_offset += 1;

            let mut ref_pos = (output.len() as i32) - ref_offset;
            if ref_pos < 0 {
                return Err(LzfError::DataCorrupted);
            }

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            while len > 0 {
                let c = output[ref_pos as usize];
                output.push(c);
                ref_pos += 1;
                len -= 1;
            }
        }
    }

    Ok(output)
}

#[test]
fn test_native_decompress_lorem() {
    use super::compress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = compress(lorem.as_bytes()).unwrap();

    let decompressed = decompress(&compressed[..]).unwrap();
    assert_eq!(lorem.as_bytes(), &decompressed[..]);
}
<commit_msg>Add out len<commit_after>use super::{LzfResult,LzfError};

pub fn decompress(data: &[u8], out_len: usize) -> LzfResult<Vec<u8>> {
    let mut current_offset = 0;

    let mut output = Vec::with_capacity(out_len);

    let in_len = data.len();

    while current_offset < in_len {
        let mut ctrl = data[current_offset] as usize;
        current_offset += 1;

        if ctrl < (1<<5) {
            ctrl += 1;

            if current_offset+ctrl > in_len {
                return Err(LzfError::DataCorrupted);
            }

            while ctrl > 0 {
                output.push(data[current_offset]);
                current_offset += 1;
                ctrl -= 1;
            }
        } else {
            let mut len = ctrl >> 5;

            let mut ref_offset = (((ctrl & 0x1f) << 8) + 1) as i32;

            if len == 7 {
                len += data[current_offset] as usize;
                current_offset += 1;
            }

            ref_offset += data[current_offset] as i32;
            current_offset += 1;

            let mut ref_pos = (output.len() as i32) - ref_offset;
            if ref_pos < 0 {
                return Err(LzfError::DataCorrupted);
            }

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            while len > 0 {
                let c = output[ref_pos as usize];
                output.push(c);
                ref_pos += 1;
                len -= 1;
            }
        }
    }

    Ok(output)
}

#[test]
fn test_native_decompress_lorem() {
    use super::compress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = compress(lorem.as_bytes()).unwrap();

    let decompressed = decompress(&compressed[..], lorem.len()).unwrap();
    assert_eq!(lorem.as_bytes(), &decompressed[..]);
}
<|endoftext|>


<commit_before>use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ptr::P;

use syntax::ast::Expr;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr};
use syntax::parse::parser::Parser;
use syntax::parse::token::Token;

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, _: Span,
               tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);
    let expr = parse_json(cx, &mut parser);
    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `json!` macro invocation");
    }
    MacExpr::new(expr)
}

fn parse_json(cx: &ExtCtxt, parser: &mut Parser) -> P<Expr> {
    use syntax::ext::build::AstBuilder;
    use syntax::parse::token::{DelimToken, IdentStyle};

    macro_rules! comma_sep {
        () =>  {
            ::syntax::parse::common::SeqSep {
                sep: Some(Token::Comma),
                trailing_sep_allowed: true // we could be JSON pedants...
            }
        }
    }

    let orig_span = parser.span;

    match &parser.token {
        &Token::OpenDelim(DelimToken::Bracket) => {
            parser.bump();
            let r_bracket = Token::CloseDelim(DelimToken::Bracket);
            let exprs = parser.parse_seq_to_end(&r_bracket, comma_sep!(), |p| {
                parse_json(cx, p)
            });
            let exprs = cx.expr_vec(orig_span, exprs);
            quote_expr!(cx, {{
                use ::std::boxed::Box;
                let xs: Box<[_]> = Box::new($exprs);
                ::rustc_serialize::json::Json::Array(xs.into_vec())
            }})
        },
        &Token::OpenDelim(DelimToken::Brace) => {
            parser.bump();
            let r_brace = Token::CloseDelim(DelimToken::Brace);
            let kvs = parser.parse_seq_to_end(&r_brace, comma_sep!(), |p| {
                let (istr, _) = p.parse_str();
                let s = &*istr;
                p.expect(&Token::Colon);
                let key = quote_expr!(cx, {{
                    use ::std::borrow::ToOwned;
                    $s.to_owned()
                }});
                (key, parse_json(cx, p))
            });
            let ob = quote_expr!(cx, _ob);
            let mut stmts = vec![];
            for &(ref key, ref value) in kvs.iter() {
                stmts.push(quote_stmt!(cx, $ob.insert($key, $value)));
            }
            quote_expr!(cx, {{
                let mut $ob = ::std::collections::BTreeMap::new();
                $stmts;
                ::rustc_serialize::json::Json::Object($ob)
            }})
        },
        &Token::OpenDelim(DelimToken::Paren) => {
            let expr = parser.parse_expr();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        },
        &Token::Ident(id, IdentStyle::Plain) if id.as_str() == "null" => {
            parser.bump();
            quote_expr!(cx, { ::rustc_serialize::json::Json::Null })
        },
        _ => { // TODO: investigate can_begin_expr (maybe eliminate need for parens)?
            let expr = parser.parse_literal_maybe_minus();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        }
    }
}
<commit_msg>rustup: MaxExpr::new -> MacEager::expr<commit_after>use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ptr::P;

use syntax::ast::Expr;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::parse::parser::Parser;
use syntax::parse::token::Token;

pub fn expand<'cx>(cx: &'cx mut ExtCtxt, _: Span,
               tts: &[TokenTree]) -> Box<MacResult + 'cx> {
    let mut parser = cx.new_parser_from_tts(tts);
    let expr = parse_json(cx, &mut parser);
    if &parser.token != &Token::Eof {
        cx.span_fatal(parser.span, "expected end of `json!` macro invocation");
    }
    MacEager::expr(expr)
}

fn parse_json(cx: &ExtCtxt, parser: &mut Parser) -> P<Expr> {
    use syntax::ext::build::AstBuilder;
    use syntax::parse::token::{DelimToken, IdentStyle};

    macro_rules! comma_sep {
        () =>  {
            ::syntax::parse::common::SeqSep {
                sep: Some(Token::Comma),
                trailing_sep_allowed: true // we could be JSON pedants...
            }
        }
    }

    let orig_span = parser.span;

    match &parser.token {
        &Token::OpenDelim(DelimToken::Bracket) => {
            parser.bump();
            let r_bracket = Token::CloseDelim(DelimToken::Bracket);
            let exprs = parser.parse_seq_to_end(&r_bracket, comma_sep!(), |p| {
                parse_json(cx, p)
            });
            let exprs = cx.expr_vec(orig_span, exprs);
            quote_expr!(cx, {{
                use ::std::boxed::Box;
                let xs: Box<[_]> = Box::new($exprs);
                ::rustc_serialize::json::Json::Array(xs.into_vec())
            }})
        },
        &Token::OpenDelim(DelimToken::Brace) => {
            parser.bump();
            let r_brace = Token::CloseDelim(DelimToken::Brace);
            let kvs = parser.parse_seq_to_end(&r_brace, comma_sep!(), |p| {
                let (istr, _) = p.parse_str();
                let s = &*istr;
                p.expect(&Token::Colon);
                let key = quote_expr!(cx, {{
                    use ::std::borrow::ToOwned;
                    $s.to_owned()
                }});
                (key, parse_json(cx, p))
            });
            let ob = quote_expr!(cx, _ob);
            let mut stmts = vec![];
            for &(ref key, ref value) in kvs.iter() {
                stmts.push(quote_stmt!(cx, $ob.insert($key, $value)));
            }
            quote_expr!(cx, {{
                let mut $ob = ::std::collections::BTreeMap::new();
                $stmts;
                ::rustc_serialize::json::Json::Object($ob)
            }})
        },
        &Token::OpenDelim(DelimToken::Paren) => {
            let expr = parser.parse_expr();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        },
        &Token::Ident(id, IdentStyle::Plain) if id.as_str() == "null" => {
            parser.bump();
            quote_expr!(cx, { ::rustc_serialize::json::Json::Null })
        },
        _ => { // TODO: investigate can_begin_expr (maybe eliminate need for parens)?
            let expr = parser.parse_literal_maybe_minus();
            quote_expr!(cx, {{
                use ::rustc_serialize::json::ToJson;
                ($expr).to_json()
            }})
        }
    }
}
<|endoftext|>


<commit_before>//! # `remote` - operations on remote repositories.
//!
//! `Remote` contains a `RadosConnection` object, along with a reference to the parent context.
//!
//! At current the only supported remote is a Ceph/RADOS cluster.

use std::sync::{Arc, Mutex};

use futures::prelude::*;
use owning_ref::OwningRefMut;
use rad::{ConnectionBuilder, Connection};

use catalog::Catalog;
use context::Context;
use errors::*;
use local::Local;
use marshal::{Hashed, ObjectHash, Object};
use trace::Trace;

/// The type of a remote repository.
// TODO: Abstract into a trait.
// TODO: Locally store what objects we know the remote to contain so that we can avoid writing them
//       when the remote already contains them.
// TODO: Make the act of writing an object asynchronous - return a future instead of a `Result.
#[derive(Clone)]
pub struct Remote {
    local: Local,

    catalog: Catalog,
    inner: Arc<RemoteInner>,
}

struct RemoteInner {
    conn: Mutex<Connection>,
    pool: String,
}

impl Remote {
    /// Connect to a remote repository, given appropriate configuration data.
    pub fn connect<T: Trace, U: AsRef<str> + Into<String>>(ctx: &mut Context<T>, remote_name: U) -> Result<Self> {
        let cfg = ctx.get_remote_cfg(remote_name.as_ref())?;

        let conn = {
            let mut builder = ConnectionBuilder::with_user(&cfg.object_store.user)
                .chain_err(|| ErrorKind::RemoteConnectInit)?;

            if let Some(ref conf_path) = cfg.object_store.conf_file {
                builder = builder.read_conf_file(conf_path).chain_err(|| {
                    ErrorKind::RemoteConnectReadConf
                })?;
            }

            builder = cfg.object_store
                .conf_options
                .iter()
                .fold(Ok(builder), |acc, (key, value)| {
                    acc.and_then(|conn| conn.conf_set(key, value))
                })
                .chain_err(|| ErrorKind::RemoteConnectConfig)?;

            Mutex::new(builder.connect().chain_err(|| ErrorKind::RemoteConnect)?)
        };

        let pool = cfg.object_store.pool.clone();

        Ok(Remote {
            local: Local::new(ctx).chain_err(|| ErrorKind::LocalLoad)?,
            catalog: ctx.get_remote_catalog(remote_name)?,
            inner: Arc::new(RemoteInner { conn, pool }),
        })
    }

    /// Write a single object to the remote repository. Returns `false` and performs no I/O if the
    /// catalog shows that the remote already contains the object; `true` otherwise.
    // TODO: Query the remote to see if it contains the object already. If so, don't send.
    pub fn write_object(&self, hashed: Hashed) -> Box<Future<Item = bool, Error = Error> + Send> {
        let lock = match self.catalog.try_lock(*hashed.as_hash()) {
            Ok(lock) => lock,
            Err(future) => return Box::new(future.map(|_| false)),
        };
        let (hash, bytes_opt) = hashed.into_components();

        match bytes_opt {
            Some(bytes) => {
                let ctx_res = self.inner.conn.lock().unwrap().get_pool_context(
                    &self.inner.pool,
                );
                let result = {
                    async_block! {
                        let mut ctx = ctx_res?;
                        await!(ctx.write_full_async(&hash.to_string(), &bytes))?;
                        lock.release();
                        Ok(true)
                    }
                };

                Box::new(result)
            }

            None => {
                unimplemented!("TODO: Must load local blob!");
            }
        }
    }

    /// Read a single object from the remote repository.
    ///
    /// This will instead read a local file if the object is already present on disk in the local
    /// blob store.
    pub fn read_object(
        &self,
        object_hash: ObjectHash,
    ) -> Box<Future<Item = Object, Error = Error> + Send> {
        let local_future = self.local.read_or_allocate_object(object_hash);
        let ctx_res = self.inner.conn.lock().unwrap().get_pool_context(
            &self.inner.pool,
        );

        let result = {
            async_block! {
                match await!(local_future)? {
                    Ok(object) => Ok(object),
                    Err(factory) => {
                        let mut ctx = ctx_res?;

                        let object_id = object_hash.to_string();
                        let stat = await!(ctx.stat_async(&object_id))?;

                        let mut buf = OwningRefMut::new(factory.with_size(stat.size as usize)?);

                        let mut total_read = 0;
                        let written_buf = loop {
                            let (bytes_read_u64, new_buf) =
                                await!(ctx.read_async(
                                    &object_id,
                                    buf.map_mut(|slice| &mut slice[total_read..]),
                                    total_read as u64,
                                ))?;

                            let bytes_read = bytes_read_u64 as usize;
                            total_read += bytes_read;

                            if bytes_read == new_buf.len() {
                                break new_buf.into_inner();
                            }

                            buf = new_buf;
                        };

                        await!(written_buf.finish())
                    }
                }
            }
        };

        Box::new(result)
    }
}
<commit_msg>rustfmt: remote.rs<commit_after>//! # `remote` - operations on remote repositories.
//!
//! `Remote` contains a `RadosConnection` object, along with a reference to the parent context.
//!
//! At current the only supported remote is a Ceph/RADOS cluster.

use std::sync::{Arc, Mutex};

use futures::prelude::*;
use owning_ref::OwningRefMut;
use rad::{ConnectionBuilder, Connection};

use catalog::Catalog;
use context::Context;
use errors::*;
use local::Local;
use marshal::{Hashed, ObjectHash, Object};
use trace::Trace;

/// The type of a remote repository.
// TODO: Abstract into a trait.
// TODO: Locally store what objects we know the remote to contain so that we can avoid writing them
//       when the remote already contains them.
// TODO: Make the act of writing an object asynchronous - return a future instead of a `Result.
#[derive(Clone)]
pub struct Remote {
    local: Local,

    catalog: Catalog,
    inner: Arc<RemoteInner>,
}

struct RemoteInner {
    conn: Mutex<Connection>,
    pool: String,
}

impl Remote {
    /// Connect to a remote repository, given appropriate configuration data.
    pub fn connect<T: Trace, U: AsRef<str> + Into<String>>(
        ctx: &mut Context<T>,
        remote_name: U,
    ) -> Result<Self> {
        let cfg = ctx.get_remote_cfg(remote_name.as_ref())?;

        let conn = {
            let mut builder = ConnectionBuilder::with_user(&cfg.object_store.user)
                .chain_err(|| ErrorKind::RemoteConnectInit)?;

            if let Some(ref conf_path) = cfg.object_store.conf_file {
                builder = builder.read_conf_file(conf_path).chain_err(|| {
                    ErrorKind::RemoteConnectReadConf
                })?;
            }

            builder = cfg.object_store
                .conf_options
                .iter()
                .fold(Ok(builder), |acc, (key, value)| {
                    acc.and_then(|conn| conn.conf_set(key, value))
                })
                .chain_err(|| ErrorKind::RemoteConnectConfig)?;

            Mutex::new(builder.connect().chain_err(|| ErrorKind::RemoteConnect)?)
        };

        let pool = cfg.object_store.pool.clone();

        Ok(Remote {
            local: Local::new(ctx).chain_err(|| ErrorKind::LocalLoad)?,
            catalog: ctx.get_remote_catalog(remote_name)?,
            inner: Arc::new(RemoteInner { conn, pool }),
        })
    }

    /// Write a single object to the remote repository. Returns `false` and performs no I/O if the
    /// catalog shows that the remote already contains the object; `true` otherwise.
    // TODO: Query the remote to see if it contains the object already. If so, don't send.
    pub fn write_object(&self, hashed: Hashed) -> Box<Future<Item = bool, Error = Error> + Send> {
        let lock = match self.catalog.try_lock(*hashed.as_hash()) {
            Ok(lock) => lock,
            Err(future) => return Box::new(future.map(|_| false)),
        };
        let (hash, bytes_opt) = hashed.into_components();

        match bytes_opt {
            Some(bytes) => {
                let ctx_res = self.inner.conn.lock().unwrap().get_pool_context(
                    &self.inner.pool,
                );
                let result = {
                    async_block! {
                        let mut ctx = ctx_res?;
                        await!(ctx.write_full_async(&hash.to_string(), &bytes))?;
                        lock.release();
                        Ok(true)
                    }
                };

                Box::new(result)
            }

            None => {
                unimplemented!("TODO: Must load local blob!");
            }
        }
    }

    /// Read a single object from the remote repository.
    ///
    /// This will instead read a local file if the object is already present on disk in the local
    /// blob store.
    pub fn read_object(
        &self,
        object_hash: ObjectHash,
    ) -> Box<Future<Item = Object, Error = Error> + Send> {
        let local_future = self.local.read_or_allocate_object(object_hash);
        let ctx_res = self.inner.conn.lock().unwrap().get_pool_context(
            &self.inner.pool,
        );

        let result = {
            async_block! {
                match await!(local_future)? {
                    Ok(object) => Ok(object),
                    Err(factory) => {
                        let mut ctx = ctx_res?;

                        let object_id = object_hash.to_string();
                        let stat = await!(ctx.stat_async(&object_id))?;

                        let mut buf = OwningRefMut::new(factory.with_size(stat.size as usize)?);

                        let mut total_read = 0;
                        let written_buf = loop {
                            let (bytes_read_u64, new_buf) =
                                await!(ctx.read_async(
                                    &object_id,
                                    buf.map_mut(|slice| &mut slice[total_read..]),
                                    total_read as u64,
                                ))?;

                            let bytes_read = bytes_read_u64 as usize;
                            total_read += bytes_read;

                            if bytes_read == new_buf.len() {
                                break new_buf.into_inner();
                            }

                            buf = new_buf;
                        };

                        await!(written_buf.finish())
                    }
                }
            }
        };

        Box::new(result)
    }
}
<|endoftext|>


<commit_before>use std::{fmt, str};
use std::io;
use std::io::Seek;
use std::io::Read as NewRead;

use regex::Regex;
use byteorder::LittleEndian as LE;
use byteorder::{ByteOrder, ReadBytesExt};

use super::io::{Read};
use super::consts;
use super::error;
use super::error::DriverError;
use super::error::MyError::MyDriverError;

#[derive(Clone, Eq, PartialEq)]
pub struct OkPacket {
    pub affected_rows: u64,
    pub last_insert_id: u64,
    pub status_flags: consts::StatusFlags,
    pub warnings: u16,
    pub info: Vec<u8>
}

impl OkPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<OkPacket> {
        let mut reader = &pld[1..];
        Ok(OkPacket{
            affected_rows: try!(reader.read_lenenc_int()),
            last_insert_id: try!(reader.read_lenenc_int()),
            status_flags: consts::StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>())),
            warnings: try!(reader.read_u16::<LE>()),
            info: {
                let mut info = Vec::with_capacity(reader.len());
                try!(NewRead::read_to_end(&mut reader, &mut info));
                info
            },
        })
    }
}

#[derive(Clone, Eq, PartialEq)]
pub struct ErrPacket {
    pub sql_state: Vec<u8>,
    pub error_message: Vec<u8>,
    pub error_code: u16
}

impl ErrPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<ErrPacket> {
        let mut reader = &pld[1..];
        let error_code = try!(reader.read_u16::<LE>());
        try!(ReadBytesExt::read_u8(&mut reader));
        Ok(ErrPacket{
            error_code: error_code,
            sql_state: {
                let mut sql_state = Vec::with_capacity(5);
                try!(reader.by_ref().take(5).read_to_end(&mut sql_state));
                sql_state
            },
            error_message: {
                let mut error_message = Vec::with_capacity(reader.len());
                try!(NewRead::read_to_end(&mut reader, &mut error_message));
                error_message
            },
        })
    }
}

impl fmt::Display for ErrPacket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ERROR {} ({}): {}",
               self.error_code,
               str::from_utf8(&self.sql_state[..]).unwrap(),
               str::from_utf8(&self.error_message[..]).unwrap())
    }
}

#[derive(Clone, Eq, PartialEq)]
pub struct EOFPacket {
    pub warnings: u16,
    pub status_flags: consts::StatusFlags,
}

impl EOFPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<EOFPacket> {
        let mut reader = &pld[1..];
        Ok(EOFPacket{
            warnings: try!(reader.read_u16::<LE>()),
            status_flags: consts::StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>())),
        })
    }
}

/// (major, minor, micro) mysql server version.
pub type ServerVersion = (u16, u16, u16);

fn parse_version(bytes: &[u8]) -> error::MyResult<ServerVersion> {
    let ver_str = String::from_utf8_lossy(bytes).into_owned();
    let version_re = Regex::new(r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)").unwrap();
    version_re.captures(&ver_str[..])
    .and_then(|capts| {
        Some((
            (capts.at(1).unwrap().parse::<u16>()).unwrap_or(0),
            (capts.at(2).unwrap().parse::<u16>()).unwrap_or(0),
            (capts.at(3).unwrap().parse::<u16>()).unwrap_or(0),
        ))
    })
    .and_then(|version| {
        if version == (0, 0, 0) {
            None
        } else {
            Some(version)
        }
    }).ok_or(MyDriverError(DriverError::CouldNotParseVersion))
}

#[derive(Clone, Eq, PartialEq)]
pub struct HandshakePacket {
    pub auth_plugin_data: Vec<u8>,
    pub auth_plugin_name: Vec<u8>,
    pub server_version: ServerVersion,
    pub connection_id: u32,
    pub capability_flags: consts::CapabilityFlags,
    pub status_flags: consts::StatusFlags,
    pub protocol_version: u8,
    pub character_set: u8,
}

impl HandshakePacket {
    pub fn from_payload(pld: &[u8]) -> error::MyResult<HandshakePacket> {
        let mut length_of_auth_plugin_data = 0i16;
        let mut auth_plugin_data: Vec<u8> = Vec::with_capacity(32);
        let mut auth_plugin_name: Vec<u8> = Vec::with_capacity(32);
        let mut character_set = 0u8;
        let mut status_flags = consts::StatusFlags::empty();
        let payload_len = pld.len();
        let mut reader = io::Cursor::new(pld);
        let protocol_version = try!(ReadBytesExt::read_u8(&mut reader));
        let version_bytes = try!(reader.read_to_null());
        let server_version = try!(parse_version(&version_bytes[..]));
        let connection_id = try!(reader.read_u32::<LE>());
        try!(reader.by_ref().take(8).read_to_end(&mut auth_plugin_data));
        // skip filler
        try!(reader.seek(io::SeekFrom::Current(1)));
        let lower_cf = try!(reader.read_u16::<LE>());
        let mut capability_flags = consts::CapabilityFlags::from_bits_truncate(lower_cf as u32);
        if reader.position() != payload_len as u64 {
            character_set = try!(ReadBytesExt::read_u8(&mut reader));
            status_flags = consts::StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>()));
            let upper_cf = try!(reader.read_u16::<LE>());
            capability_flags.insert(consts::CapabilityFlags::from_bits_truncate((upper_cf as u32) << 16));
            if capability_flags.contains(consts::CLIENT_PLUGIN_AUTH) {
                length_of_auth_plugin_data = try!(ReadBytesExt::read_u8(&mut reader)) as i16;
            } else {
                try!(reader.seek(io::SeekFrom::Current(1)));
            }
            try!(reader.seek(io::SeekFrom::Current(10)));
            if capability_flags.contains(consts::CLIENT_SECURE_CONNECTION) {
                let mut len = length_of_auth_plugin_data - 8i16;
                len = if len > 13i16 { len } else { 13i16 };
                try!(reader.by_ref().take(len as u64).read_to_end(&mut auth_plugin_data));
                if auth_plugin_data[auth_plugin_data.len() - 1] == 0u8 {
                    auth_plugin_data.pop();
                }
            }
            if capability_flags.contains(consts::CLIENT_PLUGIN_AUTH) {
                try!(reader.read_to_end(&mut auth_plugin_name));
                if auth_plugin_name[auth_plugin_name.len() - 1] == 0u8 {
                    auth_plugin_name.pop();
                }
            }
        }
        Ok(HandshakePacket{protocol_version: protocol_version, connection_id: connection_id,
                         auth_plugin_data: auth_plugin_data, server_version: server_version,
                         capability_flags: capability_flags, character_set: character_set,
                         status_flags: status_flags, auth_plugin_name: auth_plugin_name})
    }
}

#[cfg(test)]
#[allow(non_snake_case)]
mod test {
    pub use std::iter;
    pub use super::super::consts;
    pub use super::{OkPacket, ErrPacket, EOFPacket, HandshakePacket};

    #[test]
    fn should_parse_OK_packet() {
        let payload = [0u8, 1u8, 2u8, 8u8, 0u8, 4u8, 0u8, 32u8];
        let ok_packet = OkPacket::from_payload(&payload).unwrap();
        assert_eq!(ok_packet.affected_rows, 1);
        assert_eq!(ok_packet.last_insert_id, 2);
        assert_eq!(ok_packet.status_flags, consts::SERVER_MORE_RESULTS_EXISTS);
        assert_eq!(ok_packet.warnings, 4);
        assert_eq!(ok_packet.info, vec!(32u8));
    }
    #[test]
    fn should_parse_Error_packet() {
        let payload = [255u8, 1u8, 0u8, 35u8, 51u8, 68u8, 48u8, 48u8, 48u8,
                       32u8, 32u8];
        let err_packet = ErrPacket::from_payload(&payload).unwrap();
        assert_eq!(err_packet.error_code, 1);
        assert_eq!(err_packet.sql_state, vec!(51u8, 68u8, 48u8, 48u8, 48u8));
        assert_eq!(err_packet.error_message, vec!(32u8, 32u8));
    }
    #[test]
    fn should_parse_EOF_packet() {
        let payload = [0xfe_u8, 1u8, 0u8, 8u8, 0u8];
        let eof_packet = EOFPacket::from_payload(&payload).unwrap();
        assert_eq!(eof_packet.warnings, 1);
        assert_eq!(eof_packet.status_flags,
                   consts::SERVER_MORE_RESULTS_EXISTS);
    }
    #[test]
    fn should_parse_handshake_packet() {
        let payload = b"\x0a5.6.4\x00\x01\x00\x00\x00\x01\x02\x03\x04\x05\
                        \x06\x07\x08\x00\x04\x80";
        let handshake_packet = HandshakePacket::from_payload(payload)
                                               .unwrap();
        assert_eq!(handshake_packet.protocol_version, 0x0a);
        assert_eq!(handshake_packet.server_version, (5, 6, 4));
        assert_eq!(handshake_packet.connection_id, 1);
        assert_eq!(handshake_packet.auth_plugin_data,
                   vec!(1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8));
        assert_eq!(handshake_packet.capability_flags,
            consts::CLIENT_SECURE_CONNECTION | consts::CLIENT_LONG_FLAG);
        let mut payload = payload.to_vec();
        payload.push(33u8);
        payload.extend(vec!(8u8, 0u8).into_iter());
        payload.extend(vec!(0x08_u8, 0u8).into_iter());
        payload.extend(vec!(0x15_u8).into_iter());
        payload.extend(iter::repeat(0u8).take(10));
        payload.extend(vec!(0x26_u8, 0x3a_u8, 0x34_u8, 0x34_u8, 0x46_u8,
                            0x44_u8, 0x63_u8, 0x44_u8, 0x69_u8, 0x63_u8,
                            0x39_u8, 0x30_u8, 0x00_u8).into_iter());
        payload.extend(vec!(1u8, 2u8, 3u8, 4u8, 5u8, 0u8).into_iter());
        let handshake_packet =
            HandshakePacket::from_payload(&payload[..]).unwrap();
        assert_eq!(handshake_packet.protocol_version, 0x0a);
        assert_eq!(handshake_packet.connection_id, 1);
        assert_eq!(
            handshake_packet.auth_plugin_data,
            vec!(1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8,
                 0x26_u8, 0x3a_u8, 0x34_u8, 0x34_u8, 0x46_u8, 0x44_u8,
                 0x63_u8, 0x44_u8, 0x69_u8, 0x63_u8, 0x39_u8, 0x30_u8)
        );
        assert_eq!(handshake_packet.capability_flags,
                   consts::CLIENT_SECURE_CONNECTION |
                   consts::CLIENT_PLUGIN_AUTH |
                   consts::CLIENT_LONG_FLAG);
        assert_eq!(handshake_packet.character_set, 33);
        assert_eq!(handshake_packet.status_flags,
                   consts::SERVER_MORE_RESULTS_EXISTS);
        assert_eq!(handshake_packet.auth_plugin_name,
                   vec!(1u8, 2u8, 3u8, 4u8, 5u8));
    }
}
<commit_msg>Cleanup packet.rs<commit_after>use std::fmt;
use std::io;
use std::io::Read as StdRead;

use regex::Regex;
use byteorder::LittleEndian as LE;
use byteorder::{ByteOrder, ReadBytesExt};

use super::consts;
use super::consts::StatusFlags;
use super::consts::CapabilityFlags;
use super::error;
use super::error::DriverError;
use super::error::MyError::MyDriverError;
use super::io::Read;

#[derive(Clone, Eq, PartialEq)]
pub struct OkPacket {
    pub affected_rows: u64,
    pub last_insert_id: u64,
    pub status_flags: StatusFlags,
    pub warnings: u16,
    pub info: Vec<u8>
}

impl OkPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<OkPacket> {
        let mut reader = &pld[1..];
        Ok(OkPacket{
            affected_rows: try!(reader.read_lenenc_int()),
            last_insert_id: try!(reader.read_lenenc_int()),
            status_flags: StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>())),
            warnings: try!(reader.read_u16::<LE>()),
            info: {
                let mut info = Vec::with_capacity(reader.len());
                try!(reader.read_to_end(&mut info));
                info
            },
        })
    }
}

#[derive(Clone, Eq, PartialEq)]
pub struct ErrPacket {
    pub sql_state: Vec<u8>,
    pub error_message: Vec<u8>,
    pub error_code: u16
}

impl ErrPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<ErrPacket> {
        let mut reader = &pld[1..];
        let error_code = try!(reader.read_u16::<LE>());
        try!(reader.read_u8());
        Ok(ErrPacket{
            error_code: error_code,
            sql_state: {
                let mut sql_state = Vec::with_capacity(5);
                try!(reader.by_ref().take(5).read_to_end(&mut sql_state));
                sql_state
            },
            error_message: {
                let mut error_message = Vec::with_capacity(reader.len());
                try!(reader.read_to_end(&mut error_message));
                error_message
            },
        })
    }
}

impl fmt::Display for ErrPacket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ERROR {} ({}): {}",
               self.error_code,
               String::from_utf8_lossy(&self.sql_state[..]).into_owned(),
               String::from_utf8_lossy(&self.error_message[..]).into_owned())
    }
}

#[derive(Clone, Eq, PartialEq)]
pub struct EOFPacket {
    pub warnings: u16,
    pub status_flags: StatusFlags,
}

impl EOFPacket {
    pub fn from_payload(pld: &[u8]) -> io::Result<EOFPacket> {
        let mut reader = &pld[1..];
        Ok(EOFPacket{
            warnings: try!(reader.read_u16::<LE>()),
            status_flags: StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>())),
        })
    }
}

/// (major, minor, micro) mysql server version.
pub type ServerVersion = (u16, u16, u16);

fn parse_version(bytes: &[u8]) -> error::MyResult<ServerVersion> {
    let ver_str = String::from_utf8_lossy(bytes).into_owned();
    let version_re = Regex::new(r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)").unwrap();
    version_re.captures(&ver_str[..])
    .and_then(|capts| {
        Some((
            (capts.at(1).unwrap().parse::<u16>()).unwrap_or(0),
            (capts.at(2).unwrap().parse::<u16>()).unwrap_or(0),
            (capts.at(3).unwrap().parse::<u16>()).unwrap_or(0),
        ))
    }).and_then(|version| {
        if version == (0, 0, 0) {
            None
        } else {
            Some(version)
        }
    }).ok_or(MyDriverError(DriverError::CouldNotParseVersion))
}

#[derive(Clone, Eq, PartialEq)]
pub struct HandshakePacket {
    pub auth_plugin_data: Vec<u8>,
    pub auth_plugin_name: Vec<u8>,
    pub server_version: ServerVersion,
    pub connection_id: u32,
    pub capability_flags: CapabilityFlags,
    pub status_flags: StatusFlags,
    pub protocol_version: u8,
    pub character_set: u8,
}

impl HandshakePacket {
    pub fn from_payload(pld: &[u8]) -> error::MyResult<HandshakePacket> {
        let mut length_of_auth_plugin_data = 0i16;
        let mut auth_plugin_data: Vec<u8> = Vec::with_capacity(32);
        let mut auth_plugin_name: Vec<u8> = Vec::with_capacity(32);
        let mut character_set = 0u8;
        let mut status_flags = StatusFlags::empty();
        let payload_len = pld.len();
        let mut reader = io::Cursor::new(pld);
        let protocol_version = try!(reader.read_u8());
        let version_bytes = try!(reader.read_to_null());
        let server_version = try!(parse_version(&version_bytes[..]));
        let connection_id = try!(reader.read_u32::<LE>());
        try!(reader.by_ref().take(8).read_to_end(&mut auth_plugin_data));
        // skip filler
        { let pos = reader.position(); reader.set_position(pos + 1); }
        let lower_cf = try!(reader.read_u16::<LE>());
        let mut capability_flags = CapabilityFlags::from_bits_truncate(lower_cf as u32);
        if reader.position() != payload_len as u64 {
            character_set = try!(reader.read_u8());
            status_flags = StatusFlags::from_bits_truncate(try!(reader.read_u16::<LE>()));
            let upper_cf = try!(reader.read_u16::<LE>());
            capability_flags.insert(CapabilityFlags::from_bits_truncate((upper_cf as u32) << 16));
            if capability_flags.contains(consts::CLIENT_PLUGIN_AUTH) {
                length_of_auth_plugin_data = try!(reader.read_u8()) as i16;
            } else {
                let pos = reader.position();
                reader.set_position(pos + 1);
            }
            { let pos = reader.position(); reader.set_position(pos + 10); }
            if capability_flags.contains(consts::CLIENT_SECURE_CONNECTION) {
                let mut len = length_of_auth_plugin_data - 8i16;
                len = if len > 13i16 { len } else { 13i16 };
                try!(reader.by_ref().take(len as u64).read_to_end(&mut auth_plugin_data));
                if auth_plugin_data[auth_plugin_data.len() - 1] == 0u8 {
                    auth_plugin_data.pop();
                }
            }
            if capability_flags.contains(consts::CLIENT_PLUGIN_AUTH) {
                try!(reader.read_to_end(&mut auth_plugin_name));
                if auth_plugin_name[auth_plugin_name.len() - 1] == 0u8 {
                    auth_plugin_name.pop();
                }
            }
        }
        Ok(HandshakePacket{protocol_version: protocol_version, connection_id: connection_id,
                         auth_plugin_data: auth_plugin_data, server_version: server_version,
                         capability_flags: capability_flags, character_set: character_set,
                         status_flags: status_flags, auth_plugin_name: auth_plugin_name})
    }
}

#[cfg(test)]
#[allow(non_snake_case)]
mod test {
    pub use std::iter;
    pub use super::super::consts;
    pub use super::{OkPacket, ErrPacket, EOFPacket, HandshakePacket};

    #[test]
    fn should_parse_OK_packet() {
        let payload = [0u8, 1u8, 2u8, 8u8, 0u8, 4u8, 0u8, 32u8];
        let ok_packet = OkPacket::from_payload(&payload).unwrap();
        assert_eq!(ok_packet.affected_rows, 1);
        assert_eq!(ok_packet.last_insert_id, 2);
        assert_eq!(ok_packet.status_flags, consts::SERVER_MORE_RESULTS_EXISTS);
        assert_eq!(ok_packet.warnings, 4);
        assert_eq!(ok_packet.info, vec!(32u8));
    }
    #[test]
    fn should_parse_Error_packet() {
        let payload = [255u8, 1u8, 0u8, 35u8, 51u8, 68u8, 48u8, 48u8, 48u8,
                       32u8, 32u8];
        let err_packet = ErrPacket::from_payload(&payload).unwrap();
        assert_eq!(err_packet.error_code, 1);
        assert_eq!(err_packet.sql_state, vec!(51u8, 68u8, 48u8, 48u8, 48u8));
        assert_eq!(err_packet.error_message, vec!(32u8, 32u8));
    }
    #[test]
    fn should_parse_EOF_packet() {
        let payload = [0xfe_u8, 1u8, 0u8, 8u8, 0u8];
        let eof_packet = EOFPacket::from_payload(&payload).unwrap();
        assert_eq!(eof_packet.warnings, 1);
        assert_eq!(eof_packet.status_flags, consts::SERVER_MORE_RESULTS_EXISTS);
    }
    #[test]
    fn should_parse_handshake_packet() {
        let payload = b"\x0a5.6.4\x00\x01\x00\x00\x00\x01\x02\x03\x04\x05\
                        \x06\x07\x08\x00\x04\x80";
        let handshake_packet = HandshakePacket::from_payload(payload).unwrap();
        assert_eq!(handshake_packet.protocol_version, 0x0a);
        assert_eq!(handshake_packet.server_version, (5, 6, 4));
        assert_eq!(handshake_packet.connection_id, 1);
        assert_eq!(handshake_packet.auth_plugin_data,
                   vec!(1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8));
        assert_eq!(handshake_packet.capability_flags,
            consts::CLIENT_SECURE_CONNECTION | consts::CLIENT_LONG_FLAG);
        let mut payload = payload.to_vec();
        payload.push(33u8);
        payload.extend(vec!(8u8, 0u8).into_iter());
        payload.extend(vec!(0x08_u8, 0u8).into_iter());
        payload.extend(vec!(0x15_u8).into_iter());
        payload.extend(iter::repeat(0u8).take(10));
        payload.extend(vec!(0x26_u8, 0x3a_u8, 0x34_u8, 0x34_u8, 0x46_u8,
                            0x44_u8, 0x63_u8, 0x44_u8, 0x69_u8, 0x63_u8,
                            0x39_u8, 0x30_u8, 0x00_u8).into_iter());
        payload.extend(vec!(1u8, 2u8, 3u8, 4u8, 5u8, 0u8).into_iter());
        let handshake_packet =
            HandshakePacket::from_payload(&payload[..]).unwrap();
        assert_eq!(handshake_packet.protocol_version, 0x0a);
        assert_eq!(handshake_packet.connection_id, 1);
        assert_eq!(
            handshake_packet.auth_plugin_data,
            vec!(1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8,
                 0x26_u8, 0x3a_u8, 0x34_u8, 0x34_u8, 0x46_u8, 0x44_u8,
                 0x63_u8, 0x44_u8, 0x69_u8, 0x63_u8, 0x39_u8, 0x30_u8)
        );
        assert_eq!(handshake_packet.capability_flags,
                   consts::CLIENT_SECURE_CONNECTION |
                   consts::CLIENT_PLUGIN_AUTH |
                   consts::CLIENT_LONG_FLAG);
        assert_eq!(handshake_packet.character_set, 33);
        assert_eq!(handshake_packet.status_flags,
                   consts::SERVER_MORE_RESULTS_EXISTS);
        assert_eq!(handshake_packet.auth_plugin_name,
                   vec!(1u8, 2u8, 3u8, 4u8, 5u8));
    }
}
<|endoftext|>


<commit_before>use ammonia::Ammonia;
use comrak;
use htmlescape::encode_minimal;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            ("code", ["class"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let allowed_classes = [
            (
                "code",
                [
                    "language-bash",
                    "language-clike",
                    "language-glsl",
                    "language-go",
                    "language-ini",
                    "language-javascript",
                    "language-json",
                    "language-markup",
                    "language-protobuf",
                    "language-ruby",
                    "language-rust",
                    "language-scss",
                    "language-sql",
                    "yaml",
                ].iter()
                    .cloned()
                    .collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            allowed_classes: allowed_classes,
            ..Ammonia::default()
        };
        MarkdownRenderer {
            html_sanitizer: html_sanitizer,
        }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

static MARKDOWN_EXTENSIONS: [&'static str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

pub fn readme_to_html(text: &str, filename: &str) -> CargoResult<String> {
    for e in MARKDOWN_EXTENSIONS.iter() {
        if filename.to_lowercase().ends_with(e) {
            return markdown_to_html(text);
        }
    }

    Ok(encode_minimal(text).replace("\n", "
\n"))
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block).unwrap();
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f).unwrap(),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f).unwrap(),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }
}
<commit_msg>clippy!<commit_after>use ammonia::Ammonia;
use comrak;
use htmlescape::encode_minimal;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            ("code", ["class"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let allowed_classes = [
            (
                "code",
                [
                    "language-bash",
                    "language-clike",
                    "language-glsl",
                    "language-go",
                    "language-ini",
                    "language-javascript",
                    "language-json",
                    "language-markup",
                    "language-protobuf",
                    "language-ruby",
                    "language-rust",
                    "language-scss",
                    "language-sql",
                    "yaml",
                ].iter()
                    .cloned()
                    .collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            allowed_classes: allowed_classes,
            ..Ammonia::default()
        };
        MarkdownRenderer {
            html_sanitizer: html_sanitizer,
        }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

static MARKDOWN_EXTENSIONS: [&'static str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

pub fn readme_to_html(text: &str, filename: &str) -> CargoResult<String> {
    for e in &MARKDOWN_EXTENSIONS {
        if filename.to_lowercase().ends_with(e) {
            return markdown_to_html(text);
        }
    }

    Ok(encode_minimal(text).replace("\n", "
\n"))
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block).unwrap();
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f).unwrap(),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f).unwrap(),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }
}
<|endoftext|>