<commit_before>//! Handling of socket connections to screeps using ws-rs as a backend.

pub extern crate ws;
extern crate fnv;
use serde_json;

use std::time::Duration;
use std::borrow::{Borrow, Cow};
use std::str;

use self::fnv::FnvHashMap;
use self::ws::util::Token as WsToken;

pub use self::error::{Error, Result};
pub use self::parsing::{ParsedResult, ParsedMessage};
use error::{Error as HttpError, ErrorType as HttpErrorType};

use TokenStorage;
use Token;

mod error;
mod parsing;

/// Handler trait to implement for socket clients.
pub trait Handler {
    /// Run when a disconnect has occurred.
    fn on_disconnect(&mut self) -> ws::Result<()> {
        Ok(())
    }

    /// Run on any websocket error or message parsing error.
    fn on_error(&mut self, err: Error) {
        warn!("screeps socket error uncaught due to default handler method: {}",
              err);
    }

    /// Run on any messages from the server.
    fn on_message(&mut self, msg: parsing::ParsedMessage) -> ws::Result<()>;

    /// Run on any communication from the server.
    ///
    /// Default behavior is to ignore heartbeats, open and close messages, and just send any actual messages
    /// to on_message.
    fn on_communication(&mut self, result: parsing::ParsedResult) -> ws::Result<()> {
        match result {
            ParsedResult::Message(msg) => {
                debug!("screeps socket connection received single SockJS message");
                self.on_message(msg)
            }
            ParsedResult::Messages(messages) => {
                debug!("screeps socket connection received array of {} SockJS messages",
                       messages.len());
                for msg in messages {
                    self.on_message(msg)?
                }
                Ok(())
            }
            ParsedResult::Heartbeat => {
                debug!("screeps socket connection received SockJS heartbeat");
                Ok(())
            }
            ParsedResult::Open => {
                debug!("screeps socket connection received SockJS open");
                Ok(())
            }
            ParsedResult::Close { code, reason } => {
                // TODO: should we pass this on?
                debug!("screeps socket connection received SockJS close ({}, {})",
                       code,
                       reason);
                Ok(())
            }
        }
    }
}

impl<T> Handler for T
    where T: FnMut(parsing::ParsedMessage) -> ws::Result<()>
{
    fn on_message(&mut self, msg: parsing::ParsedMessage) -> ws::Result<()> {
        (self)(msg)
    }
}

enum FailState {
    Login,
}

struct ApiHandler<H: Handler, T: TokenStorage = Option<Token>> {
    token: T,
    handler: H,
    sender: Sender,
    retrying: FnvHashMap<usize, FailState>,
}

impl<H: Handler, T: TokenStorage> ApiHandler<H, T> {
    fn mark_retry(&mut self, failed: FailState, retry_in: Duration) -> ws::Result<()> {
        let mut num = 0usize;
        while self.retrying.contains_key(&num) {
            num += 1;
        }
        self.retrying.insert(num, failed);

        self.sender.sender().timeout((retry_in.subsec_nanos() as f64 / 1.0e6) as u64 + retry_in.as_secs() * 1000,
                                     WsToken(num))
    }

    fn try_or_retry_auth(&mut self) -> ws::Result<()> {
        let token = match self.token.take_token() {
            Some(t) => t,
            None => {
                self.handler.on_error(HttpError::from(HttpErrorType::Unauthorized).into());
                self.mark_retry(FailState::Login, Duration::from_secs(15))?;
                return Ok(());
            }
        };

        self.sender.authenticate(token)
    }

    fn retry_failstate(&mut self, state: FailState) -> ws::Result<()> {
        match state {
            FailState::Login => self.try_or_retry_auth(),
        }
    }
}

impl<H: Handler, T: TokenStorage> ws::Handler for ApiHandler<H, T> {
    fn on_error(&mut self, err: ws::Error) {
        self.handler.on_error(err.into());
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        match msg {
            ws::Message::Text(s) => {
                match parsing::ParsedResult::parse(s) {
                    Ok(v) => {
                        match v {
                            ParsedResult::Open => {
                                self.try_or_retry_auth()
                                    .map_err(Into::into)
                                    .unwrap_or_else(|x| self.handler.on_error(x))
                            }
                            ParsedResult::Heartbeat => self.sender.sender().send("[]")?,
                            _ => (),
                        }
                        self.handler.on_communication(v)?;
                    }
                    Err(e) => {
                        self.handler.on_error(e.into());
                    }
                }
            }
            ws::Message::Binary(b) => {
                error!("ignoring binary data received from websocket! {:?}", b);
            }
        }
        Ok(())
    }

    fn on_timeout(&mut self, msg: WsToken) -> ws::Result<()> {
        match self.retrying.remove(&msg.0) {
            Some(state) => self.retry_failstate(state)?,
            None => debug!("timeout for token {:?} ignored: token not known.", msg),
        }

        Ok(())
    }
}

