<commit_before>//! Parsing inner Screeps websocket update messages.
use std::borrow::Cow;
use std::convert::AsRef;
use std::marker::PhantomData;
use std::fmt;

use serde::{Deserialize, Deserializer};
use serde::de::{self, SeqAccess, Unexpected, Visitor};

use serde_json;
use websocket::Channel;

use RoomName;

use self::room_map_view::RoomMapViewUpdate;
use self::user_cpu::UserCpuUpdate;
use self::user_console::UserConsoleUpdate;
use self::room::RoomUpdate;
use self::messages::{ConversationUpdate, MessageUpdate};

pub mod room;
pub mod messages;
pub mod room_map_view;
pub mod user_cpu;
pub mod user_console;

/// An update to a Screeps server 'channel' that has been subscribed to.
#[derive(Clone, Debug)]
pub enum ChannelUpdate<'a> {
    /// A 'map view' update of a room. Sent once per tick.
    RoomMapView {
        /// The name of the room this is an update for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
        /// The data: the positions and nondescript types of entities in this room.
        update: RoomMapViewUpdate,
    },
    /// An update of objects in a room. Sent once per tick for up to 2 rooms per account.
    /// Other subscribed rooms receive `ChannelUpdate::NoRoomDetail` instead.
    RoomDetail {
        /// The name of the room this is an update for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
        /// The data: all properties of all objects in this room that have changed since the last tick.
        update: RoomUpdate,
    },
    /// An update/error received for room detail subscriptions which will not receive an update this tick.
    ///
    /// This is due to screeps having a 2-room subscription limit. If more are subscribed globally (per-account,
    /// not per-connection) then only 2 receive actual updates.
    ///
    /// TODO: This should have a better name, and possibly be incorporated into `ChannelUpdate::RoomDetail`.
    NoRoomDetail {
        /// The name of the room this is a notification for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
    },
    /// An update on the last tick's CPU and memory usage. Sent once per tick.
    UserCpu {
        /// The user ID this is a cpu/memory update for.
        user_id: Cow<'a, str>,
        /// The update.
        update: UserCpuUpdate,
    },
    /// An update on all user script log messages last tick or a specific error message.
    /// Sent once and exactly once per tick unless:
    ///
    /// - Multiple script errors occurred.
    /// - Normal log messages were sent and a script error also occurred.
    ///
    /// In either of these cases, two or more of these updates will occur in short succession.
    UserConsole {
        /// The user ID this console update is for.
        user_id: Cow<'a, str>,
        /// The update.
        update: UserConsoleUpdate,
    },
    /// An update on the user's credit total at the end of the last tick. Sent once per tick.
    UserCredits {
        /// The user ID this credit update is for.
        user_id: Cow<'a, str>,
        /// The number of credits.
        update: f64,
    },
    /// An update on a new message received by a user. Sent each time a user receives a message.
    UserMessage {
        /// The user ID this message update is for.
        user_id: Cow<'a, str>,
        /// The message update.
        update: MessageUpdate,
    },
    /// An update on a change in a conversation the user is participating in. Sent each time either
    /// this user or the user or the respondent either sends a message, or reads a previously unread
    /// message.
    UserConversation {
        /// The user ID of the subscribed user this update is for.
        user_id: Cow<'a, str>,
        /// The user ID of the other user in the conversation.
        target_user_id: Cow<'a, str>,
        /// The message update.
        update: ConversationUpdate,
    },
    /// Another update that was not accounted for.
    ///
    /// TODO: when we're sure of everything, remove this variant.
    Other {
        /// The string describing what channel this is.
        channel: Cow<'a, str>,
        /// The update.
        update: serde_json::Value,
    },
}

impl<'a> ChannelUpdate<'a> {
    /// If this update is directly associated with a room, gets the room name.
    pub fn shard_name(&self) -> Option<&str> {
        match *self {
            ChannelUpdate::RoomMapView { ref shard_name, .. }
            | ChannelUpdate::RoomDetail { ref shard_name, .. }
            | ChannelUpdate::NoRoomDetail { ref shard_name, .. } => shard_name.as_ref().map(AsRef::as_ref),
            _ => None,
        }
    }
    /// If this update is directly associated with a room, gets the room name.
    pub fn room_name(&self) -> Option<&RoomName> {
        match *self {
            ChannelUpdate::RoomMapView { ref room_name, .. }
            | ChannelUpdate::RoomDetail { ref room_name, .. }
            | ChannelUpdate::NoRoomDetail { ref room_name, .. } => Some(room_name),
            _ => None,
        }
    }

    /// If this update is directly associated with a subscribed user id, gets the user id.
    ///
    /// The user_id is *always* the user id of the subscribed user, never another associated id.
    ///
    /// For example, with 'message' updates, this is the user ID of the user receiving the
    /// notification, *not* the user id of the sender.
    pub fn user_id(&self) -> Option<&str> {
        match *self {
            ChannelUpdate::UserCpu { ref user_id, .. }
            | ChannelUpdate::UserConsole { ref user_id, .. }
            | ChannelUpdate::UserMessage { ref user_id, .. }
            | ChannelUpdate::UserConversation { ref user_id, .. }
            | ChannelUpdate::UserCredits { ref user_id, .. } => Some(user_id.as_ref()),
            _ => None,
        }
    }

    /// Gets the channel which this update is from.
    ///
    /// This channel specification can be used to subscribe or unsubscribe from this channel if needed.
    pub fn channel(&self) -> Channel {
        match *self {
            ChannelUpdate::RoomMapView {
                room_name,
                ref shard_name,
                ..
            } => Channel::room_map_view(room_name, shard_name.as_ref().map(AsRef::as_ref)),
            ChannelUpdate::RoomDetail {
                room_name,
                ref shard_name,
                ..
            }
            | ChannelUpdate::NoRoomDetail {
                room_name,
                ref shard_name,
                ..
            } => Channel::room_detail(room_name, shard_name.as_ref().map(AsRef::as_ref)),
            ChannelUpdate::UserCpu { ref user_id, .. } => Channel::user_cpu(user_id.as_ref()),
            ChannelUpdate::UserConsole { ref user_id, .. } => Channel::user_console(user_id.as_ref()),
            ChannelUpdate::UserCredits { ref user_id, .. } => Channel::user_credits(user_id.as_ref()),
            ChannelUpdate::UserMessage { ref user_id, .. } => Channel::user_messages(user_id.as_ref()),
            ChannelUpdate::UserConversation {
                ref user_id,
                ref target_user_id,
                ..
            } => Channel::user_conversation(user_id.as_ref(), target_user_id.as_ref()),
            ChannelUpdate::Other { ref channel, .. } => Channel::other(channel.as_ref()),
        }
    }
}

struct ChannelUpdateVisitor<'a> {
    marker: PhantomData<ChannelUpdate<'a>>,
}

impl<'a> ChannelUpdateVisitor<'a> {
    fn new() -> Self {
        ChannelUpdateVisitor {
            marker: PhantomData,
        }
    }
}

