<commit_before>// 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.

// Parser nodes represent the grammar that is defined
// by the currently permissible set of commands and their
// parameters.

use std::rc::Rc;

use super::{Completion, Parser};
use super::constants::*;
use tokenizer::Token;

/// Enumeration of node types used to have vectors of `Node` and so on.
pub enum Node {
    /// `Node` variant wrapping a `CommandNode`.
    Command(CommandNode),
    /// `Node` variant wrapping a `ParameterNode`.
    Parameter(ParameterNode),
    /// `Node` variant wrapping a `ParameterNameNode`.
    ParameterName(ParameterNameNode),
    /// `Node` variant wrapping a `RootNode`.
    Root(RootNode),
}

/// The operations that every node must implement.
pub trait NodeOps {
    /// Accept this node with the given `token` as data.
    ///
    /// By default, nothing needs to happen for `accept`.
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token);

    /// Can this node be accepted in the current parser state?
    /// By default, a node can be accepted when it hasn't been seen yet.
    ///
    /// The `node_ref` is provided so that implementations have access to
    /// the `Rc<Node>` value for the node rather than having to rely upon
    /// `self` which won't be a `Node`, but the underlying `CommandNode` or
    /// similar.
    fn acceptable(&self, _parser: &Parser, _node_ref: &Rc<Node>) -> bool;

    /// Given a node and an optional token, provide the completion options.
    ///
    /// By default, completion should complete for the name of the given
    /// node.
    ///
    /// This is the expected behavior for [`CommandNode`] as well as
    /// [`ParameterNameNode`].
    ///
    /// [`CommandNode`]: struct.CommandNode.html
    /// [`ParameterNameNode`]: struct.ParameterNameNode.html
    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text>;

    /// By default, a node matches a `token` when the name of the
    /// node starts with the `token`.
    ///
    /// This is the desired behavior for ...
    fn matches(&self, _parser: &Parser, token: Token) -> bool;
}

/// A parse tree node.
pub struct TreeNode {
    /// The name of this node.
    pub name: String,
    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    pub help_symbol: String,
    /// Help text describing this node.
    pub help_text: String,
    /// Hidden nodes are not completed. This doesn't modify matching.
    pub hidden: bool,
    /// Match and complete priority.
    pub priority: i32,
    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    pub repeatable: bool,
    /// If present, this node will no longer be `acceptable`.
    pub repeat_marker: Option<Rc<Node>>,
    /// Possible successor nodes. Collected while building.
    pub successors: Vec<Rc<Node>>,
}

/// The root of a command tree.
pub struct RootNode {
    /// `TreeNode` data.
    pub node: TreeNode,
}

/// A node representing a command. Constructed via [`Command`] and [`CommandTree`].
///
/// If `wrapped_root` is set then this node wraps another command.
/// This is used for the help command so that it can complete
/// normal commands. The `successors` will be those of the wrapped node.
///
/// [`Command`]: struct.Command.html
/// [`CommandTree`]: struct.CommandTree.html
pub struct CommandNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// The handler which is executed once this node has been accepted.
    pub handler: Option<fn(&node: Node) -> ()>,
    /// Parameter nodes for this command
    ///
    /// XXX: This should be Vec<Rc<ParameterNode>> or similar.
    pub parameters: Vec<Rc<Node>>,
    /// If present, the command wrapped by this node.
    pub wrapped_root: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// The `parameter` named by this node.
    ///
    /// XXX: This should be Rc<ParameterNode> or similar.
    pub parameter: Rc<Node>,
}

/// A node representing a parameter for a command.
pub struct ParameterNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    pub required: bool,
    /// What type of `ParameterKind` this is.
    pub kind: ParameterKind,
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// The node in the tree of commands and parameters used in the
/// parser.
///
/// This trait defines the core operations which a node must
/// support.
impl Node {
    /// Get the `TreeNode` data for a given `Node`.
    pub fn node(&self) -> &TreeNode {
        match *self {
            Node::Command(ref command) => &command.node,
            Node::Parameter(ref parameter) => &parameter.node,
            Node::ParameterName(ref name) => &name.node,
            Node::Root(ref root) => &root.node,
        }
    }

    /// Get or calculate successors of this node.
    pub fn successors(&self) -> &Vec<Rc<Node>> {
        match *self {
            Node::Root(ref root) => &root.node.successors,
            _ => &self.node().successors,
        }
    }
}

impl NodeOps for Node {
    fn accept<'text>(&self, parser: &mut Parser<'text>, token: Token) {
        match *self {
            Node::Command(ref command) => command.accept(parser, token),
            Node::Parameter(ref parameter) => parameter.accept(parser, token),
            Node::ParameterName(ref name) => name.accept(parser, token),
            Node::Root(ref root) => root.accept(parser, token),
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        match *self {
            Node::Command(ref command) => command.acceptable(parser, node_ref),
            Node::Parameter(ref parameter) => parameter.acceptable(parser, node_ref),
            Node::ParameterName(ref name) => name.acceptable(parser, node_ref),
            Node::Root(ref root) => root.acceptable(parser, node_ref),
        }
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        match *self {
            Node::Command(ref command) => command.complete(token),
            Node::Parameter(ref parameter) => parameter.complete(token),
            Node::ParameterName(ref name) => name.complete(token),
            Node::Root(ref root) => root.complete(token),
        }
    }

    fn matches(&self, parser: &Parser, token: Token) -> bool {
        match *self {
            Node::Command(ref command) => command.matches(parser, token),
            Node::Parameter(ref parameter) => parameter.matches(parser, token),
            Node::ParameterName(ref name) => name.matches(parser, token),
            Node::Root(ref root) => root.matches(parser, token),
        }
    }
}

impl RootNode {
    /// Create a new `RootNode`
    pub fn new(successors: Vec<Rc<Node>>) -> Self {
        RootNode {
            node: TreeNode {
                name: "__root__".to_string(),
                help_symbol: "".to_string(),
                help_text: "".to_string(),
                hidden: false,
                priority: PRIORITY_DEFAULT,
                repeat_marker: None,
                repeatable: false,
                successors: successors,
            },
        }
    }
}

impl NodeOps for RootNode {
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {}

    fn acceptable(&self, _parser: &Parser, _node_ref: &Rc<Node>) -> bool {
        false
    }

    /// A `RootNode` can not be completed.
    fn complete<'text>(&self, _token: Option<Token<'text>>) -> Completion<'text> {
        panic!("BUG: Can not complete a root node.");
    }

    fn matches(&self, _parser: &Parser, _token: Token) -> bool {
        panic!("BUG: Can not match a root node.");
    }
}

impl CommandNode {
    /// Construct a new `CommandNode`.
    pub fn new(name: &str,
               help_text: Option<&str>,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               handler: Option<fn(&node: Node) -> ()>,
               parameters: Vec<Rc<Node>>)
               -> Self {
        CommandNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: name.to_string(),
                help_text: help_text.unwrap_or("Command").to_string(),
                hidden: hidden,
                priority: priority,
                repeat_marker: None,
                repeatable: false,
                successors: successors,
            },
            handler: handler,
            parameters: parameters,
            wrapped_root: None,
        }
    }
}

impl NodeOps for CommandNode {
    /// Record this command.
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {
        if self.handler.is_some() {
            unimplemented!();
            // parser.commands.push(self)
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        !parser.nodes.contains(node_ref)
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        Completion::new(self.node.help_symbol.clone(),
                        self.node.help_text.clone(),
                        token,
                        true,
                        vec![&self.node.name],
                        vec![])
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.node.name.starts_with(token.text)
    }
}

impl ParameterNameNode {
    /// Construct a new `ParameterNameNode`.
    pub fn new(name: &str,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               repeatable: bool,
               repeat_marker: Option<Rc<Node>>,
               parameter: Rc<Node>)
               -> Self {
        let param_node = &parameter.node();
        let help_text = param_node.help_text.clone();
        let help_symbol = name.to_string() + " " + param_node.help_symbol.as_str();
        ParameterNameNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: help_symbol,
                help_text: help_text,
                hidden: hidden,
                priority: priority,
                repeat_marker: repeat_marker,
                repeatable: repeatable,
                successors: successors,
            },
            parameter: parameter.clone(),
        }
    }
}

impl NodeOps for ParameterNameNode {
    /// Record this command.
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {}

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        if self.node.repeatable {
            return true;
        }
        !parser.nodes.contains(node_ref) &&
        match self.node.repeat_marker {
            None => true,
            Some(ref n) => !parser.nodes.contains(n),
        }
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        Completion::new(self.node.help_symbol.clone(),
                        self.node.help_text.clone(),
                        token,
                        true,
                        vec![&self.node.name],
                        vec![])
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.node.name.starts_with(token.text)
    }
}

impl ParameterNode {
    /// Construct a new `ParameterNode`.
    pub fn new(name: &str,
               help_text: Option<&str>,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               repeatable: bool,
               repeat_marker: Option<Rc<Node>>,
               kind: ParameterKind,
               required: bool)
               -> Self {
        let help_symbol = if repeatable {
            String::from("<") + name + ">..."
        } else {
            String::from("<") + name + ">"
        };
        let default_help_text = match kind {
            ParameterKind::Flag => "Flag",
            ParameterKind::Named | ParameterKind::Simple => "Parameter",
        };
        let help_text = help_text.unwrap_or(default_help_text).to_string();
        ParameterNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: help_symbol,
                help_text: help_text,
                hidden: hidden,
                priority: priority,
                repeat_marker: repeat_marker,
                repeatable: repeatable,
                successors: successors,
            },
            kind: kind,
            required: required,
        }
    }
}