/// Different channels one can subscribe to.
// TODO: roomMap2
pub enum Channel<'a> {
    /// Server messages (TODO: find message here).
    ServerMessages,
    /// User CPU and memory usage, updates each tick.
    UserCpu {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// User message alerts, updates whenever a message is received.
    UserMessages {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// User credit count when it changes.
    UserCredits {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// Any changes to a specific path in memory.
    UserMemoryPath {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
        /// The memory path, separated with '.'.
        path: Cow<'a, str>,
    },
    /// Any console log messages.
    UserConsole {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// Small room tile view for map viewing.
    MapRoomUpdates {
        /// The room name of the subscription.
        room_name: Cow<'a, str>,
    },
    /// Updates for all entities in a room.
    ///
    /// Note: this is limited to 2 per user account at a time, and if there are more than 2 room subscriptions active,
    /// it is random which 2 will received updates on any given ticks. Rooms which are not updated do receive an error
    /// message on "off" ticks.
    RoomUpdates {
        /// The room name of the subscription.
        room_name: Cow<'a, str>,
    },
}

impl<'a> Channel<'a> {
    /// Creates a channel subscribing to server messages.
    pub fn server_messages() -> Self {
        Channel::ServerMessages
    }

    /// Creates a channel subscribing to a user's CPU and memory.
    pub fn user_cpu<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserCpu { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to a user's CPU and memory.
    pub fn user_messages<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserMessages { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to a user's credit count.
    pub fn user_credits<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserCredits { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to a path in a user's memory.
    pub fn user_memory_path<T: Into<Cow<'a, str>>, U: Into<Cow<'a, str>>>(user_id: T, path: U) -> Self {
        Channel::UserMemoryPath {
            user_id: user_id.into(),
            path: path.into(),
        }
    }

    /// Creates a channel subscribing to a user's console output.
    pub fn user_console<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserConsole { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to map-view updates of a room.
    pub fn map_room_updates<T: Into<Cow<'a, str>>>(room_name: T) -> Self {
        Channel::MapRoomUpdates { room_name: room_name.into() }
    }

    /// Creates a channel subscribing to detailed updates of a room's contents.
    ///
    /// Note: this is limited to 2 per user account at a time, and if there are more than 2 room subscriptions active,
    /// it is random which 2 will received updates on any given ticks. Rooms which are not updated do receive an error
    /// message on "off" ticks.
    pub fn room_updates<T: Into<Cow<'a, str>>>(room_name: T) -> Self {
        Channel::RoomUpdates { room_name: room_name.into() }
    }

    /// This is a really wonky scheme, but it is probably the best one right now.
    ///
    /// Adds the channel description to the message (does not add preceding space) and collects to a vec.
    fn chain_and_complete_message<T: Iterator<Item = char>>(&self, start: T) -> String {
        match *self {
            Channel::ServerMessages => start.chain("server-messages".chars()).collect(),
            Channel::UserCpu { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/cpu".chars()).collect()
            }
            Channel::UserMessages { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/newMessages".chars()).collect()
            }
            Channel::UserCredits { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/money".chars()).collect()
            }
            Channel::UserMemoryPath { ref user_id, ref path } => {
                start.chain("user:".chars())
                    .chain(user_id.as_ref().chars())
                    .chain("/memory/".chars())
                    .chain(path.as_ref().chars())
                    .collect()
            }
            Channel::UserConsole { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/console".chars()).collect()
            }
            Channel::MapRoomUpdates { ref room_name } => {
                start.chain("roomMap2:".chars()).chain(room_name.as_ref().chars()).collect()
            }
            Channel::RoomUpdates { ref room_name } => {
                start.chain("room:".chars()).chain(room_name.as_ref().chars()).collect()
            }
        }
    }

    /// Allocates a vec with the byte representation of this channel.
    pub fn to_string(&self) -> String {
        self.chain_and_complete_message("".chars())
    }
}

/// Sender structure wrapping websocket's sender with Screeps API methods.
#[derive(Clone)]
pub struct Sender(ws::Sender);

impl Sender {
    fn authenticate(&self, token: Token) -> ws::Result<()> {
        let message = "auth "
            .chars()
            .chain(token.chars())
            .collect::<String>();

        self.send_raw(&message)
    }

    /// Subscribes to a channel. Unknown effect if already subscribed, server error?
    ///
    /// Recommended that you keep track of what channels you are subscribed to separately.
    pub fn subscribe(&self, channel: Channel) -> ws::Result<()> {
        let message = channel.chain_and_complete_message("subscribe ".chars());

        self.send_raw(&message)
    }

    /// Unsubscribes from a channel. Unknown effect if not subscribed, server error?
    ///
    /// Recommended that you keep track of what channels you are subscribed to separately.
    pub fn unsubscribe(&self, channel: Channel) -> ws::Result<()> {
        let message = channel.chain_and_complete_message("unsubscribe ".chars());

        self.send_raw(&message)
    }

    /// Sends an empty SockJS frame.
    pub fn send_empty_frame(&self) -> ws::Result<()> {
        let message = "[]";

        debug!("[SockJS emulation] sending empty frame: {:?}", message);

        self.0.send(message)
    }

    /// Sends a raw SockJS frame.
    pub fn send_raw(&self, message: &str) -> ws::Result<()> {
        let encoded = serde_json::to_string(&(message,))
            .expect("serializing a tuple containing a single string can't fail.");

        debug!("[SockJS emulation] sending frame: {:?}", message);

        self.0.send(encoded)
    }

    /// Gets the inner websocket sender.
    #[inline]
    pub fn sender(&self) -> &ws::Sender {
        &self.0
    }
}

// Send: auth <token>
// Recv: auth ok <new token>
// Send: gzip on
// Possibilities:
//  Send: subscribe room:E15N52
//  Send: .

/// Method for connecting to a screeps server, mirroring the ws-rs method of the same name.
///
/// Establishes a connection, using the given token storage to authenticate.
pub fn connect<U, F, H, T>(websocket_address: U, mut factory: F, token: T) -> ws::Result<()>
    where U: Borrow<str>,
          F: FnMut(Sender) -> H,
          H: Handler,
          T: TokenStorage + Clone
{
    ws::connect(websocket_address, |ws_sender| {
        let sender = Sender(ws_sender);
        let handler = factory(sender.clone());

        ApiHandler {
            token: token.clone(),
            handler: handler,
            sender: sender,
            retrying: FnvHashMap::default(),
        }
    })
}
<commit_msg>Add more channel names<commit_after>//! Handling of socket connections to screeps using ws-rs as a backend.

pub extern crate ws;
extern crate fnv;
use serde_json;

use std::time::Duration;
use std::borrow::{Borrow, Cow};
use std::str;

use self::fnv::FnvHashMap;
use self::ws::util::Token as WsToken;

pub use self::error::{Error, Result};
pub use self::parsing::{ParsedResult, ParsedMessage};
use error::{Error as HttpError, ErrorType as HttpErrorType};

use TokenStorage;
use Token;

mod error;
mod parsing;

/// Handler trait to implement for socket clients.
pub trait Handler {
    /// Run when a disconnect has occurred.
    fn on_disconnect(&mut self) -> ws::Result<()> {
        Ok(())
    }

    /// Run on any websocket error or message parsing error.
    fn on_error(&mut self, err: Error) {
        warn!("screeps socket error uncaught due to default handler method: {}",
              err);
    }

    /// Run on any messages from the server.
    fn on_message(&mut self, msg: parsing::ParsedMessage) -> ws::Result<()>;

    /// Run on any communication from the server.
    ///
    /// Default behavior is to ignore heartbeats, open and close messages, and just send any actual messages
    /// to on_message.
    fn on_communication(&mut self, result: parsing::ParsedResult) -> ws::Result<()> {
        match result {
            ParsedResult::Message(msg) => {
                debug!("screeps socket connection received single SockJS message");
                self.on_message(msg)
            }
            ParsedResult::Messages(messages) => {
                debug!("screeps socket connection received array of {} SockJS messages",
                       messages.len());
                for msg in messages {
                    self.on_message(msg)?
                }
                Ok(())
            }
            ParsedResult::Heartbeat => {
                debug!("screeps socket connection received SockJS heartbeat");
                Ok(())
            }
            ParsedResult::Open => {
                debug!("screeps socket connection received SockJS open");
                Ok(())
            }
            ParsedResult::Close { code, reason } => {
                // TODO: should we pass this on?
                debug!("screeps socket connection received SockJS close ({}, {})",
                       code,
                       reason);
                Ok(())
            }
        }
    }
}

impl<T> Handler for T
    where T: FnMut(parsing::ParsedMessage) -> ws::Result<()>
{
    fn on_message(&mut self, msg: parsing::ParsedMessage) -> ws::Result<()> {
        (self)(msg)
    }
}

enum FailState {
    Login,
}

struct ApiHandler<H: Handler, T: TokenStorage = Option<Token>> {
    token: T,
    handler: H,
    sender: Sender,
    retrying: FnvHashMap<usize, FailState>,
}

impl<H: Handler, T: TokenStorage> ApiHandler<H, T> {
    fn mark_retry(&mut self, failed: FailState, retry_in: Duration) -> ws::Result<()> {
        let mut num = 0usize;
        while self.retrying.contains_key(&num) {
            num += 1;
        }
        self.retrying.insert(num, failed);

        self.sender.sender().timeout((retry_in.subsec_nanos() as f64 / 1.0e6) as u64 + retry_in.as_secs() * 1000,
                                     WsToken(num))
    }

    fn try_or_retry_auth(&mut self) -> ws::Result<()> {
        let token = match self.token.take_token() {
            Some(t) => t,
            None => {
                self.handler.on_error(HttpError::from(HttpErrorType::Unauthorized).into());
                self.mark_retry(FailState::Login, Duration::from_secs(15))?;
                return Ok(());
            }
        };

        self.sender.authenticate(token)
    }

    fn retry_failstate(&mut self, state: FailState) -> ws::Result<()> {
        match state {
            FailState::Login => self.try_or_retry_auth(),
        }
    }
}

impl<H: Handler, T: TokenStorage> ws::Handler for ApiHandler<H, T> {
    fn on_error(&mut self, err: ws::Error) {
        self.handler.on_error(err.into());
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        match msg {
            ws::Message::Text(s) => {
                match parsing::ParsedResult::parse(s) {
                    Ok(v) => {
                        match v {
                            ParsedResult::Open => {
                                self.try_or_retry_auth()
                                    .map_err(Into::into)
                                    .unwrap_or_else(|x| self.handler.on_error(x))
                            }
                            ParsedResult::Heartbeat => self.sender.sender().send("[]")?,
                            _ => (),
                        }
                        self.handler.on_communication(v)?;
                    }
                    Err(e) => {
                        self.handler.on_error(e.into());
                    }
                }
            }
            ws::Message::Binary(b) => {
                error!("ignoring binary data received from websocket! {:?}", b);
            }
        }
        Ok(())
    }

    fn on_timeout(&mut self, msg: WsToken) -> ws::Result<()> {
        match self.retrying.remove(&msg.0) {
            Some(state) => self.retry_failstate(state)?,
            None => debug!("timeout for token {:?} ignored: token not known.", msg),
        }

        Ok(())
    }
}

/// Different channels one can subscribe to.
pub enum Channel<'a> {
    /// Server messages (TODO: find message here).
    ServerMessages,
    /// User CPU and memory usage, updates each tick.
    UserCpu {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// User message alerts, updates whenever a message is received.
    UserMessages {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// User conversation alert: updates whenever a message is received from a specific user.
    UserConversation {
        /// The user ID of the connected user.
        user_id: Cow<'a, str>,
        /// The user ID on the other side of the conversation to listen to.
        target_user_id: Cow<'a, str>,
    },
    /// User credit count when it changes.
    UserCredits {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// Any changes to a specific path in memory.
    UserMemoryPath {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
        /// The memory path, separated with '.'.
        path: Cow<'a, str>,
    },
    /// Any console log messages.
    UserConsole {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// User active branch changes: updates whenever the active branch changes.
    UserActiveBranch {
        /// The user ID of the subscription.
        user_id: Cow<'a, str>,
    },
    /// Small room tile view for map viewing.
    MapRoomUpdates {
        /// The room name of the subscription.
        room_name: Cow<'a, str>,
    },
    /// Updates for all entities in a room.
    ///
    /// Note: this is limited to 2 per user account at a time, and if there are more than 2 room subscriptions active,
    /// it is random which 2 will received updates on any given ticks. Rooms which are not updated do receive an error
    /// message on "off" ticks.
    RoomUpdates {
        /// The room name of the subscription.
        room_name: Cow<'a, str>,
    },
}

impl<'a> Channel<'a> {
    /// Creates a channel subscribing to server messages.
    pub fn server_messages() -> Self {
        Channel::ServerMessages
    }

    /// Creates a channel subscribing to a user's CPU and memory.
    pub fn user_cpu<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserCpu { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to a user's new message notifications.
    pub fn user_messages<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserMessages { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to new messages in a user's specific conversation.
    pub fn user_convesation<T, U>(user_id: T, target_user_id: U) -> Self
        where T: Into<Cow<'a, str>>,
              U: Into<Cow<'a, str>>
    {
        Channel::UserConversation {
            user_id: user_id.into(),
            target_user_id: target_user_id.into(),
        }
    }

    /// Creates a channel subscribing to a user's credit count.
    pub fn user_credits<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserCredits { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to a path in a user's memory.
    pub fn user_memory_path<T, U>(user_id: T, path: U) -> Self
        where T: Into<Cow<'a, str>>,
              U: Into<Cow<'a, str>>
    {
        Channel::UserMemoryPath {
            user_id: user_id.into(),
            path: path.into(),
        }
    }

    /// Creates a channel subscribing to a user's console output.
    pub fn user_console<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserConsole { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to when a user's active code branch changes.
    pub fn user_active_branch<T: Into<Cow<'a, str>>>(user_id: T) -> Self {
        Channel::UserActiveBranch { user_id: user_id.into() }
    }

    /// Creates a channel subscribing to map-view updates of a room.
    pub fn map_room_updates<T: Into<Cow<'a, str>>>(room_name: T) -> Self {
        Channel::MapRoomUpdates { room_name: room_name.into() }
    }

    /// Creates a channel subscribing to detailed updates of a room's contents.
    ///
    /// Note: this is limited to 2 per user account at a time, and if there are more than 2 room subscriptions active,
    /// it is random which 2 will received updates on any given ticks. Rooms which are not updated do receive an error
    /// message on "off" ticks.
    pub fn room_updates<T: Into<Cow<'a, str>>>(room_name: T) -> Self {
        Channel::RoomUpdates { room_name: room_name.into() }
    }

    /// This is a really wonky scheme, but it is probably the best one right now.
    ///
    /// Adds the channel description to the message (does not add preceding space) and collects to a vec.
    fn chain_and_complete_message<T: Iterator<Item = char>>(&self, start: T) -> String {
        match *self {
            Channel::ServerMessages => start.chain("server-message".chars()).collect(),
            Channel::UserCpu { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/cpu".chars()).collect()
            }
            Channel::UserMessages { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/newMessage".chars()).collect()
            }
            Channel::UserConversation { ref user_id, ref target_user_id } => {
                start.chain("user:".chars())
                    .chain(user_id.as_ref().chars())
                    .chain("/message:".chars())
                    .chain(target_user_id.as_ref().chars())
                    .collect()
            }
            Channel::UserCredits { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/money".chars()).collect()
            }
            Channel::UserMemoryPath { ref user_id, ref path } => {
                start.chain("user:".chars())
                    .chain(user_id.as_ref().chars())
                    .chain("/memory/".chars())
                    .chain(path.as_ref().chars())
                    .collect()
            }
            Channel::UserConsole { ref user_id } => {
                start.chain("user:".chars()).chain(user_id.as_ref().chars()).chain("/console".chars()).collect()
            }
            Channel::UserActiveBranch { ref user_id } => {
                start.chain("user:".chars())
                    .chain(user_id.as_ref().chars())
                    .chain("/set-active-branch".chars())
                    .collect()
            }
            Channel::MapRoomUpdates { ref room_name } => {
                start.chain("roomMap2:".chars()).chain(room_name.as_ref().chars()).collect()
            }
            Channel::RoomUpdates { ref room_name } => {
                start.chain("room:".chars()).chain(room_name.as_ref().chars()).collect()
            }
        }
    }

    /// Allocates a vec with the byte representation of this channel.
    pub fn to_string(&self) -> String {
        self.chain_and_complete_message("".chars())
    }
}

/// Sender structure wrapping websocket's sender with Screeps API methods.
#[derive(Clone)]
pub struct Sender(ws::Sender);

impl Sender {
    fn authenticate(&self, token: Token) -> ws::Result<()> {
        let message = "auth "
            .chars()
            .chain(token.chars())
            .collect::<String>();

        self.send_raw(&message)
    }

    /// Subscribes to a channel. Unknown effect if already subscribed, server error?
    ///
    /// Recommended that you keep track of what channels you are subscribed to separately.
    pub fn subscribe(&self, channel: Channel) -> ws::Result<()> {
        let message = channel.chain_and_complete_message("subscribe ".chars());

        self.send_raw(&message)
    }

    /// Unsubscribes from a channel. Unknown effect if not subscribed, server error?
    ///
    /// Recommended that you keep track of what channels you are subscribed to separately.
    pub fn unsubscribe(&self, channel: Channel) -> ws::Result<()> {
        let message = channel.chain_and_complete_message("unsubscribe ".chars());

        self.send_raw(&message)
    }

    /// Sends an empty SockJS frame.
    pub fn send_empty_frame(&self) -> ws::Result<()> {
        let message = "[]";

        debug!("[SockJS emulation] sending empty frame: {:?}", message);

        self.0.send(message)
    }

    /// Sends a raw SockJS frame.
    pub fn send_raw(&self, message: &str) -> ws::Result<()> {
        let encoded = serde_json::to_string(&(message,))
            .expect("serializing a tuple containing a single string can't fail.");

        debug!("[SockJS emulation] sending frame: {:?}", message);

        self.0.send(encoded)
    }

    /// Gets the inner websocket sender.
    #[inline]
    pub fn sender(&self) -> &ws::Sender {
        &self.0
    }
}

// Send: auth <token>
// Recv: auth ok <new token>
// Send: gzip on
// Possibilities:
//  Send: subscribe room:E15N52
//  Send: .

/// Method for connecting to a screeps server, mirroring the ws-rs method of the same name.
///
/// Establishes a connection, using the given token storage to authenticate.
pub fn connect<U, F, H, T>(websocket_address: U, mut factory: F, token: T) -> ws::Result<()>
    where U: Borrow<str>,
          F: FnMut(Sender) -> H,
          H: Handler,
          T: TokenStorage + Clone
{
    ws::connect(websocket_address, |ws_sender| {
        let sender = Sender(ws_sender);
        let handler = factory(sender.clone());

        ApiHandler {
            token: token.clone(),
            handler: handler,
            sender: sender,
            retrying: FnvHashMap::default(),
        }
    })
}
<|endoftext|>


<commit_before>/// Strongly borrowed from TypedArena
/// Differs in growth strategy (non-doubling)
/// And only for variable-sized strings

use std::borrow::Borrow;
use std::cell::{Cell,RefCell};
use std::cmp::max;
use std::collections::DList;
use std::collections::hash_map::Entry::{Occupied,Vacant};
use std::collections::hash_map::HashMap;
use std::collections::hash_state::DefaultState;
use std::default::Default;
use std::ops::Deref;
use std::raw::Slice;
use std::rt::heap::{allocate, deallocate};
use std::{fmt,hash,mem,ptr,str};
use xxhash::XXHasher;

struct Chunk {
    start: *mut u8,
    capacity: usize,
}

impl Chunk {
    fn new(capacity: usize) -> Chunk {
        Chunk {
            start: unsafe { allocate(capacity, mem::min_align_of::<u8>()) },
            capacity: capacity,
        }
    }

    // Returns a pointer to the beginning of the allocated space.
    #[inline]
    fn start(&self) -> *const u8 {
        self.start as *const u8
    }

    // Returns a pointer to the end of the allocated space.
    #[inline]
    fn end(&self) -> *const u8 {
        unsafe { self.start().offset(self.capacity as isize) }
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        unsafe {
            deallocate(self.start, self.capacity, mem::min_align_of::<u8>());
        }
    }
}

#[derive(Copy)]
pub struct InternedString {
    slice: Slice<u8>,
}

impl InternedString {
    fn from_parts(data: *const u8, len: usize) -> InternedString {
        InternedString {
            slice: Slice {
                data: data,
                len: len,
            },
        }
    }

    pub fn from_str(s: &str) -> InternedString {
        let bytes: &[u8] = s.as_bytes();
        let slice: Slice<u8> = unsafe { mem::transmute(bytes) };
        InternedString {
            slice: slice,
        }
    }

    pub fn as_slice<'s>(&self) -> &'s str {
        unsafe {
            let bytes: &[u8] = mem::transmute(self.slice);
            str::from_utf8_unchecked(bytes)
        }
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        self.as_slice().eq(other.as_slice())
    }
}

impl PartialEq<str> for InternedString {
    fn eq(&self, other: &str) -> bool {
        self.as_slice().eq(other)
    }
}

impl Eq for InternedString {}

impl<S> hash::Hash<S> for InternedString
    where S: hash::Writer + hash::Hasher
{
    fn hash(&self, state: &mut S) {
        self.as_slice().hash(state)
    }
}

impl Borrow<str> for InternedString {
    fn borrow(&self) -> &str {
        self.as_slice()
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_slice()
    }
}

pub struct StringPool {
    start: Cell<*mut u8>,
    end: Cell<*const u8>,
    chunks: RefCell<DList<Chunk>>,
    index: RefCell<HashMap<InternedString, InternedString, DefaultState<XXHasher>>>,
}

static CAPACITY: usize = 10240;

impl StringPool {
    pub fn new() -> StringPool {
        StringPool{
            start: Cell::new(ptr::null_mut()),
            end: Cell::new(ptr::null()),
            chunks: RefCell::new(DList::new()),
            index: RefCell::new(Default::default()),
        }
    }

    pub fn intern<'s>(&'s self, s: &str) -> &'s str {
        if s == "" { return ""; }

        let search_string = InternedString::from_str(s);

        let interned_str = match self.index.borrow_mut().entry(search_string) {
            Occupied(entry) => *entry.get(),
            Vacant(entry) => *entry.insert(self.do_intern(s)),
        };

        // The lifetime is really matched to us
        unsafe { mem::transmute(interned_str) }
    }

    fn do_intern(&self, s: &str) -> InternedString {
        self.ensure_capacity(s.len());
        self.store(s)
    }

    fn ensure_capacity(&self, str_len: usize) {
        let remaining = self.end.get() as usize - self.start.get() as usize;
        if remaining < str_len {
            self.allocate_another(max(CAPACITY, str_len))
        }
    }

    fn allocate_another(&self, capacity: usize) {
        let chunk = Chunk::new(capacity);
        self.start.set(chunk.start() as *mut u8);
        self.end.set(chunk.end());
        self.chunks.borrow_mut().push_front(chunk);
    }

    fn store(&self, s: &str) -> InternedString {
        let str_start = s.as_bytes().as_ptr();
        let str_len = s.len();

        unsafe {
            ptr::copy_nonoverlapping_memory(self.start.get(), str_start, str_len);

            // Rebuild the string from our buffer
            let interned_str = InternedString::from_parts(self.start.get() as *const u8, str_len);

            // Increase current pointer
            self.start.set(self.start.get().offset(str_len as isize));

            interned_str
        }
    }
}

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

    #[test]
    fn keeps_the_same_string() {
        let s = StringPool::new();

        let interned = s.intern("hello");

        assert_eq!(interned, "hello");
    }

    #[test]
    fn does_not_reuse_the_pointer_of_the_input() {
        let s = StringPool::new();
        let input = "hello";

        let interned = s.intern(input);

        assert!(
            input.as_bytes().as_ptr() !=
            interned.as_bytes().as_ptr()
        );
    }

    #[test]
    fn reuses_the_pointer_for_repeated_input() {
        let s = StringPool::new();

        let interned1 = s.intern("world");
        let interned2 = s.intern("world");

        assert_eq!(
            interned1.as_bytes().as_ptr(),
            interned2.as_bytes().as_ptr()
        );
    }

    // #[test]
    // #[compile_failure]
    // fn string_cannot_outlive_the_pool() {
    //     let z = {
    //         let s = StringPool::new();
    //         s.intern("hello")
    //     };
    // }

    #[test]
    fn ignores_the_lifetime_of_the_input_string() {
        let s = StringPool::new();

        let interned = {
            let allocated_string = String::from_str("green");
            s.intern(&allocated_string)
        };

        // allocated_string is gone now, but we should be able to
        // access the result value until the storage goes away.

        assert_eq!(interned, "green");
    }

    #[test]
    fn can_be_dropped_immediately() {
        StringPool::new();
    }

    fn return_populated_storage() -> (StringPool, *const u8) {
        let s = StringPool::new();
        let ptr = {
            let interned = s.intern("hello");
            interned.as_ptr()
        };
        (s, ptr)
    }

    #[test]
    fn can_return_storage_populated_with_values() {
        let (s, ptr_val) = return_populated_storage();
        let interned = s.intern("hello");
        assert_eq!(interned.as_ptr(), ptr_val);
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;

    use super::StringPool;

    #[bench]
    fn single_string(b: &mut Bencher) {
        let s = StringPool::new();
        b.iter(|| s.intern("hello"));
        b.bytes = "hello".len() as u64;
    }

    #[bench]
    fn many_unique_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            range(0, 1000)
            .map(|i| format!("str{}str", i))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }

    #[bench]
    fn many_repeated_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            range(0, 1000)
            .map(|i| format!("str{}str", i % 100))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }
}
<commit_msg>DList -> LinkedList<commit_after>/// Strongly borrowed from TypedArena
/// Differs in growth strategy (non-doubling)
/// And only for variable-sized strings

use std::borrow::Borrow;
use std::cell::{Cell,RefCell};
use std::cmp::max;
use std::collections::LinkedList;
use std::collections::hash_map::Entry::{Occupied,Vacant};
use std::collections::hash_map::HashMap;
use std::collections::hash_state::DefaultState;
use std::default::Default;
use std::ops::Deref;
use std::raw::Slice;
use std::rt::heap::{allocate, deallocate};
use std::{fmt,hash,mem,ptr,str};
use xxhash::XXHasher;

struct Chunk {
    start: *mut u8,
    capacity: usize,
}

impl Chunk {
    fn new(capacity: usize) -> Chunk {
        Chunk {
            start: unsafe { allocate(capacity, mem::min_align_of::<u8>()) },
            capacity: capacity,
        }
    }

    // Returns a pointer to the beginning of the allocated space.
    #[inline]
    fn start(&self) -> *const u8 {
        self.start as *const u8
    }

    // Returns a pointer to the end of the allocated space.
    #[inline]
    fn end(&self) -> *const u8 {
        unsafe { self.start().offset(self.capacity as isize) }
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        unsafe {
            deallocate(self.start, self.capacity, mem::min_align_of::<u8>());
        }
    }
}

#[derive(Copy)]
pub struct InternedString {
    slice: Slice<u8>,
}

impl InternedString {
    fn from_parts(data: *const u8, len: usize) -> InternedString {
        InternedString {
            slice: Slice {
                data: data,
                len: len,
            },
        }
    }

    pub fn from_str(s: &str) -> InternedString {
        let bytes: &[u8] = s.as_bytes();
        let slice: Slice<u8> = unsafe { mem::transmute(bytes) };
        InternedString {
            slice: slice,
        }
    }

    pub fn as_slice<'s>(&self) -> &'s str {
        unsafe {
            let bytes: &[u8] = mem::transmute(self.slice);
            str::from_utf8_unchecked(bytes)
        }
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        self.as_slice().eq(other.as_slice())
    }
}

impl PartialEq<str> for InternedString {
    fn eq(&self, other: &str) -> bool {
        self.as_slice().eq(other)
    }
}

impl Eq for InternedString {}

impl<S> hash::Hash<S> for InternedString
    where S: hash::Writer + hash::Hasher
{
    fn hash(&self, state: &mut S) {
        self.as_slice().hash(state)
    }
}

impl Borrow<str> for InternedString {
    fn borrow(&self) -> &str {
        self.as_slice()
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_slice()
    }
}

pub struct StringPool {
    start: Cell<*mut u8>,
    end: Cell<*const u8>,
    chunks: RefCell<LinkedList<Chunk>>,
    index: RefCell<HashMap<InternedString, InternedString, DefaultState<XXHasher>>>,
}

static CAPACITY: usize = 10240;

impl StringPool {
    pub fn new() -> StringPool {
        StringPool{
            start: Cell::new(ptr::null_mut()),
            end: Cell::new(ptr::null()),
            chunks: RefCell::new(LinkedList::new()),
            index: RefCell::new(Default::default()),
        }
    }

    pub fn intern<'s>(&'s self, s: &str) -> &'s str {
        if s == "" { return ""; }

        let search_string = InternedString::from_str(s);

        let interned_str = match self.index.borrow_mut().entry(search_string) {
            Occupied(entry) => *entry.get(),
            Vacant(entry) => *entry.insert(self.do_intern(s)),
        };

        // The lifetime is really matched to us
        unsafe { mem::transmute(interned_str) }
    }

    fn do_intern(&self, s: &str) -> InternedString {
        self.ensure_capacity(s.len());
        self.store(s)
    }

    fn ensure_capacity(&self, str_len: usize) {
        let remaining = self.end.get() as usize - self.start.get() as usize;
        if remaining < str_len {
            self.allocate_another(max(CAPACITY, str_len))
        }
    }

    fn allocate_another(&self, capacity: usize) {
        let chunk = Chunk::new(capacity);
        self.start.set(chunk.start() as *mut u8);
        self.end.set(chunk.end());
        self.chunks.borrow_mut().push_front(chunk);
    }

    fn store(&self, s: &str) -> InternedString {
        let str_start = s.as_bytes().as_ptr();
        let str_len = s.len();

        unsafe {
            ptr::copy_nonoverlapping_memory(self.start.get(), str_start, str_len);

            // Rebuild the string from our buffer
            let interned_str = InternedString::from_parts(self.start.get() as *const u8, str_len);

            // Increase current pointer
            self.start.set(self.start.get().offset(str_len as isize));

            interned_str
        }
    }
}

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

    #[test]
    fn keeps_the_same_string() {
        let s = StringPool::new();

        let interned = s.intern("hello");

        assert_eq!(interned, "hello");
    }

    #[test]
    fn does_not_reuse_the_pointer_of_the_input() {
        let s = StringPool::new();
        let input = "hello";

        let interned = s.intern(input);

        assert!(
            input.as_bytes().as_ptr() !=
            interned.as_bytes().as_ptr()
        );
    }

    #[test]
    fn reuses_the_pointer_for_repeated_input() {
        let s = StringPool::new();

        let interned1 = s.intern("world");
        let interned2 = s.intern("world");

        assert_eq!(
            interned1.as_bytes().as_ptr(),
            interned2.as_bytes().as_ptr()
        );
    }

    // #[test]
    // #[compile_failure]
    // fn string_cannot_outlive_the_pool() {
    //     let z = {
    //         let s = StringPool::new();
    //         s.intern("hello")
    //     };
    // }

    #[test]
    fn ignores_the_lifetime_of_the_input_string() {
        let s = StringPool::new();

        let interned = {
            let allocated_string = String::from_str("green");
            s.intern(&allocated_string)
        };

        // allocated_string is gone now, but we should be able to
        // access the result value until the storage goes away.

        assert_eq!(interned, "green");
    }

    #[test]
    fn can_be_dropped_immediately() {
        StringPool::new();
    }

    fn return_populated_storage() -> (StringPool, *const u8) {
        let s = StringPool::new();
        let ptr = {
            let interned = s.intern("hello");
            interned.as_ptr()
        };
        (s, ptr)
    }

    #[test]
    fn can_return_storage_populated_with_values() {
        let (s, ptr_val) = return_populated_storage();
        let interned = s.intern("hello");
        assert_eq!(interned.as_ptr(), ptr_val);
    }
}

#[cfg(test)]
mod bench {
    use test::Bencher;

    use super::StringPool;

    #[bench]
    fn single_string(b: &mut Bencher) {
        let s = StringPool::new();
        b.iter(|| s.intern("hello"));
        b.bytes = "hello".len() as u64;
    }

    #[bench]
    fn many_unique_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            range(0, 1000)
            .map(|i| format!("str{}str", i))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }

    #[bench]
    fn many_repeated_string(b: &mut Bencher) {
        let s = StringPool::new();

        let strings: Vec<String> =
            range(0, 1000)
            .map(|i| format!("str{}str", i % 100))
            .collect();
        b.iter(|| {
            for ss in strings.iter() {
                s.intern(ss);
            }
        });
        b.bytes = strings.iter().fold(0, |a, s| a + s.len()) as u64;
    }
}
<|endoftext|>


<commit_before>//! Monitoring API for filesystem events.
//!
//! Inotify is a Linux-only API to monitor filesystems events.
//!
//! For more documentation, please read [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
//!
//! # Examples
//!
//! Monitor all events happening in directory "test":
//! ```no_run
//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
//! #
//! // We create a new inotify instance.
//! let instance = Inotify::init(InitFlags::empty()).unwrap();
//!
//! // We add a new watch on directory "test" for all events.
//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
//!
//! loop {
//!     // We read from our inotify instance for events.
//!     let events = instance.read_events().unwrap();
//!     println!("Events: {:?}", events);
//! }
//! ```

use libc;
use libc::{
    c_char,
    c_int,
    uint32_t
};
use std::ffi::{OsString,OsStr,CStr};
use std::os::unix::ffi::OsStrExt;
use std::mem::size_of;
use std::os::unix::io::{RawFd,AsRawFd,FromRawFd};
use unistd::read;
use Result;
use NixPath;
use errno::Errno;

libc_bitflags! {
    /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
    pub struct AddWatchFlags: uint32_t {
        IN_ACCESS;
        IN_MODIFY;
        IN_ATTRIB;
        IN_CLOSE_WRITE;
        IN_CLOSE_NOWRITE;
        IN_OPEN;
        IN_MOVED_FROM;
        IN_MOVED_TO;
        IN_CREATE;
        IN_DELETE;
        IN_DELETE_SELF;
        IN_MOVE_SELF;

        IN_UNMOUNT;
        IN_Q_OVERFLOW;
        IN_IGNORED;

        IN_CLOSE;
        IN_MOVE;

        IN_ONLYDIR;
        IN_DONT_FOLLOW;

        IN_ISDIR;
        IN_ONESHOT;
        IN_ALL_EVENTS;
    }
}

libc_bitflags! {
    /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
    pub struct InitFlags: c_int {
        IN_CLOEXEC;
        IN_NONBLOCK;
    }
}

/// An inotify instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
#[derive(Debug, Clone, Copy)]
pub struct Inotify {
    fd: RawFd
}

/// This object is returned when you create a new watch on an inotify instance.
/// It is then returned as part of an event once triggered. It allows you to
/// know which watch triggered which event.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
    wd: i32
}