impl<'de> Visitor<'de> for ChannelUpdateVisitor<'de> {
    type Value = ChannelUpdate<'static>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a sequence")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        const ROOM_MAP_VIEW_PREFIX: &str = "roomMap2:";
        const ROOM_PREFIX: &str = "room:";
        const ROOM_ERR_PREFIX: &str = "err@room:"; // TODO: generic error handling with this `err@` format.
        const USER_PREFIX: &str = "user:";
        const USER_CPU: &str = "cpu";
        const USER_CONSOLE: &str = "console";
        const USER_CREDITS: &str = "money";
        const USER_MESSAGE: &str = "newMessage";
        const USER_CONVERSATION_PREFIX: &str = "message:";

        let channel: &str = seq.next_element()?
            .ok_or_else(|| de::Error::invalid_length(2, &self))?;

        macro_rules! finish_other {
            () => ({
                return Ok(ChannelUpdate::Other {
                    channel: channel.to_owned().into(),
                    update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                });
            })
        }

        if channel.starts_with(ROOM_MAP_VIEW_PREFIX) {
            let room_name_and_shard = &channel[ROOM_MAP_VIEW_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, "/");
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };
            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            return Ok(ChannelUpdate::RoomMapView {
                room_name: room_name,
                shard_name: shard_name.map(ToOwned::to_owned),
                update: seq.next_element()?
                    .ok_or_else(|| de::Error::invalid_length(2, &self))?,
            });
        } else if channel.starts_with(ROOM_PREFIX) {
            let room_name_and_shard = &channel[ROOM_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };

            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            return Ok(ChannelUpdate::RoomDetail {
                room_name: room_name,
                shard_name: shard_name.map(ToOwned::to_owned),
                update: seq.next_element()?
                    .ok_or_else(|| de::Error::invalid_length(2, &self))?,
            });
        } else if channel.starts_with(ROOM_ERR_PREFIX) {
            let room_name_and_shard = &channel[ROOM_ERR_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };

            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            let err_message = seq.next_element::<&str>()?
                .ok_or_else(|| de::Error::invalid_length(2, &self))?;

            // TODO: This is currently just a patch in for a common error message, but we don't handle any other
            // errors that are reported as `err@<rest of channel name>`.
            //
            // We should:
            // A. find out if the server actually ever sends other error messages, or if this is hardcoded on the
            //    other side too.
            // B. add handling of all err@ messages into a variant which can then just parse the channel name into
            //    a `Channel`.
            if err_message == "subscribe limit reached" {
                return Ok(ChannelUpdate::NoRoomDetail {
                    room_name: room_name,
                    shard_name: shard_name.map(ToOwned::to_owned),
                });
            }
        } else if channel.starts_with(USER_PREFIX) {
            let user_id_and_part = &channel[USER_PREFIX.len()..];

            let (user_id, sub_channel) = {
                let mut split = user_id_and_part.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(v1), Some(v2)) => (v1, v2),
                    _ => finish_other!(),
                }
            };

            match sub_channel {
                USER_CPU => {
                    return Ok(ChannelUpdate::UserCpu {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?
                            .ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                }
                USER_CONSOLE => {
                    return Ok(ChannelUpdate::UserConsole {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?
                            .ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                }
                USER_CREDITS => {
                    return Ok(ChannelUpdate::UserCredits {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?
                            .ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    })
                }
                USER_MESSAGE => {
                    return Ok(ChannelUpdate::UserMessage {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?
                            .ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    })
                }
                sub_channel => if sub_channel.starts_with(USER_CONVERSATION_PREFIX) {
                    let target_user_id = &sub_channel[USER_CONVERSATION_PREFIX.len()..];

                    return Ok(ChannelUpdate::UserConversation {
                        user_id: user_id.to_owned().into(),
                        target_user_id: target_user_id.to_owned().into(),
                        update: seq.next_element()?
                            .ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                } else {
                    finish_other!()
                },
            }
        }

        finish_other!();
    }
}

impl<'de> Deserialize<'de> for ChannelUpdate<'static> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(ChannelUpdateVisitor::new())
    }
}
<commit_msg>Fixed ambiguous functions as of latest nightly.<commit_after>//! Parsing inner Screeps websocket update messages.
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;

use serde::de::{self, SeqAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};

use serde_json;
use websocket::Channel;

use RoomName;

use self::messages::{ConversationUpdate, MessageUpdate};
use self::room::RoomUpdate;
use self::room_map_view::RoomMapViewUpdate;
use self::user_console::UserConsoleUpdate;
use self::user_cpu::UserCpuUpdate;

pub mod messages;
pub mod room;
pub mod room_map_view;
pub mod user_console;
pub mod user_cpu;

/// An update to a Screeps server 'channel' that has been subscribed to.
#[derive(Clone, Debug)]
pub enum ChannelUpdate<'a> {
    /// A 'map view' update of a room. Sent once per tick.
    RoomMapView {
        /// The name of the room this is an update for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
        /// The data: the positions and nondescript types of entities in this room.
        update: RoomMapViewUpdate,
    },
    /// An update of objects in a room. Sent once per tick for up to 2 rooms per account.
    /// Other subscribed rooms receive `ChannelUpdate::NoRoomDetail` instead.
    RoomDetail {
        /// The name of the room this is an update for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
        /// The data: all properties of all objects in this room that have changed since the last tick.
        update: RoomUpdate,
    },
    /// An update/error received for room detail subscriptions which will not receive an update this tick.
    ///
    /// This is due to screeps having a 2-room subscription limit. If more are subscribed globally (per-account,
    /// not per-connection) then only 2 receive actual updates.
    ///
    /// TODO: This should have a better name, and possibly be incorporated into `ChannelUpdate::RoomDetail`.
    NoRoomDetail {
        /// The name of the room this is a notification for.
        room_name: RoomName,
        /// The shard the room is in, if any.
        shard_name: Option<String>,
    },
    /// An update on the last tick's CPU and memory usage. Sent once per tick.
    UserCpu {
        /// The user ID this is a cpu/memory update for.
        user_id: Cow<'a, str>,
        /// The update.
        update: UserCpuUpdate,
    },
    /// An update on all user script log messages last tick or a specific error message.
    /// Sent once and exactly once per tick unless:
    ///
    /// - Multiple script errors occurred.
    /// - Normal log messages were sent and a script error also occurred.
    ///
    /// In either of these cases, two or more of these updates will occur in short succession.
    UserConsole {
        /// The user ID this console update is for.
        user_id: Cow<'a, str>,
        /// The update.
        update: UserConsoleUpdate,
    },
    /// An update on the user's credit total at the end of the last tick. Sent once per tick.
    UserCredits {
        /// The user ID this credit update is for.
        user_id: Cow<'a, str>,
        /// The number of credits.
        update: f64,
    },
    /// An update on a new message received by a user. Sent each time a user receives a message.
    UserMessage {
        /// The user ID this message update is for.
        user_id: Cow<'a, str>,
        /// The message update.
        update: MessageUpdate,
    },
    /// An update on a change in a conversation the user is participating in. Sent each time either
    /// this user or the user or the respondent either sends a message, or reads a previously unread
    /// message.
    UserConversation {
        /// The user ID of the subscribed user this update is for.
        user_id: Cow<'a, str>,
        /// The user ID of the other user in the conversation.
        target_user_id: Cow<'a, str>,
        /// The message update.
        update: ConversationUpdate,
    },
    /// Another update that was not accounted for.
    ///
    /// TODO: when we're sure of everything, remove this variant.
    Other {
        /// The string describing what channel this is.
        channel: Cow<'a, str>,
        /// The update.
        update: serde_json::Value,
    },
}

impl<'a> ChannelUpdate<'a> {
    /// If this update is directly associated with a room, gets the room name.
    pub fn shard_name(&self) -> Option<&str> {
        match *self {
            ChannelUpdate::RoomMapView { ref shard_name, .. }
            | ChannelUpdate::RoomDetail { ref shard_name, .. }
            | ChannelUpdate::NoRoomDetail { ref shard_name, .. } => shard_name.as_ref().map(String::as_str),
            _ => None,
        }
    }
    /// If this update is directly associated with a room, gets the room name.
    pub fn room_name(&self) -> Option<&RoomName> {
        match *self {
            ChannelUpdate::RoomMapView { ref room_name, .. }
            | ChannelUpdate::RoomDetail { ref room_name, .. }
            | ChannelUpdate::NoRoomDetail { ref room_name, .. } => Some(room_name),
            _ => None,
        }
    }

    /// If this update is directly associated with a subscribed user id, gets the user id.
    ///
    /// The user_id is *always* the user id of the subscribed user, never another associated id.
    ///
    /// For example, with 'message' updates, this is the user ID of the user receiving the
    /// notification, *not* the user id of the sender.
    pub fn user_id(&self) -> Option<&str> {
        match *self {
            ChannelUpdate::UserCpu { ref user_id, .. }
            | ChannelUpdate::UserConsole { ref user_id, .. }
            | ChannelUpdate::UserMessage { ref user_id, .. }
            | ChannelUpdate::UserConversation { ref user_id, .. }
            | ChannelUpdate::UserCredits { ref user_id, .. } => Some(user_id.as_ref()),
            _ => None,
        }
    }

    /// Gets the channel which this update is from.
    ///
    /// This channel specification can be used to subscribe or unsubscribe from this channel if needed.
    pub fn channel(&self) -> Channel {
        match *self {
            ChannelUpdate::RoomMapView {
                room_name,
                ref shard_name,
                ..
            } => Channel::room_map_view(room_name, shard_name.as_ref().map(String::as_str)),
            ChannelUpdate::RoomDetail {
                room_name,
                ref shard_name,
                ..
            }
            | ChannelUpdate::NoRoomDetail {
                room_name,
                ref shard_name,
                ..
            } => Channel::room_detail(room_name, shard_name.as_ref().map(String::as_str)),
            ChannelUpdate::UserCpu { ref user_id, .. } => Channel::user_cpu(user_id.as_ref()),
            ChannelUpdate::UserConsole { ref user_id, .. } => Channel::user_console(user_id.as_ref()),
            ChannelUpdate::UserCredits { ref user_id, .. } => Channel::user_credits(user_id.as_ref()),
            ChannelUpdate::UserMessage { ref user_id, .. } => Channel::user_messages(user_id.as_ref()),
            ChannelUpdate::UserConversation {
                ref user_id,
                ref target_user_id,
                ..
            } => Channel::user_conversation(user_id.as_ref(), target_user_id.as_ref()),
            ChannelUpdate::Other { ref channel, .. } => Channel::other(channel.as_ref()),
        }
    }
}

struct ChannelUpdateVisitor<'a> {
    marker: PhantomData<ChannelUpdate<'a>>,
}

impl<'a> ChannelUpdateVisitor<'a> {
    fn new() -> Self {
        ChannelUpdateVisitor { marker: PhantomData }
    }
}