impl NodeOps for ParameterNode {
    /// Record this parameter value.
    fn accept<'text>(&self, parser: &mut Parser<'text>, token: Token) {
        if self.node.repeatable {
            unimplemented!();
        } else {
            parser.parameters.insert(self.node.name.clone(), token.text.to_string());
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        if self.node.repeatable {
            return true;
        }
        !parser.nodes.contains(node_ref) &&
        match self.node.repeat_marker {
            None => true,
            Some(ref n) => !parser.nodes.contains(n),
        }
    }

    /// By default named and simple parameters complete only to the token
    /// being input while flag parameters complete to the name of the flag.
    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        match self.kind {
            ParameterKind::Named | ParameterKind::Simple => {
                Completion::new(self.node.help_symbol.clone(),
                                self.node.help_text.clone(),
                                token,
                                true,
                                vec![],
                                vec![])
            }
            ParameterKind::Flag => {
                Completion::new(self.node.help_symbol.clone(),
                                self.node.help_text.clone(),
                                token,
                                true,
                                vec![&self.node.name],
                                vec![])
            }
        }
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        match self.kind {
            ParameterKind::Named | ParameterKind::Simple => true,
            ParameterKind::Flag => self.node.name.starts_with(token.text),
        }
    }
}
<commit_msg>Remove _ from trait fn signature parameters.<commit_after>// 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.

// Parser nodes represent the grammar that is defined
// by the currently permissible set of commands and their
// parameters.

use std::rc::Rc;

use super::{Completion, Parser};
use super::constants::*;
use tokenizer::Token;

/// Enumeration of node types used to have vectors of `Node` and so on.
pub enum Node {
    /// `Node` variant wrapping a `CommandNode`.
    Command(CommandNode),
    /// `Node` variant wrapping a `ParameterNode`.
    Parameter(ParameterNode),
    /// `Node` variant wrapping a `ParameterNameNode`.
    ParameterName(ParameterNameNode),
    /// `Node` variant wrapping a `RootNode`.
    Root(RootNode),
}

/// The operations that every node must implement.
pub trait NodeOps {
    /// Accept this node with the given `token` as data.
    ///
    /// By default, nothing needs to happen for `accept`.
    fn accept<'text>(&self, parser: &mut Parser<'text>, token: Token);

    /// Can this node be accepted in the current parser state?
    /// By default, a node can be accepted when it hasn't been seen yet.
    ///
    /// The `node_ref` is provided so that implementations have access to
    /// the `Rc<Node>` value for the node rather than having to rely upon
    /// `self` which won't be a `Node`, but the underlying `CommandNode` or
    /// similar.
    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool;

    /// Given a node and an optional token, provide the completion options.
    ///
    /// By default, completion should complete for the name of the given
    /// node.
    ///
    /// This is the expected behavior for [`CommandNode`] as well as
    /// [`ParameterNameNode`].
    ///
    /// [`CommandNode`]: struct.CommandNode.html
    /// [`ParameterNameNode`]: struct.ParameterNameNode.html
    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text>;

    /// By default, a node matches a `token` when the name of the
    /// node starts with the `token`.
    ///
    /// This is the desired behavior for ...
    fn matches(&self, parser: &Parser, token: Token) -> bool;
}

/// A parse tree node.
pub struct TreeNode {
    /// The name of this node.
    pub name: String,
    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    pub help_symbol: String,
    /// Help text describing this node.
    pub help_text: String,
    /// Hidden nodes are not completed. This doesn't modify matching.
    pub hidden: bool,
    /// Match and complete priority.
    pub priority: i32,
    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    pub repeatable: bool,
    /// If present, this node will no longer be `acceptable`.
    pub repeat_marker: Option<Rc<Node>>,
    /// Possible successor nodes. Collected while building.
    pub successors: Vec<Rc<Node>>,
}

/// The root of a command tree.
pub struct RootNode {
    /// `TreeNode` data.
    pub node: TreeNode,
}

/// A node representing a command. Constructed via [`Command`] and [`CommandTree`].
///
/// If `wrapped_root` is set then this node wraps another command.
/// This is used for the help command so that it can complete
/// normal commands. The `successors` will be those of the wrapped node.
///
/// [`Command`]: struct.Command.html
/// [`CommandTree`]: struct.CommandTree.html
pub struct CommandNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// The handler which is executed once this node has been accepted.
    pub handler: Option<fn(&node: Node) -> ()>,
    /// Parameter nodes for this command
    ///
    /// XXX: This should be Vec<Rc<ParameterNode>> or similar.
    pub parameters: Vec<Rc<Node>>,
    /// If present, the command wrapped by this node.
    pub wrapped_root: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// The `parameter` named by this node.
    ///
    /// XXX: This should be Rc<ParameterNode> or similar.
    pub parameter: Rc<Node>,
}

/// A node representing a parameter for a command.
pub struct ParameterNode {
    /// `TreeNode` data.
    pub node: TreeNode,
    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    pub required: bool,
    /// What type of `ParameterKind` this is.
    pub kind: ParameterKind,
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// The node in the tree of commands and parameters used in the
/// parser.
///
/// This trait defines the core operations which a node must
/// support.
impl Node {
    /// Get the `TreeNode` data for a given `Node`.
    pub fn node(&self) -> &TreeNode {
        match *self {
            Node::Command(ref command) => &command.node,
            Node::Parameter(ref parameter) => &parameter.node,
            Node::ParameterName(ref name) => &name.node,
            Node::Root(ref root) => &root.node,
        }
    }

    /// Get or calculate successors of this node.
    pub fn successors(&self) -> &Vec<Rc<Node>> {
        match *self {
            Node::Root(ref root) => &root.node.successors,
            _ => &self.node().successors,
        }
    }
}

impl NodeOps for Node {
    fn accept<'text>(&self, parser: &mut Parser<'text>, token: Token) {
        match *self {
            Node::Command(ref command) => command.accept(parser, token),
            Node::Parameter(ref parameter) => parameter.accept(parser, token),
            Node::ParameterName(ref name) => name.accept(parser, token),
            Node::Root(ref root) => root.accept(parser, token),
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        match *self {
            Node::Command(ref command) => command.acceptable(parser, node_ref),
            Node::Parameter(ref parameter) => parameter.acceptable(parser, node_ref),
            Node::ParameterName(ref name) => name.acceptable(parser, node_ref),
            Node::Root(ref root) => root.acceptable(parser, node_ref),
        }
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        match *self {
            Node::Command(ref command) => command.complete(token),
            Node::Parameter(ref parameter) => parameter.complete(token),
            Node::ParameterName(ref name) => name.complete(token),
            Node::Root(ref root) => root.complete(token),
        }
    }

    fn matches(&self, parser: &Parser, token: Token) -> bool {
        match *self {
            Node::Command(ref command) => command.matches(parser, token),
            Node::Parameter(ref parameter) => parameter.matches(parser, token),
            Node::ParameterName(ref name) => name.matches(parser, token),
            Node::Root(ref root) => root.matches(parser, token),
        }
    }
}

impl RootNode {
    /// Create a new `RootNode`
    pub fn new(successors: Vec<Rc<Node>>) -> Self {
        RootNode {
            node: TreeNode {
                name: "__root__".to_string(),
                help_symbol: "".to_string(),
                help_text: "".to_string(),
                hidden: false,
                priority: PRIORITY_DEFAULT,
                repeat_marker: None,
                repeatable: false,
                successors: successors,
            },
        }
    }
}

impl NodeOps for RootNode {
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {}

    fn acceptable(&self, _parser: &Parser, _node_ref: &Rc<Node>) -> bool {
        false
    }

    /// A `RootNode` can not be completed.
    fn complete<'text>(&self, _token: Option<Token<'text>>) -> Completion<'text> {
        panic!("BUG: Can not complete a root node.");
    }

    fn matches(&self, _parser: &Parser, _token: Token) -> bool {
        panic!("BUG: Can not match a root node.");
    }
}

impl CommandNode {
    /// Construct a new `CommandNode`.
    pub fn new(name: &str,
               help_text: Option<&str>,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               handler: Option<fn(&node: Node) -> ()>,
               parameters: Vec<Rc<Node>>)
               -> Self {
        CommandNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: name.to_string(),
                help_text: help_text.unwrap_or("Command").to_string(),
                hidden: hidden,
                priority: priority,
                repeat_marker: None,
                repeatable: false,
                successors: successors,
            },
            handler: handler,
            parameters: parameters,
            wrapped_root: None,
        }
    }
}

impl NodeOps for CommandNode {
    /// Record this command.
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {
        if self.handler.is_some() {
            unimplemented!();
            // parser.commands.push(self)
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        !parser.nodes.contains(node_ref)
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        Completion::new(self.node.help_symbol.clone(),
                        self.node.help_text.clone(),
                        token,
                        true,
                        vec![&self.node.name],
                        vec![])
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.node.name.starts_with(token.text)
    }
}

impl ParameterNameNode {
    /// Construct a new `ParameterNameNode`.
    pub fn new(name: &str,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               repeatable: bool,
               repeat_marker: Option<Rc<Node>>,
               parameter: Rc<Node>)
               -> Self {
        let param_node = &parameter.node();
        let help_text = param_node.help_text.clone();
        let help_symbol = name.to_string() + " " + param_node.help_symbol.as_str();
        ParameterNameNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: help_symbol,
                help_text: help_text,
                hidden: hidden,
                priority: priority,
                repeat_marker: repeat_marker,
                repeatable: repeatable,
                successors: successors,
            },
            parameter: parameter.clone(),
        }
    }
}