/// A single inotify event.
///
/// For more documentation see, [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
#[derive(Debug)]
pub struct InotifyEvent {
    /// Watch descriptor. This field corresponds to the watch descriptor you
    /// were issued when calling add_watch. It allows you to know which watch
    /// this event comes from.
    pub wd: WatchDescriptor,
    /// Event mask. This field is a bitfield describing the exact event that
    /// occured.
    pub mask: AddWatchFlags,
    /// This cookie is a number that allows you to connect related events. For
    /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
    pub cookie: u32,
    /// Filename. This field exists only if the event was triggered for a file
    /// inside the watched directory.
    pub name: Option<OsString>
}

impl Inotify {
    /// Initialize a new inotify instance.
    ///
    /// Returns a Result containing an inotify instance.
    ///
    /// For more information see, [inotify_init(2)](http://man7.org/linux/man-pages/man2/inotify_init.2.html).
    pub fn init(flags: InitFlags) -> Result<Inotify> {
        let res = Errno::result(unsafe {
            libc::inotify_init1(flags.bits())
        });

        res.map(|fd| Inotify { fd })
    }

    /// Adds a new watch on the target file or directory.
    ///
    /// Returns a watch descriptor. This is not a File Descriptor!
    ///
    /// For more information see, [inotify_add_watch(2)](http://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
    pub fn add_watch<P: ?Sized + NixPath>(&self,
                                          path: &P,
                                          mask: AddWatchFlags)
                                            -> Result<WatchDescriptor>
    {
        let res = path.with_nix_path(|cstr| {
            unsafe {
                libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
            }
        })?;

        Errno::result(res).map(|wd| WatchDescriptor { wd })
    }