impl<'de> Visitor<'de> for ChannelUpdateVisitor<'de> {
    type Value = ChannelUpdate<'static>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a sequence")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: SeqAccess<'de>,
    {
        const ROOM_MAP_VIEW_PREFIX: &str = "roomMap2:";
        const ROOM_PREFIX: &str = "room:";
        const ROOM_ERR_PREFIX: &str = "err@room:"; // TODO: generic error handling with this `err@` format.
        const USER_PREFIX: &str = "user:";
        const USER_CPU: &str = "cpu";
        const USER_CONSOLE: &str = "console";
        const USER_CREDITS: &str = "money";
        const USER_MESSAGE: &str = "newMessage";
        const USER_CONVERSATION_PREFIX: &str = "message:";

        let channel: &str = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;

        macro_rules! finish_other {
            () => {{
                return Ok(ChannelUpdate::Other {
                    channel: channel.to_owned().into(),
                    update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                });
            }};
        }

        if channel.starts_with(ROOM_MAP_VIEW_PREFIX) {
            let room_name_and_shard = &channel[ROOM_MAP_VIEW_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, "/");
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };
            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            return Ok(ChannelUpdate::RoomMapView {
                room_name: room_name,
                shard_name: shard_name.map(ToOwned::to_owned),
                update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
            });
        } else if channel.starts_with(ROOM_PREFIX) {
            let room_name_and_shard = &channel[ROOM_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };

            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            return Ok(ChannelUpdate::RoomDetail {
                room_name: room_name,
                shard_name: shard_name.map(ToOwned::to_owned),
                update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
            });
        } else if channel.starts_with(ROOM_ERR_PREFIX) {
            let room_name_and_shard = &channel[ROOM_ERR_PREFIX.len()..];

            let (shard_name, room_name) = {
                let mut split = room_name_and_shard.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(shard), Some(room)) => (Some(shard), room),
                    (Some(room), None) => (None, room),
                    _ => finish_other!(),
                }
            };

            let room_name = RoomName::new(room_name).map_err(|_| {
                de::Error::invalid_value(
                    Unexpected::Str(room_name),
                    &"room name formatted `(E|W)[0-9]+(N|S)[0-9]+`",
                )
            })?;

            let err_message = seq.next_element::<&str>()?
                .ok_or_else(|| de::Error::invalid_length(2, &self))?;

            // TODO: This is currently just a patch in for a common error message, but we don't handle any other
            // errors that are reported as `err@<rest of channel name>`.
            //
            // We should:
            // A. find out if the server actually ever sends other error messages, or if this is hardcoded on the
            //    other side too.
            // B. add handling of all err@ messages into a variant which can then just parse the channel name into
            //    a `Channel`.
            if err_message == "subscribe limit reached" {
                return Ok(ChannelUpdate::NoRoomDetail {
                    room_name: room_name,
                    shard_name: shard_name.map(ToOwned::to_owned),
                });
            }
        } else if channel.starts_with(USER_PREFIX) {
            let user_id_and_part = &channel[USER_PREFIX.len()..];

            let (user_id, sub_channel) = {
                let mut split = user_id_and_part.splitn(2, '/');
                match (split.next(), split.next()) {
                    (Some(v1), Some(v2)) => (v1, v2),
                    _ => finish_other!(),
                }
            };

            match sub_channel {
                USER_CPU => {
                    return Ok(ChannelUpdate::UserCpu {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                }
                USER_CONSOLE => {
                    return Ok(ChannelUpdate::UserConsole {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                }
                USER_CREDITS => {
                    return Ok(ChannelUpdate::UserCredits {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    })
                }
                USER_MESSAGE => {
                    return Ok(ChannelUpdate::UserMessage {
                        user_id: user_id.to_owned().into(),
                        update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    })
                }
                sub_channel => if sub_channel.starts_with(USER_CONVERSATION_PREFIX) {
                    let target_user_id = &sub_channel[USER_CONVERSATION_PREFIX.len()..];

                    return Ok(ChannelUpdate::UserConversation {
                        user_id: user_id.to_owned().into(),
                        target_user_id: target_user_id.to_owned().into(),
                        update: seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?,
                    });
                } else {
                    finish_other!()
                },
            }
        }

        finish_other!();
    }
}

impl<'de> Deserialize<'de> for ChannelUpdate<'static> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(ChannelUpdateVisitor::new())
    }
}
<|endoftext|>


<commit_before>//! Processes Twee files and builds an abstract syntax tree.
//!
//! The Frontend is responsible for processing a Twee file by
//! generating a Token Stream from the input and parsing this stream to build
//! an Abstract Syntax Tree.
//!
//! # Example
//!
//! This small example shows how the differend submodules interact and can
//! be used to parse a Twee-File.
//!
//! ```
//! # extern crate zwreec;
//! use std::io::Cursor;
//!
//! let cfg = zwreec::config::Config::default_config();
//! let mut twee = Cursor::new("::Start\nHello World".to_string().into_bytes());
//!
//! // Generate Token Stream
//! let tokens = zwreec::frontend::lexer::lex(&cfg, &mut twee);
//!
//! // Parse Tokens
//! let p = zwreec::frontend::parser::Parser::new(&cfg);
//! let ast = zwreec::frontend::ast::AST::build(
//!     p.parse(tokens)
//! );
//! ```
pub mod lexer;
pub mod parser;
pub mod ast;
pub mod codegen;
pub mod expressionparser;
<commit_msg>Fix typo in frontend docs<commit_after>//! Processes Twee files and builds an abstract syntax tree.
//!
//! The Frontend is responsible for processing a Twee file by
//! generating a Token Stream from the input and parsing this stream to build
//! an Abstract Syntax Tree.
//!
//! # Example
//!
//! This small example shows how the different submodules interact and can
//! be used to parse a Twee-File.
//!
//! ```
//! # extern crate zwreec;
//! use std::io::Cursor;
//!
//! let cfg = zwreec::config::Config::default_config();
//! let mut twee = Cursor::new("::Start\nHello World".to_string().into_bytes());
//!
//! // Generate Token Stream
//! let tokens = zwreec::frontend::lexer::lex(&cfg, &mut twee);
//!
//! // Parse Tokens
//! let p = zwreec::frontend::parser::Parser::new(&cfg);
//! let ast = zwreec::frontend::ast::AST::build(
//!     p.parse(tokens)
//! );
//! ```
pub mod lexer;
pub mod parser;
pub mod ast;
pub mod codegen;
pub mod expressionparser;
<|endoftext|>


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

// xfail-win32
// error-pattern:ran out of stack

// Test that the task fails after hitting the recursion limit, but
// that it doesn't bring down the whole proc

pub fn main() {
    do task::spawn_unlinked {
        fn f() { f() };
        f();
    };
}
<commit_msg>auto merge of #6118 : brson/rust/too-much-recursion, r=thestinger<commit_after><|endoftext|>


<commit_before>pub type dev_t = u64;
pub type uid_t = u32;
pub type gid_t = u32;
pub type ino_t = u64;
pub type mode_t = u32;
pub type nlink_t = u64;
pub type off_t = i64;
pub type pid_t = i32;
pub type clock_t = i64;
pub type id_t = u32;
pub type time_t = i64;
pub type suseconds_t = i64;
pub type key_t = i32;
pub type clockid_t = i32;
pub type timer_t = *mut i8;
pub type blksize_t = i64;
pub type blkcnt_t = i64;
pub type fsblkcnt_t = u64;
pub type fsfilcnt_t = u64;
pub type pthread_t = u64;

#[repr(C)]
pub struct pthread_attr_t {
    data: [u64, ..7u],
}

#[repr(C)]
pub struct pthread_mutex_t {
    _data: [u64, ..5u],
}

#[repr(C)]
pub struct pthread_mutexattr_t {
    _data: [u32, ..1u],
}

#[repr(C)]
pub struct pthread_cond_t {
    _data: [u64, ..6u],
}

#[repr(C)]
pub struct pthread_condattr_t {
    _data: [u32, ..1u],
}

pub type pthread_key_t = u32;
pub type pthread_once_t = i32;

#[repr(C)]
pub struct pthread_rwlock_t {
    _data: [u64, ..7u],
}

#[repr(C)]
pub struct pthread_rwlockattr_t {
    _data: [u64, ..1u],
}

pub type pthread_spinlock_t = i32;

#[repr(C)]
pub struct pthread_barrier_t {
    _data: [u64, ..4u],
}

#[repr(C)]
pub struct pthread_barrierattr_t {
    _data: [u32, ..1u],
}
<commit_msg>sys/types.h<commit_after>pub type dev_t = u64;
pub type uid_t = u32;
pub type gid_t = u32;
pub type ino_t = u64;
pub type mode_t = u32;
pub type nlink_t = u64;
pub type off_t = i64;
pub type pid_t = i32;
pub type clock_t = i64;
pub type id_t = u32;
pub type time_t = i64;
pub type suseconds_t = i64;
pub type key_t = i32;
pub type clockid_t = i32;
pub type timer_t = *mut i8;
pub type blksize_t = i64;
pub type blkcnt_t = i64;
pub type fsblkcnt_t = u64;
pub type fsfilcnt_t = u64;
pub type pthread_t = u64;

#[repr(C)]
pub struct pthread_attr_t {
    _data: [u64, ..7u],
}

new!(pthread_attr_t)

#[repr(C)]
pub struct pthread_mutex_t {
    _data: [u64, ..5u],
}

new!(pthread_mutex_t)

#[repr(C)]
pub struct pthread_mutexattr_t {
    _data: [u32, ..1u],
}

new!(pthread_mutexattr_t)

#[repr(C)]
pub struct pthread_cond_t {
    _data: [u64, ..6u],
}

new!(pthread_cond_t)

#[repr(C)]
pub struct pthread_condattr_t {
    _data: [u32, ..1u],
}

new!(pthread_condattr_t)

pub type pthread_key_t = u32;
pub type pthread_once_t = i32;

#[repr(C)]
pub struct pthread_rwlock_t {
    _data: [u64, ..7u],
}

new!(pthread_rwlock_t)

#[repr(C)]
pub struct pthread_rwlockattr_t {
    _data: [u64, ..1u],
}

new!(pthread_rwlockattr_t)

pub type pthread_spinlock_t = i32;

#[repr(C)]
pub struct pthread_barrier_t {
    _data: [u64, ..4u],
}

new!(pthread_barrier_t)

#[repr(C)]
pub struct pthread_barrierattr_t {
    _data: [u32, ..1u],
}

new!(pthread_barrierattr_t)
<|endoftext|>


<commit_before>use std::path::Path;
use std::fs::{DirBuilder, metadata};
use super::Error;

pub struct Database {
    name: String
}

impl Database {
    pub fn create(name: &str, path: &str) -> Result<Database, Error> {
        let d = Database { name: name.to_string() };
        DirBuilder::new()
            .recursive(true)
            .create(Path::new(path).join(name)).unwrap();

        info!("created new database: {}", d.name);
        Ok(d)
    }

    pub fn load(name: &str, path: &str) -> Result<Database, Error>{
        if try!(metadata(Path::new(path).join(name))).is_dir() {
            info!("loaded database: {}", name.to_string());
            Ok(Database { name: name.to_string() })
        } else {
            warn!("could not load database: {} at: {}", name, path);
            Err(Error::LoadDatabase)
        }
    }

    pub fn get_name(&self) -> String {
        self.name.clone()
    }
}

#[cfg(test)]
mod test {
    use super::Database;
    use std::fs::{File, metadata};
    use std::path::Path;

    #[test]
    fn create_db() {
        let path = "/tmp/test1/";
        let name = "test_db";
        let mut db = Database::create(&name, &path);

        assert!(metadata(&path).is_ok());
        assert!(metadata(&path).unwrap().is_dir());
    }