impl NodeOps for ParameterNameNode {
    /// Record this command.
    fn accept<'text>(&self, _parser: &mut Parser<'text>, _token: Token) {}

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        if self.node.repeatable {
            return true;
        }
        !parser.nodes.contains(node_ref) &&
        match self.node.repeat_marker {
            None => true,
            Some(ref n) => !parser.nodes.contains(n),
        }
    }

    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        Completion::new(self.node.help_symbol.clone(),
                        self.node.help_text.clone(),
                        token,
                        true,
                        vec![&self.node.name],
                        vec![])
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.node.name.starts_with(token.text)
    }
}

impl ParameterNode {
    /// Construct a new `ParameterNode`.
    pub fn new(name: &str,
               help_text: Option<&str>,
               hidden: bool,
               priority: i32,
               successors: Vec<Rc<Node>>,
               repeatable: bool,
               repeat_marker: Option<Rc<Node>>,
               kind: ParameterKind,
               required: bool)
               -> Self {
        let help_symbol = if repeatable {
            String::from("<") + name + ">..."
        } else {
            String::from("<") + name + ">"
        };
        let default_help_text = match kind {
            ParameterKind::Flag => "Flag",
            ParameterKind::Named | ParameterKind::Simple => "Parameter",
        };
        let help_text = help_text.unwrap_or(default_help_text).to_string();
        ParameterNode {
            node: TreeNode {
                name: name.to_string(),
                help_symbol: help_symbol,
                help_text: help_text,
                hidden: hidden,
                priority: priority,
                repeat_marker: repeat_marker,
                repeatable: repeatable,
                successors: successors,
            },
            kind: kind,
            required: required,
        }
    }
}

impl NodeOps for ParameterNode {
    /// Record this parameter value.
    fn accept<'text>(&self, parser: &mut Parser<'text>, token: Token) {
        if self.node.repeatable {
            unimplemented!();
        } else {
            parser.parameters.insert(self.node.name.clone(), token.text.to_string());
        }
    }

    fn acceptable(&self, parser: &Parser, node_ref: &Rc<Node>) -> bool {
        if self.node.repeatable {
            return true;
        }
        !parser.nodes.contains(node_ref) &&
        match self.node.repeat_marker {
            None => true,
            Some(ref n) => !parser.nodes.contains(n),
        }
    }

    /// By default named and simple parameters complete only to the token
    /// being input while flag parameters complete to the name of the flag.
    fn complete<'text>(&self, token: Option<Token<'text>>) -> Completion<'text> {
        match self.kind {
            ParameterKind::Named | ParameterKind::Simple => {
                Completion::new(self.node.help_symbol.clone(),
                                self.node.help_text.clone(),
                                token,
                                true,
                                vec![],
                                vec![])
            }
            ParameterKind::Flag => {
                Completion::new(self.node.help_symbol.clone(),
                                self.node.help_text.clone(),
                                token,
                                true,
                                vec![&self.node.name],
                                vec![])
            }
        }
    }

    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        match self.kind {
            ParameterKind::Named | ParameterKind::Simple => true,
            ParameterKind::Flag => self.node.name.starts_with(token.text),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::TreeMap;
use time::{get_time, Timespec};

use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator
};
use message::{
    IrcMessage
};

static MAX_USER_RECORDS_KEPT: uint = 5;

pub struct SeenRecord {
    when: Timespec,
    message: IrcMessage
}

impl SeenRecord {
    fn new(when: Timespec, message: IrcMessage) -> SeenRecord {
        SeenRecord {
            when: when,
            message: message
        }
    }

    fn with_now(message: IrcMessage) -> SeenRecord {
        SeenRecord::new(get_time(), message)
    }
}

pub struct SeenPlugin {
    map: TreeMap<String, Vec<SeenRecord>>,
}

impl SeenPlugin {
    pub fn new() -> SeenPlugin {
        SeenPlugin {
            map: TreeMap::new()
        }
    }
}

fn trim_vec<T>(vec: Vec<T>) -> Vec<T> {
    if vec.len() <= MAX_USER_RECORDS_KEPT {
        return vec;
    }
    let excess_elem = vec.len() - MAX_USER_RECORDS_KEPT;
    vec.into_iter().skip(excess_elem).collect()
}

fn is_message_interesting(message: &IrcMessage) -> bool {
    match message.command() {
        // "JOIN" => true,
        // "ACTION" => true,
        "PRIVMSG" => true,
        "QUIT" => true,
        _ => false
    }
}

enum SeenCommandType<'a> {
    Seen(&'a str)
}

fn parse_seen<'a>(message: &'a IrcMessage) -> Option<SeenCommandType<'a>> {
    let message_body = message.get_arg(1).as_slice();
    match message_body.find(' ') {
        Some(idx) => Some(Seen(message_body.slice_from(idx + 1))),
        None => None
    }
}

fn parse_command<'a>(m: &CommandMapperDispatch, message: &'a IrcMessage) -> Option<SeenCommandType<'a>> {
    match m.command() {
        Some("seen") => parse_seen(message),
        Some(_) => None,
        None => None
    }
}

fn format_activity(nick: &str, records: &Vec<SeenRecord>) -> String {
    let mut user_has_quit: Option<Timespec> = None;
    let mut prev_message: Option<&SeenRecord> = None;

    for record in records.iter().rev() {
        if record.message.command() == "QUIT" {
            user_has_quit = Some(record.when.clone());
        }
        if record.message.is_privmsg() {
            prev_message = Some(record);
            break;
        }
    }
    let now = get_time();
    match (user_has_quit, prev_message) {
        (Some(when_quit), Some(record)) => {
            let seconds = (now - record.when).num_seconds();
            let quit_seconds = (when_quit - record.when).num_seconds();
            format!("{} said ``{}'' {} seconds ago before quitting {} seconds later",
                nick, record.message.get_arg(1), seconds, quit_seconds)
        },
        (None, Some(record)) => {
            // let message_sent = format_message_sent(message);
            let seconds = (now - record.when).num_seconds();
            format!("{} said ``{}'' {} seconds ago", nick, record.message.get_arg(1), seconds)
        },
        (Some(when_quit), None) => {
            format!("{} quit {} seconds ago", nick, (now - when_quit).num_seconds())
        },
        (None, None) => {
            format!("Sorry, I am very confused about {}", nick)
        }
    }
}

impl RustBotPlugin for SeenPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map("seen");
    }

    fn accept(&mut self, _m: &CommandMapperDispatch, message: &IrcMessage) {
        if !is_message_interesting(message) {
            return;
        }
        let source_nick = match message.source_nick() {
            Some(source_nick) => source_nick,
            None => return
        };
        match self.map.pop(&source_nick) {
            Some(mut records) => {
                records.push(SeenRecord::with_now(message.clone()));
                self.map.insert(source_nick, trim_vec(records));
            },
            None => {
                let records = vec![SeenRecord::with_now(message.clone())];
                self.map.insert(source_nick, records);
            }
        }
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        let source_nick = match message.source_nick() {
            Some(source_nick) => source_nick,
            None => return
        };

        if !message.target_is_channel() {
            return;
        }

        if !message.is_privmsg() {
            return;
        }

        match parse_command(m, message) {
            Some(Seen(target_nick)) => {
                if source_nick.as_slice() == target_nick {
                    m.reply(format!("Looking for yourself, {}?", source_nick));
                    return;
                }

                if m.current_nick() == target_nick {
                    m.reply(format!("You found me, {}!", source_nick));
                    return;
                }
                let target_nick_str = String::from_str(target_nick);
                let activity = match self.map.find(&target_nick_str) {
                    Some(val) => val,
                    None => {
                        m.reply(format!("{} is unknown", target_nick));
                        return
                    }
                };
                m.reply(format_activity(target_nick.as_slice(), activity));
            },
            None => return
        }
    }
}
<commit_msg>more human-consumable duration format<commit_after>use std::collections::TreeMap;
use time::{get_time, Timespec};

use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator
};
use message::{
    IrcMessage
};

static MAX_USER_RECORDS_KEPT: uint = 5;

pub struct SeenRecord {
    when: Timespec,
    message: IrcMessage
}

impl SeenRecord {
    fn new(when: Timespec, message: IrcMessage) -> SeenRecord {
        SeenRecord {
            when: when,
            message: message
        }
    }

    fn with_now(message: IrcMessage) -> SeenRecord {
        SeenRecord::new(get_time(), message)
    }
}

pub struct SeenPlugin {
    map: TreeMap<String, Vec<SeenRecord>>,
}

impl SeenPlugin {
    pub fn new() -> SeenPlugin {
        SeenPlugin {
            map: TreeMap::new()
        }
    }
}

fn trim_vec<T>(vec: Vec<T>) -> Vec<T> {
    if vec.len() <= MAX_USER_RECORDS_KEPT {
        return vec;
    }
    let excess_elem = vec.len() - MAX_USER_RECORDS_KEPT;
    vec.into_iter().skip(excess_elem).collect()
}

fn is_message_interesting(message: &IrcMessage) -> bool {
    match message.command() {
        // "JOIN" => true,
        // "ACTION" => true,
        "PRIVMSG" => true,
        "QUIT" => true,
        _ => false
    }
}