    /// Removes an existing watch using the watch descriptor returned by
    /// inotify_add_watch.
    ///
    /// Returns an EINVAL error if the watch descriptor is invalid.
    ///
    /// For more information see, [inotify_rm_watch(2)](http://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
    #[cfg(target_os = "linux")]
    pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) };

        Errno::result(res).map(drop)
    }

    #[cfg(target_os = "android")]
    pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) };

        Errno::result(res).map(drop)
    }

    /// Reads a collection of events from the inotify file descriptor. This call
    /// can either be blocking or non blocking depending on whether IN_NONBLOCK
    /// was set at initialization.
    ///
    /// Returns as many events as available. If the call was non blocking and no
    /// events could be read then the EAGAIN error is returned.
    pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
        let header_size = size_of::<libc::inotify_event>();
        let mut buffer = [0u8; 4096];
        let mut events = Vec::new();
        let mut offset = 0;

        let nread = read(self.fd, &mut buffer)?;

        while (nread - offset) >= header_size {
            let event = unsafe {
                &*(
                    buffer
                        .as_ptr()
                        .offset(offset as isize) as *const libc::inotify_event
                )
            };

            let name = match event.len {
                0 => None,
                _ => {
                    let ptr = unsafe {
                        buffer
                            .as_ptr()
                            .offset(offset as isize + header_size as isize)
                            as *const c_char
                    };
                    let cstr = unsafe { CStr::from_ptr(ptr) };

                    Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
                }
            };

            events.push(InotifyEvent {
                wd: WatchDescriptor { wd: event.wd },
                mask: AddWatchFlags::from_bits_truncate(event.mask),
                cookie: event.cookie,
                name
            });

            offset += header_size + event.len as usize;
        }

        Ok(events)
    }
}