    #[test]
    fn load_db() {
        let path = "/tmp/test1/";
        let name = "test_db2";
        let db = Database::create(&name, &path).unwrap();

        let db2 = Database::load(&name, &path).unwrap();

        assert_eq!(db.get_name(), db2.get_name());
    }
}
<commit_msg>updated database api<commit_after>use std::path::Path;
use std::fs::{DirBuilder, metadata};
use super::Error;
use super::DatabaseConfig;

pub struct Database {
    name: String,
    config: DatabaseConfig
}

impl Database {
    pub fn create(name: &str, config: DatabaseConfig) -> Result<Database, Error> {
        let d = Database { name: name.to_string(), config: config.clone() };
        DirBuilder::new()
            .recursive(true)
            .create(Path::new(&config.path).join(name)).unwrap();

        info!("created new database: {}", d.name);
        Ok(d)
    }

    pub fn load(name: &str, config: DatabaseConfig) -> Result<Database, Error>{
        if try!(metadata(Path::new(&config.path).join(name))).is_dir() {
            info!("loaded database: {}", name.to_string());
            Ok(Database { name: name.to_string(), config:  config })
        } else {
            warn!("could not load database: {} at: {}", name, &config.path);
            Err(Error::LoadDatabase)
        }
    }

    pub fn get_name(&self) -> String {
        self.name.clone()
    }

    pub fn get_path(&self) -> String {
        self.config.path.clone()
    }
}

#[cfg(test)]
mod test {
    use super::Database;
    use std::fs::{File, metadata};
    use std::path::Path;
    use super::super::DatabaseConfig;

    #[test]
    fn create_db() {
        let path = "/tmp/test1/";
        let name = "test_db";
        let mut db = Database::create(&name, DatabaseConfig::new(&path));

        assert!(metadata(&path).is_ok());
        assert!(metadata(&path).unwrap().is_dir());
    }

    #[test]
    fn load_db() {
        let path = "/tmp/test1/";
        let name = "test_db2";
        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();

        let db2 = Database::load(&name, DatabaseConfig::new(&path)).unwrap();

        assert_eq!(db.get_name(), db2.get_name());
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    pub owning_module_name  : &'static str,
    pub header              : FileHeaderData,
    pub data                : String,
    pub id                  : FileID,
}

impl File {

    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    pub fn data(&self) -> &String {
        &self.data
    }

    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> &FileID {
        &self.id
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use super::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<commit_msg>Add File::set_header()<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use regex::Regex;

pub mod id;
pub mod id_type;
pub mod header;
pub mod hash;

use module::Module;
use storage::file::id::*;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use super::parser::{FileHeaderParser, Parser, ParserError};

use self::header::spec::*;
use self::header::data::*;

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File {
    pub owning_module_name  : &'static str,
    pub header              : FileHeaderData,
    pub data                : String,
    pub id                  : FileID,
}

impl File {

    pub fn owner_name(&self) -> &'static str {
        self.owning_module_name
    }

    pub fn header(&self) -> &FileHeaderData {
        &self.header
    }

    pub fn set_header(&mut self, new_header: FileHeaderData) {
        self.header = new_header;
    }

    pub fn data(&self) -> &String {
        &self.data
    }

    pub fn contents(&self) -> (&FileHeaderData, &String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> &FileID {
        &self.id
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

}

impl Display for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl Debug for File {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module_name,
               self.id,
               self.header,
               self.data);
        Ok(())
    }

}

#[cfg(test)]
mod test {
    // we use the JSON parser here, so we can generate FileHeaderData
    use storage::json::parser::JsonHeaderParser;
    use super::match_header_spec;
    use storage::parser::{FileHeaderParser, ParserError};
    use storage::file::FileHeaderData as FHD;
    use storage::file::FileHeaderSpec as FHS;

    #[test]
    fn test_spec_matching() {
        let text = String::from("{\"a\": 1, \"b\": -2}");
        let spec = FHS::Map {
            keys: vec![
                FHS::Key {
                    name: String::from("a"),
                    value_type: Box::new(FHS::UInteger)
                },
                FHS::Key {
                    name: String::from("b"),
                    value_type: Box::new(FHS::Integer)
                }
            ]
        };

        let parser  = JsonHeaderParser::new(Some(spec.clone()));
        let datares = parser.read(Some(text.clone()));
        assert!(datares.is_ok(), "Text could not be parsed: '{}'", text);
        let data = datares.unwrap();

        let matchres = match_header_spec(&spec, &data);
        assert!(matchres.is_none(), "Matching returns error: {:?}", matchres);
    }
}

<|endoftext|>


<commit_before>use tokio_core::reactor::{Core, Handle};
use hyper::Client;
use hyper_tls::HttpsConnector;
use endpoint::HnNewsEndpoint;
use slog;
use slog_term;
use slog_stream;
use slog::{Level, LevelFilter, DrainExt};
use std::fs::OpenOptions;
use std::io;

///
/// 'AppDomain' struct which have relevant parts which are use as core elements of the application
///
pub struct AppDomain {
    pub core: Core,
    pub endpoint: HnNewsEndpoint,
    pub client: Client<HttpsConnector>,
    pub logger: slog::Logger,
}

struct AppLogFormat;

impl slog_stream::Format for AppLogFormat {
    fn format(&self,
              io: &mut io::Write,
              rinfo: &slog::Record,
              _logger_values: &slog::OwnedKeyValueList)
              -> io::Result<()> {
        let msg = format!("{} - {}\n", rinfo.level(), rinfo.msg());
        let _ = try!(io.write_all(msg.as_bytes()));
        Ok(())
    }
}

fn configure_client(handle: &Handle) -> Client<HttpsConnector> {
    Client::configure()
            // Does not check the validity of certificate
            .connector(HttpsConnector::new(4, &handle))
            .build(&handle)
}

pub fn create_app_domain() -> AppDomain {
    let logger = create_loggers();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = configure_client(&handle);
    let endpoint = HnNewsEndpoint::build_default();
    let mut app_domain = AppDomain {
        core: core,
        endpoint: endpoint,
        client: client,
        logger: logger,
    };
    app_domain
}
fn create_loggers() -> slog::Logger {
        let file = OpenOptions::new()
        .read(false)
        .write(true)
        .append(true)
        .create(true)
        .open("app.log").unwrap();
    let file_drain = slog_stream::stream(file, AppLogFormat);
    let std_out_drain = slog_term::streamer().build();
    // let logger = slog::Logger::root(slog::duplicate(console_drain, file_drain).fuse(), o!());
    let logger = slog::Logger::root(
        slog::Duplicate::new(
            LevelFilter::new(file_drain, Level::Info),
            LevelFilter::new(std_out_drain, Level::Warning)
        ).fuse(), o!());
    logger
}<commit_msg>Added state machines and modified logging format<commit_after>use tokio_core::reactor::{Core, Handle};
use hyper::Client;
use hyper_tls::HttpsConnector;
use endpoint::HnNewsEndpoint;
use slog;
use slog_term;
use slog_stream;
use slog::{Level, LevelFilter, DrainExt};
use std::fs::OpenOptions;
use std::io;

///
/// 'AppDomain' struct which have relevant parts which are use as core elements of the application
///
pub struct AppDomain {
    pub core: Core,
    pub endpoint: HnNewsEndpoint,
    pub client: Client<HttpsConnector>,
    pub logger: slog::Logger,
}

enum AppStates {
    WaitingUserInput,
    RetrievingResults,
    DoingLocalWork,
    Idle,
}

struct AppStateMachine {
    pub viewing_top_stories:bool,
    pub viewing_comments_for_a_story:bool,
    pub connection_working:bool,
    pub top_story_page_index:i32,
    pub comments_page_index:i32,
    pub current_state: AppStates,
}

struct AppLogFormat;

impl slog_stream::Format for AppLogFormat {
    fn format(&self,
              io: &mut io::Write,
              rinfo: &slog::Record,
              _logger_values: &slog::OwnedKeyValueList)
              -> io::Result<()> {
        let msg = format!("{} {} from line {} in {}\n", rinfo.level(), rinfo.msg(), rinfo.line(), rinfo.file());
        let _ = try!(io.write_all(msg.as_bytes()));
        Ok(())
    }
}

fn configure_client(handle: &Handle) -> Client<HttpsConnector> {
    Client::configure()
            // Does not check the validity of certificate
            .connector(HttpsConnector::new(4, &handle))
            .build(&handle)
}

pub fn create_app_domain() -> AppDomain {
    let logger = create_loggers();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = configure_client(&handle);
    let endpoint = HnNewsEndpoint::build_default();
    let mut app_domain = AppDomain {
        core: core,
        endpoint: endpoint,
        client: client,
        logger: logger,
    };
    app_domain
}
fn create_loggers() -> slog::Logger {
        let file = OpenOptions::new()
        .read(false)
        .write(true)
        .append(true)
        .create(true)
        .open("app.log").unwrap();
    let file_drain = slog_stream::stream(file, AppLogFormat);
    let std_out_drain = slog_term::streamer().build();
    // let logger = slog::Logger::root(slog::duplicate(console_drain, file_drain).fuse(), o!());
    let logger = slog::Logger::root(
        slog::Duplicate::new(
            LevelFilter::new(file_drain, Level::Info),
            LevelFilter::new(std_out_drain, Level::Warning)
        ).fuse(), o!());
    logger
}<|endoftext|>


<commit_before>
use Renderable;
use context::Context;
use token::Token;
use LiquidOptions;
use template::Template;
use parser;
use lexer;
use error::{Result, Error};

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

struct Include {
    partial: Template,
}

impl Renderable for Include {
    fn render(&self, mut context: &mut Context) -> Result<Option<String>> {
        self.partial.render(&mut context)
    }
}

fn parse_partial<P: AsRef<Path>>(path: P, options: &LiquidOptions) -> Result<Template> {
    let file_system = &options.file_system.clone().unwrap_or(PathBuf::new());
    let path = file_system.join(path);

    // check if file exists
    if !path.exists() {
        return Err(Error::from(&*format!("{:?} does not exist", path)));
    }

    let mut file = try!(File::open(path));

    let mut content = String::new();
    try!(file.read_to_string(&mut content));

    let tokens = try!(lexer::tokenize(&content));
    parser::parse(&tokens, &options).map(Template::new)
}

pub fn include_tag(_tag_name: &str,
                   arguments: &[Token],
                   options: &LiquidOptions)
                   -> Result<Box<Renderable>> {
    let mut args = arguments.iter();

    let path = match args.next() {
        Some(&Token::Identifier(ref path)) => path,
        arg => return Error::parser("Identifier", arg),
    };

    Ok(Box::new(Include { partial: try!(parse_partial(&path, &options)) }))
}

#[cfg(test)]
mod test {
    use context::Context;
    use Renderable;
    use parse;
    use error::Error;
    use LiquidOptions;
    use std::path::PathBuf;