enum SeenCommandType<'a> {
    Seen(&'a str)
}

fn parse_seen<'a>(message: &'a IrcMessage) -> Option<SeenCommandType<'a>> {
    let message_body = message.get_arg(1).as_slice();
    match message_body.find(' ') {
        Some(idx) => Some(Seen(message_body.slice_from(idx + 1))),
        None => None
    }
}

fn parse_command<'a>(m: &CommandMapperDispatch, message: &'a IrcMessage) -> Option<SeenCommandType<'a>> {
    match m.command() {
        Some("seen") => parse_seen(message),
        Some(_) => None,
        None => None
    }
}

fn duration_to_string(dur: Duration) -> String {
    let seconds = ts.num_seconds();

    let days = ts.num_days();
    let hours = ts.num_hours();
    let minutes = ts.num_minutes();

    let mut string = String::new();
    if days > 0 {
        string = string.append(format!("{:d}d", days).as_slice());
    }
    if hours > 0 {
        string = string.append(format!("{:02d}h", hours).as_slice());
    }
    if minutes > 0 {
        string = string.append(format!("{:02d}m", minutes).as_slice());
    }
    if string.len() == 0 || seconds > 0 {
        string = string.append(format!("{:02d}s", seconds).as_slice());
    }
    string
}

fn format_activity(nick: &str, records: &Vec<SeenRecord>) -> String {
    let mut user_has_quit: Option<Timespec> = None;
    let mut prev_message: Option<&SeenRecord> = None;

    for record in records.iter().rev() {
        if record.message.command() == "QUIT" {
            user_has_quit = Some(record.when.clone());
        }
        if record.message.is_privmsg() {
            prev_message = Some(record);
            break;
        }
    }
    let now = get_time();
    match (user_has_quit, prev_message) {
        (Some(when_quit), Some(record)) => {
            format!(
                "{} said ``{}'' {} ago before quitting {} later",
                nick,
                record.message.get_arg(1),
                duration_to_string(now - record.when),
                duration_to_string(when_quit - record.when))
        },
        (None, Some(record)) => {
            // let message_sent = format_message_sent(message);
            format!(
                "{} said ``{}'' {} ago",
                nick,
                record.message.get_arg(1),
                duration_to_string(now - record.when))
        },
        (Some(when_quit), None) => {
            format!(
                "{} quit {} seconds ago",
                nick,
                duration_to_string(now - when_quit))
        },
        (None, None) => {
            format!("Sorry, I am very confused about {}", nick)
        }
    }
}

impl RustBotPlugin for SeenPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map("seen");
    }

    fn accept(&mut self, _m: &CommandMapperDispatch, message: &IrcMessage) {
        if !is_message_interesting(message) {
            return;
        }
        let source_nick = match message.source_nick() {
            Some(source_nick) => source_nick,
            None => return
        };
        match self.map.pop(&source_nick) {
            Some(mut records) => {
                records.push(SeenRecord::with_now(message.clone()));
                self.map.insert(source_nick, trim_vec(records));
            },
            None => {
                let records = vec![SeenRecord::with_now(message.clone())];
                self.map.insert(source_nick, records);
            }
        }
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        let source_nick = match message.source_nick() {
            Some(source_nick) => source_nick,
            None => return
        };

        if !message.target_is_channel() {
            return;
        }

        if !message.is_privmsg() {
            return;
        }

        match parse_command(m, message) {
            Some(Seen(target_nick)) => {
                if source_nick.as_slice() == target_nick {
                    m.reply(format!("Looking for yourself, {}?", source_nick));
                    return;
                }

                if m.current_nick() == target_nick {
                    m.reply(format!("You found me, {}!", source_nick));
                    return;
                }
                let target_nick_str = String::from_str(target_nick);
                let activity = match self.map.find(&target_nick_str) {
                    Some(val) => val,
                    None => {
                        m.reply(format!("{} is unknown", target_nick));
                        return
                    }
                };
                m.reply(format_activity(target_nick.as_slice(), activity));
            },
            None => return
        }
    }
}
<|endoftext|>


<commit_before>use game_map::RoomId;
use sized_enum::SizedEnum;
use direction::Direction;
use game_state::{GameState, GameMode};
use game_map::RoomContents;
use command::{CommandProcessor, HelpInfo};
use items::Item;
use platform;
use util;

use self::PrimaryCommand::*;

const MIN_STRENGTH_WARNING: i32 = 10;

#[derive(Debug)]
pub enum PrimaryCommand {
  Go(Direction),
  Inventory,
  Look,
  EatFood,
  MagicAmulet,
  PickUpTreasure,
  Quit,

  #[cfg(debug_assertions)]
  Debug,
}

impl CommandProcessor<PrimaryCommand> for PrimaryCommand {
  fn get_help() -> Vec<HelpInfo> {
    HelpInfo::list(vec![
      ('n', "go north"),
      ('s', "go south"),
      ('e', "go east"),
      ('w', "go west"),
      ('u', "go up"),
      ('d', "go down"),
      ('c', "consume food"),
      ('m', "use magic amulet (if equipped)"),
      ('i', "inventory/buy provisions"),
      ('p', "pick up treasure"),
      ('l', "look around"),
      ('q', "quit"),
    ])
  }

  fn from_char(c: char) -> Option<PrimaryCommand> {
    match c {
      'n' => Some(Go(Direction::North)),
      's' => Some(Go(Direction::South)),
      'e' => Some(Go(Direction::East)),
      'w' => Some(Go(Direction::West)),
      'u' => Some(Go(Direction::Up)),
      'd' => Some(Go(Direction::Down)),
      'c' => Some(EatFood),
      'm' => Some(MagicAmulet),
      'i' => Some(Inventory),
      'p' => Some(PickUpTreasure),
      'l' => Some(Look),
      'q' => Some(Quit),

      #[cfg(debug_assertions)]
      '`' => Some(Debug),

      _ => None,
    }
  }
}

impl GameState {
  fn print_status_report(&self) {
    if self.strength <= MIN_STRENGTH_WARNING {
      println!("Warning, {}! Your strength is running low.\n",
               self.player_name);
    }
    println!("{}, your strength is {}.", self.player_name, self.strength);
    self.print_wealth();
    if self.food > 0 { self.print_food(); }
    if self.suit {
      println!("You are wearing armor.");
    }
    let item_names = self.get_item_names();
    if item_names.len() > 0 {
      println!("You are carrying {}.",
               util::friendly_join(self.get_item_names()));
    }
  }

  fn get_item_names(&self) -> Vec<&str> {
    let mut items = Vec::new();
    if self.axe { items.push(Item::Axe.as_str()) }
    if self.sword { items.push(Item::Sword.as_str()) }
    if self.amulet { items.push(Item::Amulet.as_str()) }
    items
  }

  fn use_amulet(&mut self) {
    if self.amulet {
      platform::writeln_with_wrapping(
        "You invoke the magic amulet and are whisked \
         away to somewhere else..."
      );
      Self::pause();
      loop {
        let room_id = RoomId::random();
        if room_id != self.curr_room {
          self.curr_room = room_id;
          self.show_desc = true;
          self.process_move();
          break;
        }
      }
    } else {
      println!("You don't have the amulet, {}.", self.player_name);
    }
  }

  fn describe_room(&self) {
    let room = self.map.room(self.curr_room);
    platform::writeln_with_wrapping(room.description);
    if let Some(RoomContents::Treasure(amount)) = room.contents {
      println!("\nThere is treasure here worth ${}.", amount);
    }
  }

  fn process_cmd(&mut self, cmd: PrimaryCommand) {
    match cmd {
      Go(dir) => {
        if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
          self.curr_room = room;
          self.show_desc = true;
          self.process_move();
        } else {
          println!("You can't go that way.");
        }
      },
      Inventory => { self.set_mode(GameMode::Inventory) },
      PickUpTreasure => {
        if !self.can_player_see() {
          println!("It's too dark to see any treasure here.");
        } else if let Some(RoomContents::Treasure(amt)) =
            self.map.room(self.curr_room).contents {
          println!("You are now ${} richer.", amt);
          self.wealth += amt as i32;
          self.map.mut_room(self.curr_room).contents = None;
          self.process_move();
        } else {
          println!("There is no treasure to pick up here.");
        }
      },
      Look => { self.show_desc = true },
      EatFood => {
        if self.food == 0 {
          println!("You have no food!");
        } else {
          self.set_mode(GameMode::EatFood);
        }
      },
      MagicAmulet => { self.use_amulet() },
      Quit => { self.finish_game() },

      #[cfg(debug_assertions)]
      Debug => { self.set_mode(GameMode::Debug) },
    }
  }

  pub fn tick_primary_mode(&mut self) {
    if self.show_desc {
      match self.curr_room {
        RoomId::Lift => {
          println!("You have entered the lift...");
          Self::pause();
          println!("It slowly descends...");
          Self::pause();
          self.curr_room = RoomId::RearVestibule;
          return;
        },
        RoomId::Exit => {
          println!("\nYou've done it!!");
          Self::pause();
          println!("That was the exit from the castle.");
          Self::pause();
          println!("\nYou have succeeded, {}!", self.player_name);
          println!("\nYou managed to get out of the castle.");
          Self::pause();
          println!("\nWell done!");
          Self::pause();
          self.finish_game();
          return;
        },
        _ => {
          platform::clear_screen();
          self.print_status_report();
          println!("");
          if !self.can_player_see() {
            println!("It is too dark to see anything.");
          } else {
            self.describe_room();
            if self.maybe_start_combat() {
              return;
            }
          }
          if !self.shown_hint {
            println!("\n(You can press 'h' for help at any time.)");
            self.shown_hint = true;
          }
          println!("");
        }
      }
      self.show_desc = false;
    }

    self.read_input(|state, input| {
      if let Some(cmd) = PrimaryCommand::get_from_input(input) {
        state.process_cmd(cmd);
      };
    });
  }
}
<commit_msg>Oops, fix prompt.<commit_after>use game_map::RoomId;
use sized_enum::SizedEnum;
use direction::Direction;
use game_state::{GameState, GameMode};
use game_map::RoomContents;
use command::{CommandProcessor, HelpInfo};
use items::Item;
use platform;
use util;