impl AsRawFd for Inotify {
    fn as_raw_fd(&self) -> RawFd {
        self.fd
    }
}

impl FromRawFd for Inotify {
    unsafe fn from_raw_fd(fd: RawFd) -> Self {
        Inotify { fd }
    }
}
<commit_msg>libc::uint32_t -> rust u32<commit_after>//! Monitoring API for filesystem events.
//!
//! Inotify is a Linux-only API to monitor filesystems events.
//!
//! For more documentation, please read [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
//!
//! # Examples
//!
//! Monitor all events happening in directory "test":
//! ```no_run
//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
//! #
//! // We create a new inotify instance.
//! let instance = Inotify::init(InitFlags::empty()).unwrap();
//!
//! // We add a new watch on directory "test" for all events.
//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
//!
//! loop {
//!     // We read from our inotify instance for events.
//!     let events = instance.read_events().unwrap();
//!     println!("Events: {:?}", events);
//! }
//! ```

use libc;
use libc::{
    c_char,
    c_int,
};
use std::ffi::{OsString,OsStr,CStr};
use std::os::unix::ffi::OsStrExt;
use std::mem::size_of;
use std::os::unix::io::{RawFd,AsRawFd,FromRawFd};
use unistd::read;
use Result;
use NixPath;
use errno::Errno;