    fn options() -> LiquidOptions {
        LiquidOptions {
            file_system: Some(PathBuf::from("tests/fixtures/input")),
            ..Default::default()
        }
    }

    #[test]
    fn include_tag() {
        let text = "{% include example.txt %}";
        let template = parse(text, options()).unwrap();

        let mut context = Context::new();
        assert_eq!(template.render(&mut context).unwrap(),
                   Some("5 wot wot\n".to_owned()));
    }

    #[test]
    fn no_file() {
        let text = "{% include file_does_not_exist.liquid %}";
        let output = parse(text, options());

        assert!(output.is_err());
        if let Err(Error::Other(val)) = output {
            assert_eq!(format!("{}", val),
                       "\"tests/fixtures/input/file_does_not_exist.liquid\" does not exist"
                           .to_owned());
        } else {
            assert!(false);
        }
    }
}
<commit_msg>small format fix<commit_after>
use Renderable;
use context::Context;
use token::Token;
use LiquidOptions;
use template::Template;
use parser;
use lexer;
use error::{Result, Error};

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

struct Include {
    partial: Template,
}

impl Renderable for Include {
    fn render(&self, mut context: &mut Context) -> Result<Option<String>> {
        self.partial.render(&mut context)
    }
}

fn parse_partial<P: AsRef<Path>>(path: P, options: &LiquidOptions) -> Result<Template> {
    let file_system = options.file_system.clone().unwrap_or(PathBuf::new());
    let path = file_system.join(path);

    // check if file exists
    if !path.exists() {
        return Err(Error::from(&*format!("{:?} does not exist", path)));
    }

    let mut file = try!(File::open(path));

    let mut content = String::new();
    try!(file.read_to_string(&mut content));

    let tokens = try!(lexer::tokenize(&content));
    parser::parse(&tokens, &options).map(Template::new)
}

pub fn include_tag(_tag_name: &str,
                   arguments: &[Token],
                   options: &LiquidOptions)
                   -> Result<Box<Renderable>> {
    let mut args = arguments.iter();

    let path = match args.next() {
        Some(&Token::Identifier(ref path)) => path,
        arg => return Error::parser("Identifier", arg),
    };

    Ok(Box::new(Include { partial: try!(parse_partial(&path, &options)) }))
}

#[cfg(test)]
mod test {
    use context::Context;
    use Renderable;
    use parse;
    use error::Error;
    use LiquidOptions;
    use std::path::PathBuf;

    fn options() -> LiquidOptions {
        LiquidOptions {
            file_system: Some(PathBuf::from("tests/fixtures/input")),
            ..Default::default()
        }
    }

    #[test]
    fn include_tag() {
        let text = "{% include example.txt %}";
        let template = parse(text, options()).unwrap();

        let mut context = Context::new();
        assert_eq!(template.render(&mut context).unwrap(),
                   Some("5 wot wot\n".to_owned()));
    }

    #[test]
    fn no_file() {
        let text = "{% include file_does_not_exist.liquid %}";
        let output = parse(text, options());

        assert!(output.is_err());
        if let Err(Error::Other(val)) = output {
            assert_eq!(format!("{}", val),
                       "\"tests/fixtures/input/file_does_not_exist.liquid\" does not exist"
                           .to_owned());
        } else {
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>use super::*;

impl Editor {
    /// Goto a given position
    #[inline]
    pub fn goto(&mut self, (x, y): (usize, usize)) {
        self.cursor_mut().x = x;
        self.cursor_mut().y = y;
    }

    /// Get the position of the next char
    #[inline]
    pub fn next_pos(&self) -> (usize, usize) {
        // TODO: Add numerals
        if self.x() == self.text[self.y()].len() {
            if self.y() < self.text.len() - 1 {
                (0, self.y() + 1)
            } else {
                (self.x(), self.y())
            }
        } else {
            (self.x() + 1, self.y())
        }
    }

    /// Get the position of previous char
    #[inline]
    pub fn previous_pos(&self) -> (usize, usize) {
        if self.x() == 0 {
            if self.y() > 0 {
                (self.text[self.y() - 1].len(), self.y() - 1)
            } else {
                (self.x(), self.y())
            }
        } else {
            (self.x() - 1, self.y())
        }
    }

    /// Goto the next char
    #[inline]
    pub fn goto_next(&mut self) {
        let p = self.next_pos();
        self.goto(p);
    }
    /// Goto the previous char
    #[inline]
    pub fn goto_previous(&mut self) {
        let p = self.previous_pos();
        self.goto(p);
    }

    /// Get the position of the right char
    #[inline]
    pub fn right_pos(&self, n: usize) -> (usize, usize) {
        let x = self.x() + n;

        if x > self.text[self.y()].len() {
            (self.text[self.y()].len(), self.y())
        } else {
            (x, self.y())
        }
    }
    /// Goto the right char
    #[inline]
    pub fn goto_right(&mut self, n: usize) {
        self.cursor_mut().x = self.right_pos(n).0;
    }

    /// Get the position of the left char
    #[inline]
    pub fn left_pos(&self, n: usize) -> (usize, usize) {
        if n <= self.x() {
            (self.x() - n, self.y())
        } else {
            (0, self.y())
        }
    }
    /// Goto the left char
    #[inline]
    pub fn goto_left(&mut self, n: usize) {
        self.cursor_mut().x = self.left_pos(n).0;
    }

    /// Get the position of the char above the cursor
    #[inline]
    pub fn up_pos(&self, n: usize) -> (usize, usize) {
        if n <= self.y() {
            (self.cursor().x, self.y() - n)
        } else {
            (self.cursor().x, 0)
        }
    }
    /// Go a char up
    #[inline]
    pub fn goto_up(&mut self, n: usize) {
        let p = self.up_pos(n);
        self.goto(p);
    }

    /// Get the position under the char
    #[inline]
    pub fn down_pos(&self, n: usize) -> (usize, usize) {
        let y = self.y() + n;

        if y >= self.text.len() {
            (self.cursor().x, self.text.len() - 1)
        } else {
            (self.cursor().x, y)
        }
    }

    /// Go down
    #[inline]
    pub fn goto_down(&mut self, n: usize) {
        let p = self.down_pos(n);
        self.goto(p);
    }

    /// Get the position of the end of the line
    #[inline]
    pub fn ln_end_pos(&self) -> (usize, usize) {
        (self.text[self.y()].len(), self.y())
    }

    /// Goto line end
    #[inline]
    pub fn goto_ln_end(&mut self) {
        let p = self.ln_end_pos();
        self.goto(p);
    }
}
<commit_msg>Simplify and refactor<commit_after>use super::*;

impl Editor {
    /// Goto a given position
    #[inline]
    pub fn goto(&mut self, (x, y): (usize, usize)) {
        self.cursor_mut().x = x;
        self.cursor_mut().y = y;
    }

    /// Get the position of the next char
    #[inline]
    pub fn next_pos(&self) -> (usize, usize) {
        // TODO: Add numerals
        if self.x() == self.text[self.y()].len() {
            if self.y() < self.text.len() - 1 {
                (0, self.y() + 1)
            } else {
                (self.x(), self.y())
            }
        } else {
            (self.x() + 1, self.y())
        }
    }

    /// Get the position of previous char
    #[inline]
    pub fn previous_pos(&self) -> (usize, usize) {
        if self.x() == 0 {
            if self.y() > 0 {
                (self.text[self.y() - 1].len(), self.y() - 1)
            } else {
                (self.x(), self.y())
            }
        } else {
            (self.x() - 1, self.y())
        }
    }

    /// Goto the next char
    #[inline]
    pub fn goto_next(&mut self) {
        let p = self.next_pos();
        self.goto(p);
    }
    /// Goto the previous char
    #[inline]
    pub fn goto_previous(&mut self) {
        let p = self.previous_pos();
        self.goto(p);
    }

    /// Get the position of the right char
    #[inline]
    pub fn right_pos(&self, n: usize) -> (usize, usize) {
        (self.x() + n, self.y())
    }
    /// Goto the right char
    #[inline]
    pub fn goto_right(&mut self, n: usize) {
        self.cursor_mut().x = self.right_pos(n).0;
    }

    /// Get the position of the left char
    #[inline]
    pub fn left_pos(&self, n: usize) -> (usize, usize) {
        if n <= self.x() {
            (self.x() - n, self.y())
        } else {
            (0, self.y())
        }
    }
    /// Goto the left char
    #[inline]
    pub fn goto_left(&mut self, n: usize) {
        self.cursor_mut().x = self.left_pos(n).0;
    }

    /// Get the position of the char above the cursor
    #[inline]
    pub fn up_pos(&self, n: usize) -> (usize, usize) {
        if n <= self.y() {
            (self.cursor().x, self.y() - n)
        } else {
            (self.cursor().x, 0)
        }
    }
    /// Go a char up
    #[inline]
    pub fn goto_up(&mut self, n: usize) {
        let p = self.up_pos(n);
        self.goto(p);
    }

    /// Get the position under the char
    #[inline]
    pub fn down_pos(&self, n: usize) -> (usize, usize) {
        (self.cursor().x, self.y() + n)
    }

    /// Go down
    #[inline]
    pub fn goto_down(&mut self, n: usize) {
        let p = self.down_pos(n);
        self.goto(p);
    }

    /// Get the position of the end of the line
    #[inline]
    pub fn ln_end_pos(&self) -> (usize, usize) {
        (self.text[self.y()].len(), self.y())
    }

    /// Goto line end
    #[inline]
    pub fn goto_ln_end(&mut self) {
        let p = self.ln_end_pos();
        self.goto(p);
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate rand;
extern crate libshft;

mod output;

use clap::{Arg, App, SubCommand};
use rand::SeedableRng;
use rand::isaac;
use std::io;
use std::io::{Read, Write};
use std::fs::File;
use std::path::Path;
use std::process;
use libshft::grammar::Grammar;
use libshft::parse::{ParsedFile, slurp};
use libshft::fuzz::fuzz_one;
use output::OutputPattern;

#[cfg(test)]
mod test {
    use libshft::grammar::{Grammar, GrammarDef};
    use libshft::parse::slurp;
    use libshft::fuzz::FuzzFile;

    fn roundtrip(grammar: &Grammar, buf: &[u8]) -> bool {
        let parsed_file = slurp(grammar, buf);
        println!("parsed = {:?}", parsed_file.dump());
        let ff = FuzzFile::new(&parsed_file);
        let serialized = ff.serialize();
        println!("serialized = {:#?}", serialized);
        serialized == buf
    }

    #[test]
    fn test_whitespace() {
        let grammar = Grammar::new(vec![], vec![b" ".to_vec()]);
        let buf = b"1 2 3";
        assert!(roundtrip(&grammar, buf))
    }

    #[test]
    fn test_two_whitespaces() {
        let grammar = Grammar::new(vec![], vec![b" ".to_vec(), b"\r\n".to_vec()]);
        let buf = b"1 2\r\n3 \r\n4";
        assert!(roundtrip(&grammar, buf))
    }

    #[test]
    fn test_delim() {
        let grammar = Grammar::new(vec![
            GrammarDef::Delim(vec![b'<', b'<'], vec![b'>', b'>']),
        ], vec![]);
        assert!(roundtrip(&grammar, b"1<<2<<3>>4>>5"));
        assert!(roundtrip(&grammar, b"1<<2<<3>>4"));
        assert!(roundtrip(&grammar, b"1<<2>>3>>4"));
        assert!(roundtrip(&grammar, b"1<<2"));
        assert!(roundtrip(&grammar, b"1>>2"));
    }
}

fn read_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
    let mut f = File::open(path)?;
    let mut buffer = Vec::new();

    match f.read_to_end(&mut buffer) {
        Ok(_) => Ok(buffer),
        Err(err) => Err(err),
    }
}

fn do_fuzz<'buf>(parsed_file: &ParsedFile<'buf>, pattern: &OutputPattern, iterations: usize) {
    let mut rng = isaac::Isaac64Rng::from_seed(&[1, 2, 3, 4]);
    for i in 0..iterations {
        let fuzzed = fuzz_one(parsed_file, &mut rng, 5);

        let out_filename = pattern.with(i);
        let mut file = File::create(out_filename).expect("oops");
        file.write_all(&fuzzed[..]).expect("oops")
    }
}

fn parse_input_file<'buf>(config_filename: &str, buf: &'buf [u8]) -> ParsedFile<'buf> {
    let grammar = Grammar::from_path(config_filename);
    slurp(&grammar, buf)
}

fn die<S: AsRef<str>>(app: &App, msg: S) -> i32 {
    let mut out = io::stdout();
    app.write_help(&mut out).expect("app.write_help");
    write!(out, "\n\n{}\n", msg.as_ref()).expect("write newline");
    1
}

fn go() -> i32 {
    let app = App::new("super-happy-fuzz-time")
        .arg(Arg::with_name("INPUT")
            .help("File to fuzz")
            .long("input")
            .short("i")
            .number_of_values(1)
            .required(true))
        .arg(Arg::with_name("CONFIG")
            .help("Config file")
            .long("config")
            .short("c")
            .number_of_values(1)
            .required(true))
        .subcommand(
            SubCommand::with_name("dump")
                .help("Parse and dump input file"))
        .subcommand(
            SubCommand::with_name("fuzz")
                .help("Fuzz input file")
                .arg(Arg::with_name("OUTPUT")
                    .help("Output pattern")
                    .long("output")
                    .short("o")
                    .number_of_values(1)
                    .required(true)));

    let matches = app.clone().get_matches();

    let config_filename = matches.value_of("CONFIG").expect("impossible");
    let input_filename = matches.value_of("INPUT").expect("impossible");

    match matches.subcommand() {
        ("dump", _) => {
            let buf = read_file(input_filename).expect("read_file");
            let parsed_file = parse_input_file(config_filename, &buf[..]);
            println!("{}", parsed_file.dump());
            0
        },
        ("fuzz", Some(fuzz_matches)) => {
            let output = fuzz_matches.value_of("OUTPUT").expect("impossible");
            match OutputPattern::from_path(output) {
                Some(pattern) => {
                    let buf = read_file(input_filename).expect("read_file");
                    let parsed_file = parse_input_file(config_filename, &buf[..]);
                    do_fuzz(&parsed_file, &pattern, 10);
                    0
                },
                None => {
                    die(&app, format!("'{}' is an invalid output pattern", output))
                }
            }
        },
        _ => {
            die(&app, "Must provide 'dump' or 'fuzz'")
        },
    }
}

fn main() {
    process::exit(go());
}
<commit_msg>Breakout lookup logic.<commit_after>extern crate clap;
extern crate rand;
extern crate libshft;

mod output;

use clap::{Arg, ArgMatches, App, SubCommand};
use rand::SeedableRng;
use rand::isaac;
use std::io;
use std::io::{Read, Write};
use std::fs::File;
use std::path::Path;
use std::process;
use libshft::grammar::Grammar;
use libshft::parse::{ParsedFile, slurp};
use libshft::fuzz::fuzz_one;
use output::OutputPattern;

#[cfg(test)]
mod test {
    use libshft::grammar::{Grammar, GrammarDef};
    use libshft::parse::slurp;
    use libshft::fuzz::FuzzFile;