use self::PrimaryCommand::*;

const MIN_STRENGTH_WARNING: i32 = 10;

#[derive(Debug)]
pub enum PrimaryCommand {
  Go(Direction),
  Inventory,
  Look,
  EatFood,
  MagicAmulet,
  PickUpTreasure,
  Quit,

  #[cfg(debug_assertions)]
  Debug,
}

impl CommandProcessor<PrimaryCommand> for PrimaryCommand {
  fn get_help() -> Vec<HelpInfo> {
    HelpInfo::list(vec![
      ('n', "go north"),
      ('s', "go south"),
      ('e', "go east"),
      ('w', "go west"),
      ('u', "go up"),
      ('d', "go down"),
      ('c', "consume food"),
      ('m', "use magic amulet (if equipped)"),
      ('i', "inventory/buy provisions"),
      ('p', "pick up treasure"),
      ('l', "look around"),
      ('q', "quit"),
    ])
  }

  fn from_char(c: char) -> Option<PrimaryCommand> {
    match c {
      'n' => Some(Go(Direction::North)),
      's' => Some(Go(Direction::South)),
      'e' => Some(Go(Direction::East)),
      'w' => Some(Go(Direction::West)),
      'u' => Some(Go(Direction::Up)),
      'd' => Some(Go(Direction::Down)),
      'c' => Some(EatFood),
      'm' => Some(MagicAmulet),
      'i' => Some(Inventory),
      'p' => Some(PickUpTreasure),
      'l' => Some(Look),
      'q' => Some(Quit),

      #[cfg(debug_assertions)]
      '`' => Some(Debug),

      _ => None,
    }
  }
}

impl GameState {
  fn print_status_report(&self) {
    if self.strength <= MIN_STRENGTH_WARNING {
      println!("Warning, {}! Your strength is running low.\n",
               self.player_name);
    }
    println!("{}, your strength is {}.", self.player_name, self.strength);
    self.print_wealth();
    if self.food > 0 { self.print_food(); }
    if self.suit {
      println!("You are wearing armor.");
    }
    let item_names = self.get_item_names();
    if item_names.len() > 0 {
      println!("You are carrying {}.",
               util::friendly_join(self.get_item_names()));
    }
  }

  fn get_item_names(&self) -> Vec<&str> {
    let mut items = Vec::new();
    if self.axe { items.push(Item::Axe.as_str()) }
    if self.sword { items.push(Item::Sword.as_str()) }
    if self.amulet { items.push(Item::Amulet.as_str()) }
    items
  }

  fn use_amulet(&mut self) {
    if self.amulet {
      platform::writeln_with_wrapping(
        "You invoke the magic amulet and are whisked \
         away to somewhere else..."
      );
      Self::pause();
      loop {
        let room_id = RoomId::random();
        if room_id != self.curr_room {
          self.curr_room = room_id;
          self.show_desc = true;
          self.process_move();
          break;
        }
      }
    } else {
      println!("You don't have the amulet, {}.", self.player_name);
    }
  }

  fn describe_room(&self) {
    let room = self.map.room(self.curr_room);
    platform::writeln_with_wrapping(room.description);
    if let Some(RoomContents::Treasure(amount)) = room.contents {
      println!("\nThere is treasure here worth ${}.", amount);
    }
  }

  fn process_cmd(&mut self, cmd: PrimaryCommand) {
    match cmd {
      Go(dir) => {
        if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
          self.curr_room = room;
          self.show_desc = true;
          self.process_move();
        } else {
          println!("You can't go that way.");
        }
      },
      Inventory => { self.set_mode(GameMode::Inventory) },
      PickUpTreasure => {
        if !self.can_player_see() {
          println!("It's too dark to see any treasure here.");
        } else if let Some(RoomContents::Treasure(amt)) =
            self.map.room(self.curr_room).contents {
          println!("You are now ${} richer.", amt);
          self.wealth += amt as i32;
          self.map.mut_room(self.curr_room).contents = None;
          self.process_move();
        } else {
          println!("There is no treasure to pick up here.");
        }
      },
      Look => { self.show_desc = true },
      EatFood => {
        if self.food == 0 {
          println!("You have no food!");
        } else {
          self.set_mode(GameMode::EatFood);
        }
      },
      MagicAmulet => { self.use_amulet() },
      Quit => { self.finish_game() },

      #[cfg(debug_assertions)]
      Debug => { self.set_mode(GameMode::Debug) },
    }
  }

  pub fn tick_primary_mode(&mut self) {
    if self.show_desc {
      match self.curr_room {
        RoomId::Lift => {
          println!("You have entered the lift...");
          Self::pause();
          println!("It slowly descends...");
          Self::pause();
          self.curr_room = RoomId::RearVestibule;
          return;
        },
        RoomId::Exit => {
          println!("\nYou've done it!!");
          Self::pause();
          println!("That was the exit from the castle.");
          Self::pause();
          println!("\nYou have succeeded, {}!", self.player_name);
          println!("\nYou managed to get out of the castle.");
          Self::pause();
          println!("\nWell done!");
          Self::pause();
          self.finish_game();
          return;
        },
        _ => {
          platform::clear_screen();
          self.print_status_report();
          println!("");
          if !self.can_player_see() {
            println!("It is too dark to see anything.");
          } else {
            self.describe_room();
            if self.maybe_start_combat() {
              return;
            }
          }
          if !self.shown_hint {
            println!("\n(You can press 'h' for help at any time.)");
            self.shown_hint = true;
          }
          println!("");
        }
      }
      self.show_desc = false;
    }

    platform::show_prompt(PrimaryCommand::prompt());

    self.read_input(|state, input| {
      if let Some(cmd) = PrimaryCommand::get_from_input(input) {
        state.process_cmd(cmd);
      };
    });
  }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::Write;
use std::error::Error;

/// A list of files contained in the /proc directory>
///
/// This list is used to identify which file or directory an error is relating too.
#[derive(PartialEq)]
pub enum ProcFile {
  /// /proc Directory, contains files containg various pieces of information about the system.
  ProcDir,
  /// /proc/cmdline file, contains the cmdline used when starting the kernel.
  ProcCmdline,
  /// /proc/cpuinfo file, contains information about the cpu.
  ProcCpuinfo,
  /// /proc/meminfo file, contains information about the memory resources of the system.
  ProcMeminfo,
  /// /proc/stat file.
  ProcStat,
  /// /proc/uptime file, contains the uptime of the system.
  ProcUptime,
  /// /proc/status file.
  ProcStatus,

  /// /proc/[pid] directory, contains files relating to the process at [pid].
  PidDir,
  /// /proc/[pid]/status file, contains various human-readable stats about the process.
  PidStatus,
  /// /proc/[pid]/stat file, contains various stats about the process.
  PidStat,
  /// /proc/[pid]/cmdline file, contains the cmdline given when starting the process.
  PidCmdline
}

impl Error for ProcFile {
  fn description(&self) -> &str {
    match *self {
      ProcFile::ProcDir => "/proc directory",
      ProcFile::ProcCmdline => "/proc/cmdline file",
      ProcFile::ProcCpuinfo => "/proc/cmdinfo file",
      ProcFile::ProcMeminfo => "/proc/meminfo file",
      ProcFile::ProcStat => "/proc/stat file",
      ProcFile::ProcUptime => "/proc/uptime file",
      ProcFile::ProcStatus => "/proc/status file",
      ProcFile::PidDir => "/proc/[pid] directory",
      ProcFile::PidStatus => "/proc/[pid]/status file",
      ProcFile::PidStat => "/proc/[pid]/stat file",
      ProcFile::PidCmdline => "/proc/[pid]/cmdline file"
    }
  }

  fn cause(&self) -> Option<&Error> {
    None
  }
}

impl fmt::Debug for ProcFile {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

impl fmt::Display for ProcFile {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

/// A list of errors that can occur while operating on something in /proc.
#[derive(PartialEq)]
pub enum ProcOper {
  /// Error opening a file/directory.
  Opening,
  /// Error reading a file/directorr.
  Reading,
  /// Error parsing a file/directory.
  Parsing,
  /// Error parsing a specific field in a file/directory.
  ParsingField,
}

impl ProcOper {
  pub fn is_hard(&self) -> bool {
    match *self {
      ProcOper::Opening => false,
      ProcOper::Reading => false,
      _ => true
    }
  }
}

impl Error for ProcOper {
  fn description(&self) -> &'static str {
    match *self {
      ProcOper::Opening => "opening",
      ProcOper::Reading => "reading",
      ProcOper::Parsing => "parsing",
      ProcOper::ParsingField => "parsing field"
    }
  }
}

impl fmt::Debug for ProcOper {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

impl fmt::Display for ProcOper {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

// The error type for operations on /proc.
//
// Errors that can occur while reading /proc. These have an error
// kind (error), a file/directory (file), an inner error (inner)
// and optionally more information that is error-specific.
pub struct ProcError {
  error: ProcOper,
  file: ProcFile,
  inner: Option<Box<Error>>,
  more: Option<&'static str>
}

impl ProcError {
  pub fn new_err<E: Error + 'static>(error: ProcOper, file: ProcFile, cause: E)
    -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: Some(Box::new(cause)),
      more: None
    }
  }