libc_bitflags! {
    /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
    pub struct AddWatchFlags: u32 {
        IN_ACCESS;
        IN_MODIFY;
        IN_ATTRIB;
        IN_CLOSE_WRITE;
        IN_CLOSE_NOWRITE;
        IN_OPEN;
        IN_MOVED_FROM;
        IN_MOVED_TO;
        IN_CREATE;
        IN_DELETE;
        IN_DELETE_SELF;
        IN_MOVE_SELF;

        IN_UNMOUNT;
        IN_Q_OVERFLOW;
        IN_IGNORED;

        IN_CLOSE;
        IN_MOVE;

        IN_ONLYDIR;
        IN_DONT_FOLLOW;

        IN_ISDIR;
        IN_ONESHOT;
        IN_ALL_EVENTS;
    }
}

libc_bitflags! {
    /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
    pub struct InitFlags: c_int {
        IN_CLOEXEC;
        IN_NONBLOCK;
    }
}

/// An inotify instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
#[derive(Debug, Clone, Copy)]
pub struct Inotify {
    fd: RawFd
}

/// This object is returned when you create a new watch on an inotify instance.
/// It is then returned as part of an event once triggered. It allows you to
/// know which watch triggered which event.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
    wd: i32
}

/// A single inotify event.
///
/// For more documentation see, [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
#[derive(Debug)]
pub struct InotifyEvent {
    /// Watch descriptor. This field corresponds to the watch descriptor you
    /// were issued when calling add_watch. It allows you to know which watch
    /// this event comes from.
    pub wd: WatchDescriptor,
    /// Event mask. This field is a bitfield describing the exact event that
    /// occured.
    pub mask: AddWatchFlags,
    /// This cookie is a number that allows you to connect related events. For
    /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
    pub cookie: u32,
    /// Filename. This field exists only if the event was triggered for a file
    /// inside the watched directory.
    pub name: Option<OsString>
}