    fn roundtrip(grammar: &Grammar, buf: &[u8]) -> bool {
        let parsed_file = slurp(grammar, buf);
        println!("parsed = {:?}", parsed_file.dump());
        let ff = FuzzFile::new(&parsed_file);
        let serialized = ff.serialize();
        println!("serialized = {:#?}", serialized);
        serialized == buf
    }

    #[test]
    fn test_whitespace() {
        let grammar = Grammar::new(vec![], vec![b" ".to_vec()]);
        let buf = b"1 2 3";
        assert!(roundtrip(&grammar, buf))
    }

    #[test]
    fn test_two_whitespaces() {
        let grammar = Grammar::new(vec![], vec![b" ".to_vec(), b"\r\n".to_vec()]);
        let buf = b"1 2\r\n3 \r\n4";
        assert!(roundtrip(&grammar, buf))
    }

    #[test]
    fn test_delim() {
        let grammar = Grammar::new(vec![
            GrammarDef::Delim(vec![b'<', b'<'], vec![b'>', b'>']),
        ], vec![]);
        assert!(roundtrip(&grammar, b"1<<2<<3>>4>>5"));
        assert!(roundtrip(&grammar, b"1<<2<<3>>4"));
        assert!(roundtrip(&grammar, b"1<<2>>3>>4"));
        assert!(roundtrip(&grammar, b"1<<2"));
        assert!(roundtrip(&grammar, b"1>>2"));
    }
}

fn read_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
    let mut f = File::open(path)?;
    let mut buffer = Vec::new();

    match f.read_to_end(&mut buffer) {
        Ok(_) => Ok(buffer),
        Err(err) => Err(err),
    }
}

fn do_fuzz<'buf>(parsed_file: &ParsedFile<'buf>, pattern: &OutputPattern, iterations: usize) {
    let mut rng = isaac::Isaac64Rng::from_seed(&[1, 2, 3, 4]);
    for i in 0..iterations {
        let fuzzed = fuzz_one(parsed_file, &mut rng, 5);

        let out_filename = pattern.with(i);
        let mut file = File::create(out_filename).expect("oops");
        file.write_all(&fuzzed[..]).expect("oops")
    }
}

fn parse_input_file<'buf>(config_filename: &str, buf: &'buf [u8]) -> ParsedFile<'buf> {
    let grammar = Grammar::from_path(config_filename);
    slurp(&grammar, buf)
}

fn die<S: AsRef<str>>(app: &App, msg: S) -> i32 {
    let mut out = io::stdout();
    app.write_help(&mut out).expect("app.write_help");
    write!(out, "\n\n{}\n", msg.as_ref()).expect("write newline");
    1
}

fn lookup<'a>(matches: &'a ArgMatches, key: &str) -> &'a str {
    matches.value_of(key).expect("impossible")
}

fn go() -> i32 {
    let app = App::new("super-happy-fuzz-time")
        .arg(Arg::with_name("INPUT")
            .help("File to fuzz")
            .long("input")
            .short("i")
            .number_of_values(1)
            .required(true))
        .arg(Arg::with_name("CONFIG")
            .help("Config file")
            .long("config")
            .short("c")
            .number_of_values(1)
            .required(true))
        .subcommand(
            SubCommand::with_name("dump")
                .help("Parse and dump input file"))
        .subcommand(
            SubCommand::with_name("fuzz")
                .help("Fuzz input file")
                .arg(Arg::with_name("OUTPUT")
                    .help("Output pattern")
                    .long("output")
                    .short("o")
                    .number_of_values(1)
                    .required(true)));

    let matches = app.clone().get_matches();

    let config_filename = lookup(&matches, "CONFIG");
    let input_filename = lookup(&matches, "INPUT");

    match matches.subcommand() {
        ("dump", _) => {
            let buf = read_file(input_filename).expect("read_file");
            let parsed_file = parse_input_file(config_filename, &buf[..]);
            println!("{}", parsed_file.dump());
            0
        },
        ("fuzz", Some(fuzz_matches)) => {
            let output = lookup(fuzz_matches, "OUTPUT");
            match OutputPattern::from_path(output) {
                Some(pattern) => {
                    let buf = read_file(input_filename).expect("read_file");
                    let parsed_file = parse_input_file(config_filename, &buf[..]);
                    do_fuzz(&parsed_file, &pattern, 10);
                    0
                },
                None => {
                    die(&app, format!("'{}' is an invalid output pattern", output))
                }
            }
        },
        _ => {
            die(&app, "Must provide 'dump' or 'fuzz'")
        },
    }
}