  pub fn new_more(error: ProcOper, file: ProcFile, more: Option<&'static str>) -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: None,
      more: more
    }
  }

  pub fn new<E: Error + 'static>(error: ProcOper, file: ProcFile, cause: Option<E>,
    more: Option<&'static str>) -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: match cause {
        Some(e) => Some(Box::new(e)),
        None => None
      },
      more: more
    }
  }

  pub fn is_hard(&self) -> bool {
    self.error.is_hard()
  }
}

impl Error for ProcError {
  fn description(&self) -> &str {
    self.error.description()
  }

  fn cause(&self) -> Option<&'static Error> {
    None
    // self.inner
  }
}

impl fmt::Debug for ProcError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let more = self.more.unwrap_or("");
    let error;
    if let Some(e) = self.inner.as_ref() {
      error = e.description();
    } else {
      error = "";
    }
    write!(f, "error {} ({}) from {}: {}",
      self.error.description(), more,
      self.file.description(), error)
  }
}

impl fmt::Display for ProcError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    fmt::Debug::fmt(self, f)
  }
}

impl PartialEq for ProcError {
  fn eq(&self, other: &Self) -> bool {
    self.error.eq(&other.error) && self.file.eq(&other.file) && self.more.eq(&other.more)
  }
}
<commit_msg>Fix error cause and doc strings<commit_after>use std::fmt;
use std::io::Write;
use std::error::Error;

/// A list of files contained in the /proc directory>
///
/// This list is used to identify which file or directory an error is relating too.
#[derive(PartialEq)]
pub enum ProcFile {
  /// /proc Directory, contains files containg various pieces of information about the system.
  ProcDir,
  /// /proc/cmdline file, contains the cmdline used when starting the kernel.
  ProcCmdline,
  /// /proc/cpuinfo file, contains information about the cpu.
  ProcCpuinfo,
  /// /proc/meminfo file, contains information about the memory resources of the system.
  ProcMeminfo,
  /// /proc/stat file.
  ProcStat,
  /// /proc/uptime file, contains the uptime of the system.
  ProcUptime,
  /// /proc/status file.
  ProcStatus,

  /// /proc/[pid] directory, contains files relating to the process at [pid].
  PidDir,
  /// /proc/[pid]/status file, contains various human-readable stats about the process.
  PidStatus,
  /// /proc/[pid]/stat file, contains various stats about the process.
  PidStat,
  /// /proc/[pid]/cmdline file, contains the cmdline given when starting the process.
  PidCmdline
}

impl Error for ProcFile {
  fn description(&self) -> &str {
    match *self {
      ProcFile::ProcDir => "/proc directory",
      ProcFile::ProcCmdline => "/proc/cmdline file",
      ProcFile::ProcCpuinfo => "/proc/cmdinfo file",
      ProcFile::ProcMeminfo => "/proc/meminfo file",
      ProcFile::ProcStat => "/proc/stat file",
      ProcFile::ProcUptime => "/proc/uptime file",
      ProcFile::ProcStatus => "/proc/status file",
      ProcFile::PidDir => "/proc/[pid] directory",
      ProcFile::PidStatus => "/proc/[pid]/status file",
      ProcFile::PidStat => "/proc/[pid]/stat file",
      ProcFile::PidCmdline => "/proc/[pid]/cmdline file"
    }
  }

  fn cause(&self) -> Option<&Error> {
    None
  }
}

impl fmt::Debug for ProcFile {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

impl fmt::Display for ProcFile {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

/// A list of errors that can occur while operating on something in /proc.
#[derive(PartialEq)]
pub enum ProcOper {
  /// Error opening a file/directory.
  Opening,
  /// Error reading a file/directorr.
  Reading,
  /// Error parsing a file/directory.
  Parsing,
  /// Error parsing a specific field in a file/directory.
  ParsingField,
}

impl ProcOper {
  pub fn is_hard(&self) -> bool {
    match *self {
      ProcOper::Opening => false,
      ProcOper::Reading => false,
      _ => true
    }
  }
}

impl Error for ProcOper {
  fn description(&self) -> &'static str {
    match *self {
      ProcOper::Opening => "opening",
      ProcOper::Reading => "reading",
      ProcOper::Parsing => "parsing",
      ProcOper::ParsingField => "parsing field"
    }
  }
}

impl fmt::Debug for ProcOper {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

impl fmt::Display for ProcOper {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.description())
  }
}

/// The error type for operations on /proc.
///
/// Errors that can occur while reading /proc. These have an error
/// kind (error), a file/directory (file), an inner error (inner)
/// and optionally more information that is error-specific.
pub struct ProcError {
  error: ProcOper,
  file: ProcFile,
  inner: Option<Box<Error>>,
  more: Option<&'static str>
}

impl ProcError {
  pub fn new_err<E: Error + 'static>(error: ProcOper, file: ProcFile, cause: E)
    -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: Some(Box::new(cause)),
      more: None
    }
  }

  pub fn new_more(error: ProcOper, file: ProcFile, more: Option<&'static str>) -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: None,
      more: more
    }
  }

  pub fn new<E: Error + 'static>(error: ProcOper, file: ProcFile, cause: Option<E>,
    more: Option<&'static str>) -> ProcError {
    ProcError {
      error: error,
      file: file,
      inner: match cause {
        Some(e) => Some(Box::new(e)),
        None => None
      },
      more: more
    }
  }

  pub fn is_hard(&self) -> bool {
    self.error.is_hard()
  }
}

impl Error for ProcError {
  fn description(&self) -> &str {
    self.error.description()
  }

  fn cause(&self) -> Option<&Error> {
    self.inner.as_ref().map(|e| e.as_ref())
  }
}

impl fmt::Debug for ProcError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let more = self.more.unwrap_or("");
    let error;
    if let Some(e) = self.inner.as_ref() {
      error = e.description();
    } else {
      error = "";
    }
    write!(f, "error {} ({}) from {}: {}",
      self.error.description(), more,
      self.file.description(), error)
  }
}

impl fmt::Display for ProcError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    fmt::Debug::fmt(self, f)
  }
}

impl PartialEq for ProcError {
  fn eq(&self, other: &Self) -> bool {
    self.error.eq(&other.error) && self.file.eq(&other.file) && self.more.eq(&other.more)
  }
}
<|endoftext|>


<commit_before>//! Generic WebSocket protocol implementation

mod frame;
mod message;

pub use self::message::Message;

use std::collections::VecDeque;
use std::io::{Read, Write};
use std::mem::replace;

use error::{Error, Result};
use self::message::{IncompleteMessage, IncompleteMessageType};
use self::frame::{Frame, FrameSocket};
use self::frame::coding::{OpCode, Data as OpData, Control as OpCtl, CloseCode};
use util::NonBlockingResult;

/// Indicates a Client or Server role of the websocket
#[derive(Debug, Clone, Copy)]
pub enum Role {
    /// This socket is a server
    Server,
    /// This socket is a client
    Client,
}

/// WebSocket input-output stream
pub struct WebSocket<Stream> {
    /// Server or client?
    role: Role,
    /// The underlying socket.
    socket: FrameSocket<Stream>,
    /// The state of processing, either "active" or "closing".
    state: WebSocketState,
    /// Receive: an incomplete message being processed.
    incomplete: Option<IncompleteMessage>,
    /// Send: a data send queue.
    send_queue: VecDeque<Frame>,
    /// Send: an OOB pong message.
    pong: Option<Frame>,
}