impl Inotify {
    /// Initialize a new inotify instance.
    ///
    /// Returns a Result containing an inotify instance.
    ///
    /// For more information see, [inotify_init(2)](http://man7.org/linux/man-pages/man2/inotify_init.2.html).
    pub fn init(flags: InitFlags) -> Result<Inotify> {
        let res = Errno::result(unsafe {
            libc::inotify_init1(flags.bits())
        });

        res.map(|fd| Inotify { fd })
    }

    /// Adds a new watch on the target file or directory.
    ///
    /// Returns a watch descriptor. This is not a File Descriptor!
    ///
    /// For more information see, [inotify_add_watch(2)](http://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
    pub fn add_watch<P: ?Sized + NixPath>(&self,
                                          path: &P,
                                          mask: AddWatchFlags)
                                            -> Result<WatchDescriptor>
    {
        let res = path.with_nix_path(|cstr| {
            unsafe {
                libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
            }
        })?;

        Errno::result(res).map(|wd| WatchDescriptor { wd })
    }

    /// Removes an existing watch using the watch descriptor returned by
    /// inotify_add_watch.
    ///
    /// Returns an EINVAL error if the watch descriptor is invalid.
    ///
    /// For more information see, [inotify_rm_watch(2)](http://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
    #[cfg(target_os = "linux")]
    pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) };

        Errno::result(res).map(drop)
    }

    #[cfg(target_os = "android")]
    pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) };

        Errno::result(res).map(drop)
    }

    /// Reads a collection of events from the inotify file descriptor. This call
    /// can either be blocking or non blocking depending on whether IN_NONBLOCK
    /// was set at initialization.
    ///
    /// Returns as many events as available. If the call was non blocking and no
    /// events could be read then the EAGAIN error is returned.
    pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
        let header_size = size_of::<libc::inotify_event>();
        let mut buffer = [0u8; 4096];
        let mut events = Vec::new();
        let mut offset = 0;

        let nread = read(self.fd, &mut buffer)?;

        while (nread - offset) >= header_size {
            let event = unsafe {
                &*(
                    buffer
                        .as_ptr()
                        .offset(offset as isize) as *const libc::inotify_event
                )
            };

            let name = match event.len {
                0 => None,
                _ => {
                    let ptr = unsafe {
                        buffer
                            .as_ptr()
                            .offset(offset as isize + header_size as isize)
                            as *const c_char
                    };
                    let cstr = unsafe { CStr::from_ptr(ptr) };

                    Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
                }
            };

            events.push(InotifyEvent {
                wd: WatchDescriptor { wd: event.wd },
                mask: AddWatchFlags::from_bits_truncate(event.mask),
                cookie: event.cookie,
                name
            });

            offset += header_size + event.len as usize;
        }

        Ok(events)
    }
}

impl AsRawFd for Inotify {
    fn as_raw_fd(&self) -> RawFd {
        self.fd
    }
}

impl FromRawFd for Inotify {
    unsafe fn from_raw_fd(fd: RawFd) -> Self {
        Inotify { fd }
    }
}
<|endoftext|>


<commit_before>mod crc;
#[macro_use]
mod macros;

pub use self::crc::{crc8, crc16};

// Convert one to four byte slices into an unsigned 32-bit number.
//
// NOTE: This assumes big-endian since most numbers in the FLAC binary are
// that endianness.
#[inline]
pub fn to_u32(bytes: &[u8]) -> u32 {
  let length = bytes.len();

  debug_assert!(length <= 4);

  (0..length).fold(0, |result, i|
    result + ((bytes[i] as u32) << ((length - 1 - i) * 8))
  )
}

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

  #[test]
  #[should_panic]
  fn test_panic_to_u32() {
    to_u32(&[0x00, 0x00, 0x00, 0x00, 0x00]);
  }

  #[test]
  fn test_to_u32() {
    let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];

    assert_eq!(to_u32(&bytes[0..1]), 0x00000001);
    assert_eq!(to_u32(&bytes[3..5]), 0x00006789);
    assert_eq!(to_u32(&bytes[1..4]), 0x00234567);
    assert_eq!(to_u32(&bytes[4..]), 0x89abcdef);
  }
}
<commit_msg>Add separate function for sign extension<commit_after>mod crc;
#[macro_use]
mod macros;

pub use self::crc::{crc8, crc16};

// Convert one to four byte slices into an unsigned 32-bit number.
//
// NOTE: This assumes big-endian since most numbers in the FLAC binary are
// that endianness.
#[inline]
pub fn to_u32(bytes: &[u8]) -> u32 {
  let length = bytes.len();

  debug_assert!(length <= 4);

  (0..length).fold(0, |result, i|
    result + ((bytes[i] as u32) << ((length - 1 - i) * 8))
  )
}