fn main() {
    process::exit(go());
}
<|endoftext|>


<commit_before>extern mod cairo;

use azure_hl::{BackendType, CairoBackend, CoreGraphicsAcceleratedBackend, CoreGraphicsBackend};
use azure_hl::{Direct2DBackend, NoBackend, RecordingBackend, SkiaBackend};
use bindgen::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};
use cairo::{cairo_font_face_t, cairo_matrix_t, cairo_scaled_font_t};
use cairo::bindgen::{cairo_font_face_destroy, cairo_font_options_create};
use cairo::bindgen::{cairo_font_options_destroy, cairo_matrix_init_identity, cairo_matrix_scale};
use cairo::bindgen::{cairo_scaled_font_create};

#[cfg(target_os="macos")]
priv mod macos {
    extern mod core_graphics;
    extern mod core_text;
    use core_graphics::font::CGFontRef;
    use core_text::font::{CTFont, CTFontRef};
    use cairo::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;

    type NativeFont = CTFont;
}
#[cfg(target_os="macos")]
priv use macos::*;

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,

    drop {
        AzReleaseScaledFont(self.azure_scaled_font);
    }
}

impl ScaledFont {
    static priv fn create_cairo_font(face: *cairo_font_face_t, size: AzFloat)
                                  -> *cairo_scaled_font_t {
        // FIXME: error handling

        let idmatrix: cairo_matrix_t = {
            xx: 0 as c_double,
            yx: 0 as c_double,
            xy: 0 as c_double,
            yy: 0 as c_double,
            x0: 0 as c_double,
            y0: 0 as c_double
        };
        cairo_matrix_init_identity(ptr::to_unsafe_ptr(&idmatrix));

        let fontmatrix = idmatrix;
        cairo_matrix_scale(ptr::to_unsafe_ptr(&fontmatrix), size as c_double, size as c_double);
        let options = cairo_font_options_create();
        let cfont = cairo_scaled_font_create(face,
                                             ptr::to_unsafe_ptr(&fontmatrix),
                                             ptr::to_unsafe_ptr(&idmatrix),
                                             options);
        cairo_font_options_destroy(options);

        return cfont;
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    static pub fn new(backend: BackendType, native_font: &const CTFont, size: AzFloat)
                   -> ScaledFont {
        let mut azure_native_font = {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;

                unsafe {
                    azure_native_font.mFont = cast::transmute(native_font.copy_to_CGFont());
                }
            }
            CairoBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_CAIRO_FONT_FACE;

                // TODO(Issue #184): may want to just reuse CGFont stored in servo's QuartzFontHandle.
                // This would require creating some trait like CGFontProvider with .get_CGFont().
                let cg_font = native_font.copy_to_CGFont();
                let face = cairo_quartz_font_face_create_for_cgfont(cg_font);
                if face == ptr::null() { fail; }

                let cairo_font = ScaledFont::create_cairo_font(face, size);
                cairo_font_face_destroy(face);

                unsafe {
                    azure_native_font.mFont = cast::transmute(cairo_font);
                }
            }
            NoBackend | Direct2DBackend | RecordingBackend => {
                fail ~"don't know how to make a scaled font for this backend";
            }
        }

        let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
        let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
        ScaledFont { azure_scaled_font: move azure_scaled_font }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[link_args="-lskia"]
extern {
    #[cfg(target_os="macos")]
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<commit_msg>Add ScaledFont::new() for FreeType/Cairo backend combination.<commit_after>extern mod cairo;

use azure_hl::{BackendType, CairoBackend, CoreGraphicsAcceleratedBackend, CoreGraphicsBackend};
use azure_hl::{Direct2DBackend, NoBackend, RecordingBackend, SkiaBackend};
use bindgen::{AzCreateScaledFontForNativeFont, AzReleaseScaledFont};
use cairo::{cairo_font_face_t, cairo_matrix_t, cairo_scaled_font_t};
use cairo::bindgen::{cairo_font_face_destroy, cairo_font_options_create};
use cairo::bindgen::{cairo_font_options_destroy, cairo_matrix_init_identity, cairo_matrix_scale};
use cairo::bindgen::{cairo_scaled_font_create};

#[cfg(target_os="macos")]
priv mod macos {
    extern mod core_graphics;
    extern mod core_text;
    use core_graphics::font::CGFontRef;
    use core_text::font::{CTFont, CTFontRef};
    use cairo::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;

    type NativeFont = CTFont;
}
#[cfg(target_os="macos")]
priv use macos::*;

#[cfg(target_os="linux")]
priv mod linux {
    extern mod freetype;
    use cairo::cairo_ft::bindgen::cairo_ft_font_face_create_for_ft_face;
    use freetype::freetype::{FT_Face, FT_LOAD_DEFAULT};
}
#[cfg(target_os="linux")]
priv use linux::*;

type SkTypeface = *c_void;

pub struct ScaledFont {
    azure_scaled_font: AzScaledFontRef,

    drop {
        AzReleaseScaledFont(self.azure_scaled_font);
    }
}

impl ScaledFont {
    static priv fn create_cairo_font(face: *cairo_font_face_t, size: AzFloat)
                                  -> *cairo_scaled_font_t {
        // FIXME: error handling

        let idmatrix: cairo_matrix_t = {
            xx: 0 as c_double,
            yx: 0 as c_double,
            xy: 0 as c_double,
            yy: 0 as c_double,
            x0: 0 as c_double,
            y0: 0 as c_double
        };
        cairo_matrix_init_identity(ptr::to_unsafe_ptr(&idmatrix));

        let fontmatrix = idmatrix;
        cairo_matrix_scale(ptr::to_unsafe_ptr(&fontmatrix), size as c_double, size as c_double);
        let options = cairo_font_options_create();
        let cfont = cairo_scaled_font_create(face,
                                             ptr::to_unsafe_ptr(&fontmatrix),
                                             ptr::to_unsafe_ptr(&idmatrix),
                                             options);
        cairo_font_options_destroy(options);

        return cfont;
    }

    #[cfg(target_os="linux")]
    static pub fn new(backend: BackendType, native_font: FT_Face, size: AzFloat)
        -> ScaledFont {
        let mut azure_native_font = {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CairoBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_CAIRO_FONT_FACE;

                unsafe {
                    let cairo_face = cairo_ft_font_face_create_for_ft_face(native_font, FT_LOAD_DEFAULT as c_int);
                    if cairo_face.is_null() { fail; }

                    let cairo_font = ScaledFont::create_cairo_font(cairo_face, size);
                    cairo_font_face_destroy(cairo_face);

                    unsafe {
                        azure_native_font.mFont = cast::transmute(cairo_font);
                    }
                }
            },
            _ => { fail ~"don't know how to make a scaled font for this backend"; }
        }

        let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
        let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
        ScaledFont { azure_scaled_font: move azure_scaled_font }
    }

    /// Mac-specific function to create a font for the given backend.
    #[cfg(target_os="macos")]
    static pub fn new(backend: BackendType, native_font: &const CTFont, size: AzFloat)
                   -> ScaledFont {
        let mut azure_native_font = {
            mType: 0,
            mFont: ptr::null()
        };

        match backend {
            CoreGraphicsBackend | CoreGraphicsAcceleratedBackend | SkiaBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_MAC_FONT_FACE;

                unsafe {
                    azure_native_font.mFont = cast::transmute(native_font.copy_to_CGFont());
                }
            }
            CairoBackend => {
                azure_native_font.mType = AZ_NATIVE_FONT_CAIRO_FONT_FACE;

                // TODO(Issue #184): may want to just reuse CGFont stored in servo's QuartzFontHandle.
                // This would require creating some trait like CGFontProvider with .get_CGFont().
                let cg_font = native_font.copy_to_CGFont();
                let face = cairo_quartz_font_face_create_for_cgfont(cg_font);
                if face == ptr::null() { fail; }

                let cairo_font = ScaledFont::create_cairo_font(face, size);
                cairo_font_face_destroy(face);

                unsafe {
                    azure_native_font.mFont = cast::transmute(cairo_font);
                }
            }
            NoBackend | Direct2DBackend | RecordingBackend => {
                fail ~"don't know how to make a scaled font for this backend";
            }
        }

        let azure_native_font_ptr = ptr::to_unsafe_ptr(&azure_native_font);
        let azure_scaled_font = AzCreateScaledFontForNativeFont(azure_native_font_ptr, size);
        ScaledFont { azure_scaled_font: move azure_scaled_font }
    }
}

// FIXME: Move this stuff to a rust-skia?
// FIXME: Demangle the names!!!
#[link_args="-lskia"]
extern {
    #[cfg(target_os="macos")]
    pub fn _Z26SkCreateTypefaceFromCTFontPK8__CTFont(font: CTFontRef) -> *SkTypeface;
}

<|endoftext|>


<commit_before>// This code is heavily inspired by hoedown.

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;