impl<Stream> WebSocket<Stream>
    where Stream: Read + Write
{

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_raw_socket(stream: Stream, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::new(stream), role)
    }

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_partially_read(stream: Stream, part: Vec<u8>, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::from_partially_read(stream, part), role)
    }

    /// Read a message from stream, if possible.
    pub fn read_message(&mut self) -> Result<Message> {
        loop {
            let write_blocks = self.send_pending().no_block()?.is_none();
            let read = self.read_message_frame();
            let frame = if write_blocks {
                Some(read?)
            } else {
                read.no_block()?
            };
            if let Some(Some(message)) = frame {
                debug!("Received message {}", message);
                return Ok(message)
            }
        }
    }

    /// Send a message to stream, if possible.
    pub fn write_message(&mut self, message: Message) -> Result<()> {
        let frame = {
            let opcode = match message {
                Message::Text(_) => OpData::Text,
                Message::Binary(_) => OpData::Binary,
            };
            Frame::message(message.into_data(), OpCode::Data(opcode), true)
        };
        self.send_queue.push_back(frame);
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Close the connection.
    pub fn close(&mut self) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByUs;
                let frame = Frame::close(None);
                self.send_queue.push_back(frame);
            }
            _ => {
                // already closed, nothing to do
            }
        }
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Convert a frame socket into a WebSocket.
    fn from_frame_socket(socket: FrameSocket<Stream>, role: Role) -> Self {
        WebSocket {
            role: role,
            socket: socket,
            state: WebSocketState::Active,
            incomplete: None,
            send_queue: VecDeque::new(),
            pong: None,
        }
    }

    /// Try to decode one message frame. May return None.
    fn read_message_frame(&mut self) -> Result<Option<Message>> {
        if let Some(mut frame) = self.socket.read_frame()? {

            // MUST be 0 unless an extension is negotiated that defines meanings
            // for non-zero values.  If a nonzero value is received and none of
            // the negotiated extensions defines the meaning of such a nonzero
            // value, the receiving endpoint MUST _Fail the WebSocket
            // Connection_.
            if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
                return Err(Error::Protocol("Reserved bits are non-zero".into()))
            }

            match self.role {
                Role::Server => {
                    if frame.is_masked() {
                        // A server MUST remove masking for data frames received from a client
                        // as described in Section 5.3. (RFC 6455)
                        frame.remove_mask()
                    } else {
                        // The server MUST close the connection upon receiving a
                        // frame that is not masked. (RFC 6455)
                        return Err(Error::Protocol("Received an unmasked frame from client".into()))
                    }
                }
                Role::Client => {
                    if frame.is_masked() {
                        // A client MUST close a connection if it detects a masked frame. (RFC 6455)
                        return Err(Error::Protocol("Received a masked frame from server".into()))
                    }
                }
            }

            match frame.opcode() {

                OpCode::Control(ctl) => {
                    (match ctl {
                        // All control frames MUST have a payload length of 125 bytes or less
                        // and MUST NOT be fragmented. (RFC 6455)
                        _ if !frame.is_final() => {
                            Err(Error::Protocol("Fragmented control frame".into()))
                        }
                        _ if frame.payload().len() > 125 => {
                            Err(Error::Protocol("Control frame too big".into()))
                        }
                        OpCtl::Close => {
                            self.do_close(frame.into_close()?)
                        }
                        OpCtl::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown control frame type {}", i).into()))
                        }
                        OpCtl::Ping | OpCtl::Pong if !self.state.is_active() => {
                            // No ping processing while closing.
                            Ok(())
                        }
                        OpCtl::Ping => {
                            self.do_ping(frame.into_data())
                        }
                        OpCtl::Pong => {
                            self.do_pong(frame.into_data())
                        }
                    }).map(|_| None)
                }

                OpCode::Data(_) if !self.state.is_active() => {
                    // No data processing while closing.
                    Ok(None)
                }

                OpCode::Data(data) => {
                    let fin = frame.is_final();
                    match data {
                        OpData::Continue => {
                            if let Some(ref mut msg) = self.incomplete {
                                // TODO if msg too big
                                msg.extend(frame.into_data())?;
                            } else {
                                return Err(Error::Protocol("Continue frame but nothing to continue".into()))
                            }
                            if fin {
                                Ok(Some(replace(&mut self.incomplete, None).unwrap().complete()?))
                            } else {
                                Ok(None)
                            }
                        }
                        c if self.incomplete.is_some() => {
                            Err(Error::Protocol(
                                format!("Received {} while waiting for more fragments", c).into()
                            ))
                        }
                        OpData::Text | OpData::Binary => {
                            let msg = {
                                let message_type = match data {
                                    OpData::Text => IncompleteMessageType::Text,
                                    OpData::Binary => IncompleteMessageType::Binary,
                                    _ => panic!("Bug: message is not text nor binary"),
                                };
                                let mut m = IncompleteMessage::new(message_type);
                                m.extend(frame.into_data())?;
                                m
                            };
                            if fin {
                                Ok(Some(msg.complete()?))
                            } else {
                                self.incomplete = Some(msg);
                                Ok(None)
                            }
                        }
                        OpData::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown data frame type {}", i).into()))
                        }
                    }
                }

            } // match opcode

        } else {
            Err(Error::Protocol("Connection reset without closing handshake".into()))
        }
    }

    /// Received a close frame.
    fn do_close(&mut self, close: Option<(CloseCode, String)>) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByPeer;
                let reply = if let Some((code, _)) = close {
                    if code.is_allowed() {
                        Frame::close(Some((CloseCode::Normal, "")))
                    } else {
                        Frame::close(Some((CloseCode::Protocol, "Protocol violation")))
                    }
                } else {
                    Frame::close(None)
                };
                self.send_queue.push_back(reply);
                Ok(())
            }
            WebSocketState::ClosedByPeer => {
                // It is already closed, just ignore.
                Ok(())
            }
            WebSocketState::ClosedByUs => {
                // We received a reply.
                match self.role {
                    Role::Client => {
                        // Client waits for the server to close the connection.
                        Ok(())
                    }
                    Role::Server => {
                        // Server closes the connection.
                        Err(Error::ConnectionClosed)
                    }
                }
            }
        }
    }

    /// Received a ping frame.
    fn do_ping(&mut self, ping: Vec<u8>) -> Result<()> {
        // If an endpoint receives a Ping frame and has not yet sent Pong
        // frame(s) in response to previous Ping frame(s), the endpoint MAY
        // elect to send a Pong frame for only the most recently processed Ping
        // frame. (RFC 6455)
        // We do exactly that, keeping a "queue" from one and only Pong frame.
        self.pong = Some(Frame::pong(ping));
        Ok(())
    }

    /// Received a pong frame.
    fn do_pong(&mut self, _: Vec<u8>) -> Result<()> {
        // A Pong frame MAY be sent unsolicited.  This serves as a
        // unidirectional heartbeat.  A response to an unsolicited Pong frame is
        // not expected. (RFC 6455)
        // Due to this, we just don't check pongs right now.
        // TODO: check if there was a reply to our ping at all...
        Ok(())
    }

    /// Flush the pending send queue.
    fn send_pending(&mut self) -> Result<()> {
        // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
        // response, unless it already received a Close frame. It SHOULD
        // respond with Pong frame as soon as is practical. (RFC 6455)
        if let Some(pong) = replace(&mut self.pong, None) {
            self.send_one_frame(pong)?;
        }
        // If we have any unsent frames, send them.
        while let Some(data) = self.send_queue.pop_front() {
            self.send_one_frame(data)?;
        }

        // If we're closing and there is nothing to send anymore, we should close the connection.
        match self.state {
            WebSocketState::ClosedByPeer if self.send_queue.is_empty() => {
                // The underlying TCP connection, in most normal cases, SHOULD be closed
                // first by the server, so that it holds the TIME_WAIT state and not the
                // client (as this would prevent it from re-opening the connection for 2
                // maximum segment lifetimes (2MSL), while there is no corresponding
                // server impact as a TIME_WAIT connection is immediately reopened upon
                // a new SYN with a higher seq number). (RFC 6455)
                match self.role {
                    Role::Client => Ok(()),
                    Role::Server => Err(Error::ConnectionClosed),
                }
            }
            _ => Ok(()),
        }
    }

    /// Send a single pending frame.
    fn send_one_frame(&mut self, mut frame: Frame) -> Result<()> {
        match self.role {
            Role::Server => {
            }
            Role::Client => {
                // 5.  If the data is being sent by the client, the frame(s) MUST be
                // masked as defined in Section 5.3. (RFC 6455)
                frame.set_mask();
            }
        }
        self.socket.write_frame(frame)?;
        Ok(())
    }

}

/// The current connection state.
enum WebSocketState {
    Active,
    ClosedByUs,
    ClosedByPeer,
}

impl WebSocketState {
    /// Tell if we're allowed to process normal messages.
    fn is_active(&self) -> bool {
        match *self {
            WebSocketState::Active => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{WebSocket, Role, Message};

    use std::io;
    use std::io::Cursor;

    struct WriteMoc<Stream>(Stream);

    impl<Stream> io::Write for WriteMoc<Stream> {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            Ok(buf.len())
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    impl<Stream: io::Read> io::Read for WriteMoc<Stream> {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            self.0.read(buf)
        }
    }

    #[test]
    fn receive_messages() {
        let incoming = Cursor::new(vec![
            0x01, 0x07,
            0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
            0x80, 0x06,
            0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
            0x82, 0x03,
            0x01, 0x02, 0x03,
        ]);
        let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client);
        assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into()));
        assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
    }

}
<commit_msg>protocol: fix infinite loop in non-blocking mode<commit_after>//! Generic WebSocket protocol implementation

mod frame;
mod message;

pub use self::message::Message;

use std::collections::VecDeque;
use std::io::{Read, Write};
use std::mem::replace;

use error::{Error, Result};
use self::message::{IncompleteMessage, IncompleteMessageType};
use self::frame::{Frame, FrameSocket};
use self::frame::coding::{OpCode, Data as OpData, Control as OpCtl, CloseCode};
use util::NonBlockingResult;

/// Indicates a Client or Server role of the websocket
#[derive(Debug, Clone, Copy)]
pub enum Role {
    /// This socket is a server
    Server,
    /// This socket is a client
    Client,
}

/// WebSocket input-output stream
pub struct WebSocket<Stream> {
    /// Server or client?
    role: Role,
    /// The underlying socket.
    socket: FrameSocket<Stream>,
    /// The state of processing, either "active" or "closing".
    state: WebSocketState,
    /// Receive: an incomplete message being processed.
    incomplete: Option<IncompleteMessage>,
    /// Send: a data send queue.
    send_queue: VecDeque<Frame>,
    /// Send: an OOB pong message.
    pong: Option<Frame>,
}