pub fn extend_sign(value: u32, bit_count: usize) -> i32 {
  if bit_count >= 32 || value < (1 << (bit_count - 1)) {
    value as i32
  } else {
    (value as i32).wrapping_sub(1 << bit_count)
  }
}

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

  #[test]
  #[should_panic]
  fn test_panic_to_u32() {
    to_u32(&[0x00, 0x00, 0x00, 0x00, 0x00]);
  }

  #[test]
  fn test_to_u32() {
    let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];

    assert_eq!(to_u32(&bytes[0..1]), 0x00000001);
    assert_eq!(to_u32(&bytes[3..5]), 0x00006789);
    assert_eq!(to_u32(&bytes[1..4]), 0x00234567);
    assert_eq!(to_u32(&bytes[4..]), 0x89abcdef);
  }
}
<|endoftext|>


<commit_before>// DOCS

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

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

pub struct Property {
    /// Parameters.
    pub params: HashMap<String, String>,

    /// Value as unparsed string.
    pub raw_value: String,

    /// Property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub prop_group: Option<String>
}

impl Property {
    /// Create property from unescaped string.
    pub fn new(value: &str) -> Property {
        Property {
            params: HashMap::new(),
            raw_value: escape_chars(value),
            prop_group: None
        }
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.raw_value.as_slice())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    pub fn new(name: &str) -> Component {
        Component {
            name: name.to_string(),
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

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

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

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

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

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

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

        rv
    }

component_begin -> &'input str
    = "BEGIN:" v:value __ { v }

component_end -> &'input str
    = "END:" v:value __ { v }

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

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

prop -> (&'input str, Property)
    = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
        (k, Property { params: p, raw_value: v.to_string(), prop_group: g })
    }

group -> String
    = g:group_name "." { g.to_string() }

group_name -> &'input str
    = group_char+ { match_str }

name -> &'input str
    = iana_token+ { match_str }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::with_capacity(ps.len());
        rv.extend(ps.into_iter().map(|(k, v)| (k.to_string(), v.to_string())));
        rv
    }

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

param_name -> &'input str
    = iana_token+ { match_str }

param_value -> &'input str
    = x:(quoted_string / param_text) { x }

param_text -> &'input str
    = safe_char* { match_str }

value -> &'input str
    = value_char+ { match_str }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

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

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+

// Taken from vCard. vCalendar's is a subset. Together with the definition of "qsafe_char" this
// might reject a bunch of valid iCalendars, but I can't imagine one.
ctl = [\u{00}-\u{1F}] / "\u{7F}"

whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &str) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = unfold_lines(s);
    parser::component(unfolded.as_slice())
}

/// Write a component. The error value is a human-readable message.
pub fn write_component(c: &Component) -> String {
    fn inner(buf: &mut String, c: &Component) {
        buf.push_str("BEGIN:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");

        for (prop_name, props) in c.props.iter() {
            for prop in props.iter() {
                match prop.prop_group {
                    Some(ref x) => { buf.push_str(x.as_slice()); buf.push('.'); },
                    None => ()
                };
                buf.push_str(prop_name.as_slice());
                for (param_key, param_value) in prop.params.iter() {
                    buf.push(';');
                    buf.push_str(param_key.as_slice());
                    buf.push('=');
                    buf.push_str(param_value.as_slice());
                };
                buf.push(':');
                buf.push_str(fold_line(prop.raw_value.as_slice()).as_slice());
                buf.push_str("\r\n");
            };
        };

        for subcomponent in c.subcomponents.iter() {
            inner(buf, subcomponent);
        };

        buf.push_str("END:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");
    }

    let mut buf = String::new();
    inner(&mut buf, c);
    buf
}

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

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

/// Unfold contentline.
pub fn unfold_lines(s: &str) -> String {
    s
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r ", "").replace("\r\t", "")
}

/// Fold contentline to 75 chars. This function assumes the input to be unfolded, which means no
/// '\n' or '\r' in it.
pub fn fold_line(s: &str) -> String {
    let mut rv = String::new();
    for (i, c) in s.chars().enumerate() {
        rv.push(c);
        if i != 0 && i % 75 == 0 {
            rv.push_str("\r\n ");
        };
    };
    rv
}
<commit_msg>Indent PEG grammar<commit_after>// DOCS

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

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

pub struct Property {
    /// Parameters.
    pub params: HashMap<String, String>,

    /// Value as unparsed string.
    pub raw_value: String,

    /// Property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub prop_group: Option<String>
}

impl Property {
    /// Create property from unescaped string.
    pub fn new(value: &str) -> Property {
        Property {
            params: HashMap::new(),
            raw_value: escape_chars(value),
            prop_group: None
        }
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.raw_value.as_slice())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    pub fn new(name: &str) -> Component {
        Component {
            name: name.to_string(),
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

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

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

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

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

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

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

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

            rv
        }

    component_begin -> &'input str
        = "BEGIN:" v:value __ { v }

    component_end -> &'input str
        = "END:" v:value __ { v }

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

    prop -> (&'input str, Property)
        = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
            (k, Property { params: p, raw_value: v.to_string(), prop_group: g })
        }

    group -> String
        = g:group_name "." { g.to_string() }

        group_name -> &'input str
            = group_char+ { match_str }

    name -> &'input str
        = iana_token+ { match_str }

    params -> HashMap<String, String>
        = ps:(";" p:param {p})* {
            let mut rv: HashMap<String, String> = HashMap::with_capacity(ps.len());
            rv.extend(ps.into_iter().map(|(k, v)| (k.to_string(), v.to_string())));
            rv
        }

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

        param_name -> &'input str
            = iana_token+ { match_str }

        param_value -> &'input str
            = x:(quoted_string / param_text) { x }

        param_text -> &'input str
            = safe_char* { match_str }

    value -> &'input str
        = value_char+ { match_str }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

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

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+

// Taken from vCard. vCalendar's is a subset. Together with the definition of "qsafe_char" this
// might reject a bunch of valid iCalendars, but I can't imagine one.
ctl = [\u{00}-\u{1F}] / "\u{7F}"

whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &str) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = unfold_lines(s);
    parser::component(unfolded.as_slice())
}

/// Write a component. The error value is a human-readable message.
pub fn write_component(c: &Component) -> String {
    fn inner(buf: &mut String, c: &Component) {
        buf.push_str("BEGIN:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");

        for (prop_name, props) in c.props.iter() {
            for prop in props.iter() {
                match prop.prop_group {
                    Some(ref x) => { buf.push_str(x.as_slice()); buf.push('.'); },
                    None => ()
                };
                buf.push_str(prop_name.as_slice());
                for (param_key, param_value) in prop.params.iter() {
                    buf.push(';');
                    buf.push_str(param_key.as_slice());
                    buf.push('=');
                    buf.push_str(param_value.as_slice());
                };
                buf.push(':');
                buf.push_str(fold_line(prop.raw_value.as_slice()).as_slice());
                buf.push_str("\r\n");
            };
        };

        for subcomponent in c.subcomponents.iter() {
            inner(buf, subcomponent);
        };

        buf.push_str("END:");
        buf.push_str(c.name.as_slice());
        buf.push_str("\r\n");
    }

    let mut buf = String::new();
    inner(&mut buf, c);
    buf
}

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

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

/// Unfold contentline.
pub fn unfold_lines(s: &str) -> String {
    s
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r ", "").replace("\r\t", "")
}

/// Fold contentline to 75 chars. This function assumes the input to be unfolded, which means no
/// '\n' or '\r' in it.
pub fn fold_line(s: &str) -> String {
    let mut rv = String::new();
    for (i, c) in s.chars().enumerate() {
        rv.push(c);
        if i != 0 && i % 75 == 0 {
            rv.push_str("\r\n ");
        };
    };
    rv
}
<|endoftext|>