/// Returns true if the line is a horizontal rule.
fn is_hrule_old(buf: &[u8]) -> bool {
    let len = buf.len();

    if len < 3 { return false }

    let mut pos: uint = 0;

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf[0] == SP {
        pos += 1;
        if buf[1] == SP {
            pos += 1;
            if buf[2] == SP {
                pos += 1;
            }
        }
    }

    // We need at least 3 items
    if pos + 2 >= len { return false }

    let item = buf[pos];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    let mut cnt: uint = 0; // The count of '*', '-' or '_'
    for &ch in buf.slice_from(pos).iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

/// Returns true if the line is a horizontal rule.
fn is_hrule(buf: &[u8]) -> bool {
    let mut buf = buf;

    if buf.len() < 3 { return false }

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    // We need at least 3 items
    if buf.len() < 3 { return false }

    let item = buf[0];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    let mut cnt: uint = 0; // The count of '*', '-' or '_'
    for &ch in buf.iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert_eq!(is_hrule(bytes!("* * *\n")), true);
    assert_eq!(is_hrule(bytes!("***\n")), true);
    assert_eq!(is_hrule(bytes!("*****\n")), true);
    assert_eq!(is_hrule(bytes!("- - -\n")), true);
    assert_eq!(is_hrule(bytes!("---------------------------------------\n")), true);

    // up to three spaces ignored
    assert_eq!(is_hrule(bytes!(" ***\n")), true);
    assert_eq!(is_hrule(bytes!("  ***\n")), true);
    assert_eq!(is_hrule(bytes!("   ***\n")), true);

    // but not four, or a tab which is equivalent to four spaces
    assert_eq!(is_hrule(bytes!("    ***\n")), false);
    assert_eq!(is_hrule(bytes!("\t***\n")), false);

    // need at least three
    assert_eq!(is_hrule(bytes!("*\n")), false);
    assert_eq!(is_hrule(bytes!("**\n")), false);
    assert_eq!(is_hrule(bytes!("* *\n")), false);
    assert_eq!(is_hrule(bytes!("   * *\n")), false);

    // Also works without newline at the end
    assert_eq!(is_hrule(bytes!("* * *")), true);

    // And underscores also supported
    assert_eq!(is_hrule(bytes!("___")), true);
    assert_eq!(is_hrule(bytes!("______________")), true);
    assert_eq!(is_hrule(bytes!(" ______________")), true);
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}

#[bench]
fn bench_is_hrule_old(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule_old(s));
}

<commit_msg>Add performance note<commit_after>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;

/// Returns true if the line is a horizontal rule.
fn is_hrule_old(buf: &[u8]) -> bool {
    let len = buf.len();

    if len < 3 { return false }

    let mut pos: uint = 0;

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf[0] == SP {
        pos += 1;
        if buf[1] == SP {
            pos += 1;
            if buf[2] == SP {
                pos += 1;
            }
        }
    }

    // We need at least 3 items
    if pos + 2 >= len { return false }

    let item = buf[pos];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    let mut cnt: uint = 0; // The count of '*', '-' or '_'
    for &ch in buf.slice_from(pos).iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

/// Returns true if the line is a horizontal rule.
fn is_hrule(buf: &[u8]) -> bool {
    let mut buf = buf;

    if buf.len() < 3 { return false }

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    // We need at least 3 items
    if buf.len() < 3 { return false }

    let item = buf[0];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return false;
    }

    let mut cnt: uint = 0; // The count of '*', '-' or '_'
    for &ch in buf.iter() {
        if ch == item {
            cnt += 1;
        } else if ch == NL {
            break;
        } else if ch != SP {
            return false;
        }
    }

    return cnt >= 3;
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert_eq!(is_hrule(bytes!("* * *\n")), true);
    assert_eq!(is_hrule(bytes!("***\n")), true);
    assert_eq!(is_hrule(bytes!("*****\n")), true);
    assert_eq!(is_hrule(bytes!("- - -\n")), true);
    assert_eq!(is_hrule(bytes!("---------------------------------------\n")), true);

    // up to three spaces ignored
    assert_eq!(is_hrule(bytes!(" ***\n")), true);
    assert_eq!(is_hrule(bytes!("  ***\n")), true);
    assert_eq!(is_hrule(bytes!("   ***\n")), true);

    // but not four, or a tab which is equivalent to four spaces
    assert_eq!(is_hrule(bytes!("    ***\n")), false);
    assert_eq!(is_hrule(bytes!("\t***\n")), false);

    // need at least three
    assert_eq!(is_hrule(bytes!("*\n")), false);
    assert_eq!(is_hrule(bytes!("**\n")), false);
    assert_eq!(is_hrule(bytes!("* *\n")), false);
    assert_eq!(is_hrule(bytes!("   * *\n")), false);

    // Also works without newline at the end
    assert_eq!(is_hrule(bytes!("* * *")), true);

    // And underscores also supported
    assert_eq!(is_hrule(bytes!("___")), true);
    assert_eq!(is_hrule(bytes!("______________")), true);
    assert_eq!(is_hrule(bytes!(" ______________")), true);
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}

#[bench]
fn bench_is_hrule_old(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule_old(s));
}

<|endoftext|>


<commit_before>extern crate robin_core;

#[macro_use]
extern crate pest;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser::{ExpressionParser, Rule};

    #[test]
    fn parse_an_empty_list() {
        parses_to!(
            parser: ExpressionParser,
            input: "()",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 2)
            ]
        )
    }

    #[test]
    fn parse_a_list_with_one_value() {
        parses_to!(
            parser: ExpressionParser,
            input: "(hello)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 7, [identifier_literal(1, 6)])
            ]
        )
    }

    #[test]
    fn parse_a_list_with_multiple_values() {
        parses_to!(
            parser: ExpressionParser,
            input: "(hello true)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 12, [identifier_literal(1, 6), boolean_literal(7, 11)])
            ]
        )
    }
}<commit_msg>Add new tests for symbol lists<commit_after>extern crate robin_core;

#[macro_use]
extern crate pest;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser::{ExpressionParser, Rule};

    #[test]
    fn parse_an_empty_list() {
        parses_to!(
            parser: ExpressionParser,
            input: "()",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 2)
            ]
        )
    }

    #[test]
    fn parse_a_list_with_one_value() {
        parses_to!(
            parser: ExpressionParser,
            input: "(hello)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 7, [identifier_literal(1, 6)])
            ]
        )
    }

    #[test]
    fn parse_a_list_with_multiple_values() {
        parses_to!(
            parser: ExpressionParser,
            input: "(hello true)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 12, [identifier_literal(1, 6), boolean_literal(7, 11)])
            ]
        )
    }

    #[test]
    fn parse_a_list_with_symbols() {
        parses_to!(
            parser: ExpressionParser,
            input: "(+)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 3, [identifier_literal(1, 2)])
            ]
        )
    }

    #[test]
    fn parse_a_list_with_multiple_symbols() {
        parses_to!(
            parser: ExpressionParser,
            input: "(+ 1 1)",
            rule: Rule::list_literal,
            tokens: [
                list_literal(0, 7,
                    [identifier_literal(1, 2), decimal_digits_literal(3, 4), decimal_digits_literal(5, 6)]
                )
            ]
        )
    }
}<|endoftext|>


<commit_before>extern crate lz4_compress as lz4;

use std::env;
use std::io::{self, Write, Read};

fn main() {
    // Get and lock stdout.
    let stdout = io::stdout();
    let mut stdout = stdout.lock();

    match &*env::args().nth(1).unwrap_or(String::new()) {
        "-c" => {
            // Read stream from stdin.
            let mut vec = Vec::new();
            io::stdin().read_to_end(&mut vec).expect("Failed to read stdin");

            // Compress it and write the result to stdout.
            stdout.write(&lz4::compress(&vec)).expect("Failed to write to stdout");
        },
        "-d" => {
            // Read stream from stdin.
            let mut vec = Vec::new();
            io::stdin().read_to_end(&mut vec).expect("Failed to read stdin");

            // Decompress the input.
            let decompressed = lz4::decompress(&vec).expect("Compressed data contains errors");

            // Write the decompressed buffer to stdout.
            stdout.write(&decompressed).expect("Failed to write to stdout");
        },
        // If no valid arguments are given, we print the help page.
        _ => {
            stdout.write(b"\
            Introduction:\n\
                lz4 - an utility to decompress or compress a raw, headerless LZ4 stream.\n\
            Usage:\n\
                lz4 [option]\n\
            Options:\n\
                -c : Compress stdin and write the result to stdout.\n\
                -d : Decompress stdin and write the result to stdout.\n\
                -h : Write this manpage to stderr.\n\
            ").expect("Failed to write to stdout");
        },
    }
}
<commit_msg>Fix bug in which errors in the command line flags weren't detected properly (LZ4 compression CLI)<commit_after>extern crate lz4_compress as lz4;

use std::{env, process};
use std::io::{self, Write, Read};

/// The help page for this command.
const HELP: &'static [u8] = br#"
Introduction:
    lz4 - an utility to decompress or compress a raw, headerless LZ4 stream.
Usage:
    lz4 [option]
Options:
    -c : Compress stdin and write the result to stdout.
    -d : Decompress stdin and write the result to stdout.
    -h : Write this manpage to stderr.
"#;

fn main() {
    let mut iter = env::args().skip(1);
    let mut flag = iter.next().unwrap_or(String::new());
    // If another argument is provided (e.g. the user passes a file name), we need to make sure we
    // issue an error properly, so we set back the flag to `""`.
    if iter.next().is_some() {
        flag = String::new();
    }

    match &*flag {
        "-c" => {
            // Read stream from stdin.
            let mut vec = Vec::new();
            io::stdin().read_to_end(&mut vec).expect("Failed to read stdin");

            // Compress it and write the result to stdout.
            io::stdout().write(&lz4::compress(&vec)).expect("Failed to write to stdout");
        },
        "-d" => {
            // Read stream from stdin.
            let mut vec = Vec::new();
            io::stdin().read_to_end(&mut vec).expect("Failed to read stdin");

            // Decompress the input.
            let decompressed = lz4::decompress(&vec).expect("Compressed data contains errors");

            // Write the decompressed buffer to stdout.
            io::stdout().write(&decompressed).expect("Failed to write to stdout");
        },
        // If no valid arguments are given, we print the help page.
        _ => {
            io::stdout().write(HELP).expect("Failed to write to stdout");

            process::exit(1);
        },
    }
}
<|endoftext|>