impl<Stream> WebSocket<Stream>
    where Stream: Read + Write
{

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_raw_socket(stream: Stream, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::new(stream), role)
    }

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_partially_read(stream: Stream, part: Vec<u8>, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::from_partially_read(stream, part), role)
    }

    /// Read a message from stream, if possible.
    pub fn read_message(&mut self) -> Result<Message> {
        loop {
            self.send_pending().no_block()?;
            // If we get here, either write blocks or we have nothing to write.
            // Thus if read blocks, just let it return WouldBlock.
            if let Some(message) = self.read_message_frame()? {
                debug!("Received message {}", message);
                return Ok(message)
            }
        }
    }

    /// Send a message to stream, if possible.
    pub fn write_message(&mut self, message: Message) -> Result<()> {
        let frame = {
            let opcode = match message {
                Message::Text(_) => OpData::Text,
                Message::Binary(_) => OpData::Binary,
            };
            Frame::message(message.into_data(), OpCode::Data(opcode), true)
        };
        self.send_queue.push_back(frame);
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Close the connection.
    pub fn close(&mut self) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByUs;
                let frame = Frame::close(None);
                self.send_queue.push_back(frame);
            }
            _ => {
                // already closed, nothing to do
            }
        }
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Convert a frame socket into a WebSocket.
    fn from_frame_socket(socket: FrameSocket<Stream>, role: Role) -> Self {
        WebSocket {
            role: role,
            socket: socket,
            state: WebSocketState::Active,
            incomplete: None,
            send_queue: VecDeque::new(),
            pong: None,
        }
    }

    /// Try to decode one message frame. May return None.
    fn read_message_frame(&mut self) -> Result<Option<Message>> {
        if let Some(mut frame) = self.socket.read_frame()? {

            // MUST be 0 unless an extension is negotiated that defines meanings
            // for non-zero values.  If a nonzero value is received and none of
            // the negotiated extensions defines the meaning of such a nonzero
            // value, the receiving endpoint MUST _Fail the WebSocket
            // Connection_.
            if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
                return Err(Error::Protocol("Reserved bits are non-zero".into()))
            }

            match self.role {
                Role::Server => {
                    if frame.is_masked() {
                        // A server MUST remove masking for data frames received from a client
                        // as described in Section 5.3. (RFC 6455)
                        frame.remove_mask()
                    } else {
                        // The server MUST close the connection upon receiving a
                        // frame that is not masked. (RFC 6455)
                        return Err(Error::Protocol("Received an unmasked frame from client".into()))
                    }
                }
                Role::Client => {
                    if frame.is_masked() {
                        // A client MUST close a connection if it detects a masked frame. (RFC 6455)
                        return Err(Error::Protocol("Received a masked frame from server".into()))
                    }
                }
            }

            match frame.opcode() {

                OpCode::Control(ctl) => {
                    (match ctl {
                        // All control frames MUST have a payload length of 125 bytes or less
                        // and MUST NOT be fragmented. (RFC 6455)
                        _ if !frame.is_final() => {
                            Err(Error::Protocol("Fragmented control frame".into()))
                        }
                        _ if frame.payload().len() > 125 => {
                            Err(Error::Protocol("Control frame too big".into()))
                        }
                        OpCtl::Close => {
                            self.do_close(frame.into_close()?)
                        }
                        OpCtl::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown control frame type {}", i).into()))
                        }
                        OpCtl::Ping | OpCtl::Pong if !self.state.is_active() => {
                            // No ping processing while closing.
                            Ok(())
                        }
                        OpCtl::Ping => {
                            self.do_ping(frame.into_data())
                        }
                        OpCtl::Pong => {
                            self.do_pong(frame.into_data())
                        }
                    }).map(|_| None)
                }

                OpCode::Data(_) if !self.state.is_active() => {
                    // No data processing while closing.
                    Ok(None)
                }

                OpCode::Data(data) => {
                    let fin = frame.is_final();
                    match data {
                        OpData::Continue => {
                            if let Some(ref mut msg) = self.incomplete {
                                // TODO if msg too big
                                msg.extend(frame.into_data())?;
                            } else {
                                return Err(Error::Protocol("Continue frame but nothing to continue".into()))
                            }
                            if fin {
                                Ok(Some(replace(&mut self.incomplete, None).unwrap().complete()?))
                            } else {
                                Ok(None)
                            }
                        }
                        c if self.incomplete.is_some() => {
                            Err(Error::Protocol(
                                format!("Received {} while waiting for more fragments", c).into()
                            ))
                        }
                        OpData::Text | OpData::Binary => {
                            let msg = {
                                let message_type = match data {
                                    OpData::Text => IncompleteMessageType::Text,
                                    OpData::Binary => IncompleteMessageType::Binary,
                                    _ => panic!("Bug: message is not text nor binary"),
                                };
                                let mut m = IncompleteMessage::new(message_type);
                                m.extend(frame.into_data())?;
                                m
                            };
                            if fin {
                                Ok(Some(msg.complete()?))
                            } else {
                                self.incomplete = Some(msg);
                                Ok(None)
                            }
                        }
                        OpData::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown data frame type {}", i).into()))
                        }
                    }
                }

            } // match opcode

        } else {
            Err(Error::Protocol("Connection reset without closing handshake".into()))
        }
    }

    /// Received a close frame.
    fn do_close(&mut self, close: Option<(CloseCode, String)>) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByPeer;
                let reply = if let Some((code, _)) = close {
                    if code.is_allowed() {
                        Frame::close(Some((CloseCode::Normal, "")))
                    } else {
                        Frame::close(Some((CloseCode::Protocol, "Protocol violation")))
                    }
                } else {
                    Frame::close(None)
                };
                self.send_queue.push_back(reply);
                Ok(())
            }
            WebSocketState::ClosedByPeer => {
                // It is already closed, just ignore.
                Ok(())
            }
            WebSocketState::ClosedByUs => {
                // We received a reply.
                match self.role {
                    Role::Client => {
                        // Client waits for the server to close the connection.
                        Ok(())
                    }
                    Role::Server => {
                        // Server closes the connection.
                        Err(Error::ConnectionClosed)
                    }
                }
            }
        }
    }

    /// Received a ping frame.
    fn do_ping(&mut self, ping: Vec<u8>) -> Result<()> {
        // If an endpoint receives a Ping frame and has not yet sent Pong
        // frame(s) in response to previous Ping frame(s), the endpoint MAY
        // elect to send a Pong frame for only the most recently processed Ping
        // frame. (RFC 6455)
        // We do exactly that, keeping a "queue" from one and only Pong frame.
        self.pong = Some(Frame::pong(ping));
        Ok(())
    }

    /// Received a pong frame.
    fn do_pong(&mut self, _: Vec<u8>) -> Result<()> {
        // A Pong frame MAY be sent unsolicited.  This serves as a
        // unidirectional heartbeat.  A response to an unsolicited Pong frame is
        // not expected. (RFC 6455)
        // Due to this, we just don't check pongs right now.
        // TODO: check if there was a reply to our ping at all...
        Ok(())
    }

    /// Flush the pending send queue.
    fn send_pending(&mut self) -> Result<()> {
        // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
        // response, unless it already received a Close frame. It SHOULD
        // respond with Pong frame as soon as is practical. (RFC 6455)
        if let Some(pong) = replace(&mut self.pong, None) {
            self.send_one_frame(pong)?;
        }
        // If we have any unsent frames, send them.
        while let Some(data) = self.send_queue.pop_front() {
            self.send_one_frame(data)?;
        }

        // If we're closing and there is nothing to send anymore, we should close the connection.
        match self.state {
            WebSocketState::ClosedByPeer if self.send_queue.is_empty() => {
                // The underlying TCP connection, in most normal cases, SHOULD be closed
                // first by the server, so that it holds the TIME_WAIT state and not the
                // client (as this would prevent it from re-opening the connection for 2
                // maximum segment lifetimes (2MSL), while there is no corresponding
                // server impact as a TIME_WAIT connection is immediately reopened upon
                // a new SYN with a higher seq number). (RFC 6455)
                match self.role {
                    Role::Client => Ok(()),
                    Role::Server => Err(Error::ConnectionClosed),
                }
            }
            _ => Ok(()),
        }
    }

    /// Send a single pending frame.
    fn send_one_frame(&mut self, mut frame: Frame) -> Result<()> {
        match self.role {
            Role::Server => {
            }
            Role::Client => {
                // 5.  If the data is being sent by the client, the frame(s) MUST be
                // masked as defined in Section 5.3. (RFC 6455)
                frame.set_mask();
            }
        }
        self.socket.write_frame(frame)?;
        Ok(())
    }

}

/// The current connection state.
enum WebSocketState {
    Active,
    ClosedByUs,
    ClosedByPeer,
}

impl WebSocketState {
    /// Tell if we're allowed to process normal messages.
    fn is_active(&self) -> bool {
        match *self {
            WebSocketState::Active => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{WebSocket, Role, Message};

    use std::io;
    use std::io::Cursor;

    struct WriteMoc<Stream>(Stream);

    impl<Stream> io::Write for WriteMoc<Stream> {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            Ok(buf.len())
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    impl<Stream: io::Read> io::Read for WriteMoc<Stream> {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            self.0.read(buf)
        }
    }

    #[test]
    fn receive_messages() {
        let incoming = Cursor::new(vec![
            0x01, 0x07,
            0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
            0x80, 0x06,
            0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
            0x82, 0x03,
            0x01, 0x02, 0x03,
        ]);
        let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client);
        assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into()));
        assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
    }

}
<|endoftext|>