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

//! # Command Parser
//!
//! The `commands::parser` module provides:
//!
//! * Functionality for building a tree of commands based on their names.
//! * Parsing tokenized input to select a command. Input tokenization can
//!   be performed by the [`commands::tokenizer`] module.
//! * Validating parameters.
//! * Performing completion on commands and parameters.
//!
//! The command parser consists of two important things:
//!
//! * A tree that represents the available commands and their arguments.
//!   This tree consists of instances of implementations of [`Node`] like
//!   [`CommandNode`], [`ParameterNode`] and [`RootNode`]. Construction of this
//!   tree is done with the help of [`CommandTree`], [`Command`] and
//!   [`Parameter`].
//! * A [`Parser`] that handles input and matches it against the command
//!   tree. This parser is intended to be short-lived and to just live
//!   for the duration of parsing and evaluating a single command line
//!   input.
//!
//! ## Building Nodes
//!
//! Building a tree of nodes for use with the parser is best done with
//! the [`CommandTree`] in conjunction with [`Command`] and [`Parameter`].
//!
//! Start by creating a mutable [`CommandTree`] instance:
//!
//! ```
//! use commands::parser::{CommandTree, Parser};
//!
//! let mut tree = CommandTree::new();
//! ```
//!
//! Then, add your commands and arguments, and finally,
//! call `finalize` on the tree to get back a [`RootNode`]
//! that can use be used with a [`Parser`].
//!
//! ```
//! use commands::parser::{Command, CommandTree, Parameter, Parser};
//!
//! let mut tree = CommandTree::new();
//! tree.command(Command::new("again")
//!                  .hidden(false)
//!                  .parameter(Parameter::new("test")
//!                                 .required(false)
//!                                 .help("This is just a test parameter.")));
//! let root = tree.finalize();
//! let mut parser = Parser::new(root);
//! ```
//!
//! [`commands::tokenizer`]: ../tokenizer/index.html
//! [`Command`]: struct.Command.html
//! [`CommandNode`]: struct.CommandNode.html
//! [`CommandTree`]: struct.CommandTree.html
//! [`Node`]: trait.Node.html
//! [`Parameter`]: struct.Parameter.html
//! [`ParameterNode`]: trait.ParameterNode.html
//! [`Parser`]: struct.Parser.html
//! [`RootNode`]: struct.RootNode.html

mod builder;
mod completion;
mod constants;
mod nodes;

// Re-export public API
pub use self::builder::{Command, CommandTree, Parameter};
pub use self::constants::ParameterKind;
pub use self::constants::{PRIORITY_DEFAULT, PRIORITY_MINIMUM, PRIORITY_PARAMETER};
pub use self::completion::{Completion, CompletionOption};
pub use self::nodes::{Node, NodeOps};
pub use self::nodes::{CommandNode, ParameterNameNode, ParameterNode, RootNode};

use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
use tokenizer::{Token, TokenType};

/// Command parser
///
/// The lifetime parameter `'text` refers to the lifetime of the
/// tokens passed into the parser. This is the same as the lifetime
/// of the text used to create the tokens.
///
/// When creating a `Parser`, you must give it an `Rc<RootNode>`.
/// [`RootNode`] instances should be created using a [`CommandTree`].
///
/// ```
/// use commands::parser::{Command, CommandTree, Parser};
///
/// let mut tree = CommandTree::new();
/// tree.command(Command::new("show"));
/// tree.command(Command::new("set"));
/// tree.command(Command::new("help"));
///
/// let mut parser = Parser::new(tree.finalize());
/// ```
///
/// The parser is constructed as a `mut`able object as most of
/// the methods on it will modify its state.
///
/// [`CommandTree`]: struct.CommandTree.html
/// ['RootNode`]: struct.RootNode.html
pub struct Parser<'text> {
    current_node: Rc<Node>,
    /// The nodes which have been accepted during `parse` or `advance`.
    pub nodes: Vec<Rc<Node>>,
    /// The tokens which have been accepted during `parse` or `advance`.
    pub tokens: Vec<Token<'text>>,
    commands: Vec<Rc<Node>>,
    parameters: HashMap<String, String>,
}

impl<'text> Parser<'text> {
    /// Construct a parser with a root node.
    pub fn new(initial_node: Rc<Node>) -> Parser<'text> {
        Parser {
            current_node: initial_node,
            nodes: vec![],
            tokens: vec![],
            commands: vec![],
            parameters: HashMap::new(),
        }
    }

    /// Given an optional token, get the possible valid completions
    /// for the current parser state.
    ///
    /// Possible completions are successors of the current node which
    /// are not `hidden`, are `acceptable`, and which match the token,
    /// if one has been provided.
    ///
    /// Nodes may customize the `Complete` trait to customize the
    /// [`Completion`] and [`CompletionOption`]s which are generated
    /// for that node.
    ///
    /// Each valid successor node will have one [`Completion`] in the
    /// result vector. Each [`Completion`] will have one or more
    /// [`CompletionOption`] for each valid way that the value may be
    /// entered.
    ///
    /// ```
    /// use commands::parser::{Command, CommandTree, Parser};
    /// use commands::tokenizer::{Token, tokenize};
    ///
    /// let mut tree = CommandTree::new();
    /// tree.command(Command::new("show"));
    /// tree.command(Command::new("set"));
    /// tree.command(Command::new("help"));
    ///
    /// let mut parser = Parser::new(tree.finalize());
    ///
    /// // Completing now should have 3 options, 1 for each command.
    /// let comps = parser.complete(None);
    /// assert_eq!(comps.len(), 3);
    ///
    /// // But completing with a token for 'h' should have 1 option.
    /// if let Ok(tokens) = tokenize("h") {
    ///   let comps = parser.complete(Some(tokens[0]));
    ///   assert_eq!(comps.len(), 1);
    ///   assert_eq!(comps[0].options.len(), 1);
    ///   assert_eq!(comps[0].options[0].option_string, "help");
    /// } else {
    ///   panic!("Tokenize failed.");
    /// }
    ///
    /// // And completing for 's' should have 2 options.
    /// if let Ok(tokens) = tokenize("s") {
    ///   let comps = parser.complete(Some(tokens[0]));
    ///   assert_eq!(comps.len(), 2);
    /// } else {
    ///   panic!("Tokenize failed.");
    /// }
    /// ```
    ///
    /// [`Completion`]: struct.Completion.html
    /// [`CompletionOption`]: struct.CompletionOption.html
    pub fn complete(&self, token: Option<Token<'text>>) -> Vec<Completion> {
        self.current_node
            .successors()
            .into_iter()
            .filter(|n| {
                // To be a possible completion, the node should not be
                // hidden, it should be acceptable, and if there's a token,
                // it should be a valid match for the node.
                !n.node().hidden && n.acceptable(self, n) &&
                if let Some(t) = token {
                    n.matches(self, t)
                } else {
                    true
                }
            })
            .map(|n| n.complete(token))
            .collect::<Vec<_>>()
    }

    /// Parse a vector of tokens, advancing through the
    /// node hierarchy.
    ///
    /// ```
    /// use commands::parser::{Command, CommandTree, Parser};
    /// use commands::tokenizer::tokenize;
    ///
    /// let mut tree = CommandTree::new();
    /// tree.command(Command::new("show interface"));
    ///
    /// let mut parser = Parser::new(tree.finalize());
    ///
    /// if let Ok(tokens) = tokenize("show interface") {
    ///     parser.parse(tokens);
    /// }
    /// ```
    pub fn parse(&mut self, tokens: Vec<Token<'text>>) -> Result<(), ParseError<'text>> {
        for token in tokens {
            match token.token_type {
                TokenType::Whitespace => {}
                TokenType::Word => try!(self.advance(token)),
            }
        }
        Ok(())
    }

    /// Parse a single token, advancing through the node hierarchy.
    pub fn advance(&mut self, token: Token<'text>) -> Result<(), ParseError<'text>> {
        let acceptable = self.current_node
                             .successors()
                             .into_iter()
                             .filter(|n| n.acceptable(self, n))
                             .map(|n| n.clone())
                             .collect::<Vec<_>>();
        let matches = acceptable.clone()
                                .into_iter()
                                .filter(|n| n.matches(self, token))
                                .map(|n| n.clone())
                                .collect::<Vec<_>>();
        match matches.len() {
            1 => {
                let matching_node = &matches[0];
                matching_node.accept(self, token, matching_node);
                self.current_node = matching_node.clone();
                self.nodes.push(matching_node.clone());
                self.tokens.push(token);
                Ok(())
            }
            0 => Err(ParseError::NoMatches(token, acceptable)),
            _ => Err(ParseError::AmbiguousMatch(token, matches)),
        }
    }

    /// Execute the command that has been accepted by the parser.
    ///
    /// * XXX: This should be returning a Result probably.
    pub fn execute(&self) {
        if !self.commands.is_empty() {
            unimplemented!();
            // self.commands[0].execute(self)
        }
    }

    /// Verify that the parser is in a valid state with
    /// respect to having accepted a command and all
    /// required parameters.
    pub fn verify(&self) -> Result<(), VerifyError> {
        if self.commands.is_empty() {
            return Err(VerifyError::NoCommandAccepted);
        } else {
            if let Node::Command(ref command) = *self.commands[0] {
                for expected in &command.parameters {
                    if let Node::Parameter(ref param) = **expected {
                        let name = &param.node.name;
                        if param.required && !self.parameters.contains_key(name) {
                            return Err(VerifyError::MissingParameter(name.clone()));
                        }
                    } else {
                        unreachable!();
                    }
                }
                Ok(())
            } else {
                unreachable!();
            }
        }
    }
}

/// Errors that calling `parse` on the `Parser` can raise.
#[derive(Clone)]
pub enum ParseError<'text> {
    /// There were no matches for the token.
    NoMatches(Token<'text>, Vec<Rc<Node>>),
    /// There was more than 1 possible match for the token.
    AmbiguousMatch(Token<'text>, Vec<Rc<Node>>),
}

impl<'text> fmt::Debug for ParseError<'text> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::NoMatches(token, _) => write!(f, "NoMatches({:?}, ...)", token),
            ParseError::AmbiguousMatch(token, _) => write!(f, "AmbiguousMatch({:?}, ...)", token),
        }
    }
}

impl<'text> Error for ParseError<'text> {
    fn description(&self) -> &str {
        match *self {
            ParseError::NoMatches(_, _) => "No match.",
            ParseError::AmbiguousMatch(_, _) => "Ambiguous match.",
        }
    }
}

impl<'text> fmt::Display for ParseError<'text> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Errors that calling `verify` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum VerifyError {
    /// No command has been accepted by the parser.
    NoCommandAccepted,
    /// A required parameter is missing.
    MissingParameter(String),
}

impl Error for VerifyError {
    fn description(&self) -> &str {
        match *self {
            VerifyError::NoCommandAccepted => "No command has been accepted by the parser.",
            VerifyError::MissingParameter(_) => "A required parameter is missing.",
        }
    }
}

impl fmt::Display for VerifyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

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

    #[test]
    #[should_panic]
    fn verify_signals_no_command() {
        let root = CommandTree::new().finalize();
        let parser = Parser::new(root);
        match parser.verify() {
            Err(VerifyError::NoCommandAccepted) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn parse_signals_no_matches() {
        let mut tree = CommandTree::new();
        tree.command(Command::new("show"));
        let mut parser = Parser::new(tree.finalize());
        if let Ok(tokens) = tokenize("h") {
            match parser.parse(tokens) {
                Err(ParseError::NoMatches(_, _)) => panic!(),
                _ => {}
            }
        }
    }

    #[test]
    #[should_panic]
    fn parse_signals_ambiguous_match() {
        let mut tree = CommandTree::new();
        tree.command(Command::new("show"));
        tree.command(Command::new("set"));
        let mut parser = Parser::new(tree.finalize());
        if let Ok(tokens) = tokenize("s") {
            match parser.parse(tokens) {
                Err(ParseError::AmbiguousMatch(_, _)) => panic!(),
                _ => {}
            }
        }
    }
}
<commit_msg>Remove 'return' usage that isn't needed.<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.

//! # Command Parser
//!
//! The `commands::parser` module provides:
//!
//! * Functionality for building a tree of commands based on their names.
//! * Parsing tokenized input to select a command. Input tokenization can
//!   be performed by the [`commands::tokenizer`] module.
//! * Validating parameters.
//! * Performing completion on commands and parameters.
//!
//! The command parser consists of two important things:
//!
//! * A tree that represents the available commands and their arguments.
//!   This tree consists of instances of implementations of [`Node`] like
//!   [`CommandNode`], [`ParameterNode`] and [`RootNode`]. Construction of this
//!   tree is done with the help of [`CommandTree`], [`Command`] and
//!   [`Parameter`].
//! * A [`Parser`] that handles input and matches it against the command
//!   tree. This parser is intended to be short-lived and to just live
//!   for the duration of parsing and evaluating a single command line
//!   input.
//!
//! ## Building Nodes
//!
//! Building a tree of nodes for use with the parser is best done with
//! the [`CommandTree`] in conjunction with [`Command`] and [`Parameter`].
//!
//! Start by creating a mutable [`CommandTree`] instance:
//!
//! ```
//! use commands::parser::{CommandTree, Parser};
//!
//! let mut tree = CommandTree::new();
//! ```
//!
//! Then, add your commands and arguments, and finally,
//! call `finalize` on the tree to get back a [`RootNode`]
//! that can use be used with a [`Parser`].
//!
//! ```
//! use commands::parser::{Command, CommandTree, Parameter, Parser};
//!
//! let mut tree = CommandTree::new();
//! tree.command(Command::new("again")
//!                  .hidden(false)
//!                  .parameter(Parameter::new("test")
//!                                 .required(false)
//!                                 .help("This is just a test parameter.")));
//! let root = tree.finalize();
//! let mut parser = Parser::new(root);
//! ```
//!
//! [`commands::tokenizer`]: ../tokenizer/index.html
//! [`Command`]: struct.Command.html
//! [`CommandNode`]: struct.CommandNode.html
//! [`CommandTree`]: struct.CommandTree.html
//! [`Node`]: trait.Node.html
//! [`Parameter`]: struct.Parameter.html
//! [`ParameterNode`]: trait.ParameterNode.html
//! [`Parser`]: struct.Parser.html
//! [`RootNode`]: struct.RootNode.html

mod builder;
mod completion;
mod constants;
mod nodes;

// Re-export public API
pub use self::builder::{Command, CommandTree, Parameter};
pub use self::constants::ParameterKind;
pub use self::constants::{PRIORITY_DEFAULT, PRIORITY_MINIMUM, PRIORITY_PARAMETER};
pub use self::completion::{Completion, CompletionOption};
pub use self::nodes::{Node, NodeOps};
pub use self::nodes::{CommandNode, ParameterNameNode, ParameterNode, RootNode};

use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
use tokenizer::{Token, TokenType};

/// Command parser
///
/// The lifetime parameter `'text` refers to the lifetime of the
/// tokens passed into the parser. This is the same as the lifetime
/// of the text used to create the tokens.
///
/// When creating a `Parser`, you must give it an `Rc<RootNode>`.
/// [`RootNode`] instances should be created using a [`CommandTree`].
///
/// ```
/// use commands::parser::{Command, CommandTree, Parser};
///
/// let mut tree = CommandTree::new();
/// tree.command(Command::new("show"));
/// tree.command(Command::new("set"));
/// tree.command(Command::new("help"));
///
/// let mut parser = Parser::new(tree.finalize());
/// ```
///
/// The parser is constructed as a `mut`able object as most of
/// the methods on it will modify its state.
///
/// [`CommandTree`]: struct.CommandTree.html
/// ['RootNode`]: struct.RootNode.html
pub struct Parser<'text> {
    current_node: Rc<Node>,
    /// The nodes which have been accepted during `parse` or `advance`.
    pub nodes: Vec<Rc<Node>>,
    /// The tokens which have been accepted during `parse` or `advance`.
    pub tokens: Vec<Token<'text>>,
    commands: Vec<Rc<Node>>,
    parameters: HashMap<String, String>,
}

impl<'text> Parser<'text> {
    /// Construct a parser with a root node.
    pub fn new(initial_node: Rc<Node>) -> Parser<'text> {
        Parser {
            current_node: initial_node,
            nodes: vec![],
            tokens: vec![],
            commands: vec![],
            parameters: HashMap::new(),
        }
    }

    /// Given an optional token, get the possible valid completions
    /// for the current parser state.
    ///
    /// Possible completions are successors of the current node which
    /// are not `hidden`, are `acceptable`, and which match the token,
    /// if one has been provided.
    ///
    /// Nodes may customize the `Complete` trait to customize the
    /// [`Completion`] and [`CompletionOption`]s which are generated
    /// for that node.
    ///
    /// Each valid successor node will have one [`Completion`] in the
    /// result vector. Each [`Completion`] will have one or more
    /// [`CompletionOption`] for each valid way that the value may be
    /// entered.
    ///
    /// ```
    /// use commands::parser::{Command, CommandTree, Parser};
    /// use commands::tokenizer::{Token, tokenize};
    ///
    /// let mut tree = CommandTree::new();
    /// tree.command(Command::new("show"));
    /// tree.command(Command::new("set"));
    /// tree.command(Command::new("help"));
    ///
    /// let mut parser = Parser::new(tree.finalize());
    ///
    /// // Completing now should have 3 options, 1 for each command.
    /// let comps = parser.complete(None);
    /// assert_eq!(comps.len(), 3);
    ///
    /// // But completing with a token for 'h' should have 1 option.
    /// if let Ok(tokens) = tokenize("h") {
    ///   let comps = parser.complete(Some(tokens[0]));
    ///   assert_eq!(comps.len(), 1);
    ///   assert_eq!(comps[0].options.len(), 1);
    ///   assert_eq!(comps[0].options[0].option_string, "help");
    /// } else {
    ///   panic!("Tokenize failed.");
    /// }
    ///
    /// // And completing for 's' should have 2 options.
    /// if let Ok(tokens) = tokenize("s") {
    ///   let comps = parser.complete(Some(tokens[0]));
    ///   assert_eq!(comps.len(), 2);
    /// } else {
    ///   panic!("Tokenize failed.");
    /// }
    /// ```
    ///
    /// [`Completion`]: struct.Completion.html
    /// [`CompletionOption`]: struct.CompletionOption.html
    pub fn complete(&self, token: Option<Token<'text>>) -> Vec<Completion> {
        self.current_node
            .successors()
            .into_iter()
            .filter(|n| {
                // To be a possible completion, the node should not be
                // hidden, it should be acceptable, and if there's a token,
                // it should be a valid match for the node.
                !n.node().hidden && n.acceptable(self, n) &&
                if let Some(t) = token {
                    n.matches(self, t)
                } else {
                    true
                }
            })
            .map(|n| n.complete(token))
            .collect::<Vec<_>>()
    }

    /// Parse a vector of tokens, advancing through the
    /// node hierarchy.
    ///
    /// ```
    /// use commands::parser::{Command, CommandTree, Parser};
    /// use commands::tokenizer::tokenize;
    ///
    /// let mut tree = CommandTree::new();
    /// tree.command(Command::new("show interface"));
    ///
    /// let mut parser = Parser::new(tree.finalize());
    ///
    /// if let Ok(tokens) = tokenize("show interface") {
    ///     parser.parse(tokens);
    /// }
    /// ```
    pub fn parse(&mut self, tokens: Vec<Token<'text>>) -> Result<(), ParseError<'text>> {
        for token in tokens {
            match token.token_type {
                TokenType::Whitespace => {}
                TokenType::Word => try!(self.advance(token)),
            }
        }
        Ok(())
    }

    /// Parse a single token, advancing through the node hierarchy.
    pub fn advance(&mut self, token: Token<'text>) -> Result<(), ParseError<'text>> {
        let acceptable = self.current_node
                             .successors()
                             .into_iter()
                             .filter(|n| n.acceptable(self, n))
                             .map(|n| n.clone())
                             .collect::<Vec<_>>();
        let matches = acceptable.clone()
                                .into_iter()
                                .filter(|n| n.matches(self, token))
                                .map(|n| n.clone())
                                .collect::<Vec<_>>();
        match matches.len() {
            1 => {
                let matching_node = &matches[0];
                matching_node.accept(self, token, matching_node);
                self.current_node = matching_node.clone();
                self.nodes.push(matching_node.clone());
                self.tokens.push(token);
                Ok(())
            }
            0 => Err(ParseError::NoMatches(token, acceptable)),
            _ => Err(ParseError::AmbiguousMatch(token, matches)),
        }
    }

    /// Execute the command that has been accepted by the parser.
    ///
    /// * XXX: This should be returning a Result probably.
    pub fn execute(&self) {
        if !self.commands.is_empty() {
            unimplemented!();
            // self.commands[0].execute(self)
        }
    }

    /// Verify that the parser is in a valid state with
    /// respect to having accepted a command and all
    /// required parameters.
    pub fn verify(&self) -> Result<(), VerifyError> {
        if self.commands.is_empty() {
            Err(VerifyError::NoCommandAccepted)
        } else {
            if let Node::Command(ref command) = *self.commands[0] {
                for expected in &command.parameters {
                    if let Node::Parameter(ref param) = **expected {
                        let name = &param.node.name;
                        if param.required && !self.parameters.contains_key(name) {
                            return Err(VerifyError::MissingParameter(name.clone()));
                        }
                    } else {
                        unreachable!();
                    }
                }
                Ok(())
            } else {
                unreachable!();
            }
        }
    }
}

/// Errors that calling `parse` on the `Parser` can raise.
#[derive(Clone)]
pub enum ParseError<'text> {
    /// There were no matches for the token.
    NoMatches(Token<'text>, Vec<Rc<Node>>),
    /// There was more than 1 possible match for the token.
    AmbiguousMatch(Token<'text>, Vec<Rc<Node>>),
}

impl<'text> fmt::Debug for ParseError<'text> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::NoMatches(token, _) => write!(f, "NoMatches({:?}, ...)", token),
            ParseError::AmbiguousMatch(token, _) => write!(f, "AmbiguousMatch({:?}, ...)", token),
        }
    }
}

impl<'text> Error for ParseError<'text> {
    fn description(&self) -> &str {
        match *self {
            ParseError::NoMatches(_, _) => "No match.",
            ParseError::AmbiguousMatch(_, _) => "Ambiguous match.",
        }
    }
}

impl<'text> fmt::Display for ParseError<'text> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Errors that calling `verify` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum VerifyError {
    /// No command has been accepted by the parser.
    NoCommandAccepted,
    /// A required parameter is missing.
    MissingParameter(String),
}

impl Error for VerifyError {
    fn description(&self) -> &str {
        match *self {
            VerifyError::NoCommandAccepted => "No command has been accepted by the parser.",
            VerifyError::MissingParameter(_) => "A required parameter is missing.",
        }
    }
}

impl fmt::Display for VerifyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

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

    #[test]
    #[should_panic]
    fn verify_signals_no_command() {
        let root = CommandTree::new().finalize();
        let parser = Parser::new(root);
        match parser.verify() {
            Err(VerifyError::NoCommandAccepted) => panic!(),
            _ => {}
        }
    }

    #[test]
    #[should_panic]
    fn parse_signals_no_matches() {
        let mut tree = CommandTree::new();
        tree.command(Command::new("show"));
        let mut parser = Parser::new(tree.finalize());
        if let Ok(tokens) = tokenize("h") {
            match parser.parse(tokens) {
                Err(ParseError::NoMatches(_, _)) => panic!(),
                _ => {}
            }
        }
    }

    #[test]
    #[should_panic]
    fn parse_signals_ambiguous_match() {
        let mut tree = CommandTree::new();
        tree.command(Command::new("show"));
        tree.command(Command::new("set"));
        let mut parser = Parser::new(tree.finalize());
        if let Ok(tokens) = tokenize("s") {
            match parser.parse(tokens) {
                Err(ParseError::AmbiguousMatch(_, _)) => panic!(),
                _ => {}
            }
        }
    }
}
<|endoftext|>


<commit_before>use time;
use replies::ReplyRenderer;

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Article {
    pub title: String,
    pub description: String,
    pub url: String,
    pub image: String,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ArticlesReply {
    pub source: String,
    pub target: String,
    pub time: i64,
    pub articles: Vec<Article>,
}

impl Article {
    #[inline]
    pub fn new(title: &str, url: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: "".to_owned(),
            description: "".to_owned(),
        }
    }

    #[inline]
    pub fn with_image(title: &str, url: &str, image: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: image.to_owned(),
            description: "".to_owned(),
        }
    }

    #[inline]
    pub fn with_description(title: &str, url: &str, description: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: "".to_owned(),
            description: description.to_owned(),
        }
    }

    fn render(&self) -> String {
        format!("<item>\n
            <Title><![CDATA[{title}]]></Title>\n\
            <Description><![CDATA[{description}]]></Description>\n\
            <PicUrl><![CDATA[{picurl}]]></PicUrl>\n\
            <Url><![CDATA[{url}]]></Url>\n\
            </item>",
            title=self.title,
            description=self.description,
            picurl=self.image,
            url=self.url,
        )
    }
}

impl ArticlesReply {
    #[inline]
    pub fn new(source: &str, target: &str) -> ArticlesReply {
        ArticlesReply {
            source: source.to_owned(),
            target: target.to_owned(),
            time: time::get_time().sec,
            articles: vec![],
        }
    }

    #[inline]
    pub fn with_articles(source: &str, target: &str, articles: &[Article]) -> ArticlesReply {
        ArticlesReply {
            source: source.to_owned(),
            target: target.to_owned(),
            time: time::get_time().sec,
            articles: articles.to_vec(),
        }
    }

    pub fn add_article(&mut self, article: Article) -> bool {
        if self.articles.len() >= 10 {
            return false
        }
        self.articles.push(article);
        true
    }
}

impl ReplyRenderer for ArticlesReply {
    #[inline]
    fn render(&self) -> String {
        let mut articles = vec![];
        for article in self.articles.iter() {
            articles.push(article.render());
        }
        let articles_str = articles.connect("\n");
        format!("<xml>\n\
            <ToUserName><![CDATA[{target}]]></ToUserName>\n\
            <FromUserName><![CDATA[{source}]]></FromUserName>\n\
            <CreateTime>{time}</CreateTime>\n\
            <MsgType><![CDATA[news]]></MsgType>\n\
            <ArticleCount>{count}</ArticleCount>\n\
            <Articles>{articles}</Articles>\n\
            </xml>",
            target=self.target,
            source=self.source,
            time=self.time,
            count=self.articles.len(),
            articles=articles_str,
        )
    }
}

#[cfg(test)]
mod tests {
    use replies::ReplyRenderer;
    use super::{Article, ArticlesReply};

    #[test]
    fn test_render_text_reply() {
        let mut reply = ArticlesReply::new("test1", "test2");
        let article1 = Article::new("test3", "test4");
        let article2 = Article::with_image("test5", "test6", "test7");
        reply.add_article(article1);
        reply.add_article(article2);
        let rendered = reply.render();

        assert!(rendered.contains("test1"));
        assert!(rendered.contains("test2"));
        assert!(rendered.contains("test3"));
        assert!(rendered.contains("test4"));
        assert!(rendered.contains("test5"));
        assert!(rendered.contains("test6"));
        assert!(rendered.contains("test7"));
    }
}<commit_msg>Bug fix<commit_after>use time;
use replies::ReplyRenderer;

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Article {
    pub title: String,
    pub description: String,
    pub url: String,
    pub image: String,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ArticlesReply {
    pub source: String,
    pub target: String,
    pub time: i64,
    pub articles: Vec<Article>,
}

impl Article {
    #[inline]
    pub fn new(title: &str, url: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: "".to_owned(),
            description: "".to_owned(),
        }
    }

    #[inline]
    pub fn with_image(title: &str, url: &str, image: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: image.to_owned(),
            description: "".to_owned(),
        }
    }

    #[inline]
    pub fn with_description(title: &str, url: &str, description: &str) -> Article {
        Article {
            title: title.to_owned(),
            url: url.to_owned(),
            image: "".to_owned(),
            description: description.to_owned(),
        }
    }

    fn render(&self) -> String {
        format!("<item>\n
            <Title><![CDATA[{title}]]></Title>\n\
            <Description><![CDATA[{description}]]></Description>\n\
            <PicUrl><![CDATA[{picurl}]]></PicUrl>\n\
            <Url><![CDATA[{url}]]></Url>\n\
            </item>",
            title=self.title,
            description=self.description,
            picurl=self.image,
            url=self.url,
        )
    }
}

impl ArticlesReply {
    #[inline]
    pub fn new(source: &str, target: &str) -> ArticlesReply {
        ArticlesReply {
            source: source.to_owned(),
            target: target.to_owned(),
            time: time::get_time().sec,
            articles: vec![],
        }
    }

    #[inline]
    pub fn with_articles(source: &str, target: &str, articles: &[Article]) -> ArticlesReply {
        ArticlesReply {
            source: source.to_owned(),
            target: target.to_owned(),
            time: time::get_time().sec,
            articles: articles.to_vec(),
        }
    }

    pub fn add_article(&mut self, article: Article) -> bool {
        if self.articles.len() >= 10 {
            return false;
        }
        self.articles.push(article);
        true
    }
}

impl ReplyRenderer for ArticlesReply {
    #[inline]
    fn render(&self) -> String {
        let mut articles = vec![];
        for article in self.articles.iter() {
            articles.push(article.render());
        }
        let articles_str = articles.connect("\n");
        format!("<xml>\n\
            <ToUserName><![CDATA[{target}]]></ToUserName>\n\
            <FromUserName><![CDATA[{source}]]></FromUserName>\n\
            <CreateTime>{time}</CreateTime>\n\
            <MsgType><![CDATA[news]]></MsgType>\n\
            <ArticleCount>{count}</ArticleCount>\n\
            <Articles>{articles}</Articles>\n\
            </xml>",
            target=self.target,
            source=self.source,
            time=self.time,
            count=self.articles.len(),
            articles=articles_str,
        )
    }
}

#[cfg(test)]
mod tests {
    use replies::ReplyRenderer;
    use super::{Article, ArticlesReply};

    #[test]
    fn test_render_text_reply() {
        let mut reply = ArticlesReply::new("test1", "test2");
        let article1 = Article::new("test3", "test4");
        let article2 = Article::with_image("test5", "test6", "test7");
        reply.add_article(article1);
        reply.add_article(article2);
        let rendered = reply.render();

        assert!(rendered.contains("test1"));
        assert!(rendered.contains("test2"));
        assert!(rendered.contains("test3"));
        assert!(rendered.contains("test4"));
        assert!(rendered.contains("test5"));
        assert!(rendered.contains("test6"));
        assert!(rendered.contains("test7"));
    }
}
<|endoftext|>


<commit_before>use error::Error;
use http;
use resources::{Address, Card, CardParams};
use params::Metadata;

#[derive(Serialize)]
pub struct OwnerParams {
    #[serde(skip_serializing_if = "Option::is_none")] pub address: Option<Address>,
    #[serde(skip_serializing_if = "Option::is_none")] pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub phone: Option<String>,
}

#[derive(Serialize)]
pub struct RedirectParams {
    return_url: String,
}

#[derive(Serialize)]
pub struct SourceParams {
    #[serde(rename = "type")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_type: Option<&'static str>,

    #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")] pub currency: Option<String>, // eg. "usd"
    #[serde(skip_serializing_if = "Option::is_none")] pub flow: Option<String>, // (redirect, receiver, code_verification, none)
    #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option<Metadata>,
    #[serde(skip_serializing_if = "Option::is_none")] pub owner: Option<OwnerParams>,
    #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option<RedirectParams>,
    #[serde(skip_serializing_if = "Option::is_none")] pub token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub usage: Option<String>, // (reusable, single-use)
}

#[derive(Debug, Deserialize)]
#[serde(tag = "object")]
pub enum Source {
    // BitcoinReceiver(...),

    #[serde(rename = "card")]
    Card(Card),
}

impl Source {
    pub fn create(params: SourceParams, key: &str) -> Result<Source, Error> {
        return http::post("/sources", key, params);
    }

    pub fn get(source_id: &str, key: &str) -> Result<Source, Error> {
        return http::get(&format!("/sources/{}", source_id), key);
    }

    pub fn update(source_id: &str, params: SourceParams, key: &str) -> Result<Source, Error> {
        return http::post(&format!("/source/{}", source_id), key, params);
    }
}
<commit_msg>Remove stray import in resources/source<commit_after>use error::Error;
use http;
use resources::{Address, Card};
use params::Metadata;

#[derive(Serialize)]
pub struct OwnerParams {
    #[serde(skip_serializing_if = "Option::is_none")] pub address: Option<Address>,
    #[serde(skip_serializing_if = "Option::is_none")] pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub phone: Option<String>,
}

#[derive(Serialize)]
pub struct RedirectParams {
    return_url: String,
}

#[derive(Serialize)]
pub struct SourceParams {
    #[serde(rename = "type")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_type: Option<&'static str>,

    #[serde(skip_serializing_if = "Option::is_none")] pub amount: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")] pub currency: Option<String>, // eg. "usd"
    #[serde(skip_serializing_if = "Option::is_none")] pub flow: Option<String>, // (redirect, receiver, code_verification, none)
    #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option<Metadata>,
    #[serde(skip_serializing_if = "Option::is_none")] pub owner: Option<OwnerParams>,
    #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option<RedirectParams>,
    #[serde(skip_serializing_if = "Option::is_none")] pub token: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")] pub usage: Option<String>, // (reusable, single-use)
}

#[derive(Debug, Deserialize)]
#[serde(tag = "object")]
pub enum Source {
    // BitcoinReceiver(...),

    #[serde(rename = "card")]
    Card(Card),
}

impl Source {
    pub fn create(params: SourceParams, key: &str) -> Result<Source, Error> {
        return http::post("/sources", key, params);
    }

    pub fn get(source_id: &str, key: &str) -> Result<Source, Error> {
        return http::get(&format!("/sources/{}", source_id), key);
    }

    pub fn update(source_id: &str, params: SourceParams, key: &str) -> Result<Source, Error> {
        return http::post(&format!("/source/{}", source_id), key, params);
    }
}
<|endoftext|>


<commit_before>mod exponent;
mod mantissa;

use core::{mem, ptr};

use self::exponent::*;
use self::mantissa::*;
use d2s;
use d2s::*;
use f2s;
use f2s::*;

#[cfg(feature = "no-panic")]
use no_panic::no_panic;

/// Print f64 to the given buffer and return number of bytes written.
///
/// At most 24 bytes will be written.
///
/// ## Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
///
/// ## Safety
///
/// The `result` pointer argument must point to sufficiently many writable bytes
/// to hold Ryū's representation of `f`.
///
/// ## Example
///
/// ```edition2018
/// let f = 1.234f64;
///
/// unsafe {
///     let mut buffer: [u8; 24] = std::mem::uninitialized();
///     let n = ryu::raw::format64(f, &mut buffer[0]);
///     let s = std::str::from_utf8_unchecked(&buffer[..n]);
///     assert_eq!(s, "1.234");
/// }
/// ```
#[cfg_attr(must_use_return, must_use)]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn format64(f: f64, result: *mut u8) -> usize {
    let bits = mem::transmute::<f64, u64>(f);
    let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
    let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
    let ieee_exponent =
        (bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);

    let mut index = 0isize;
    if sign {
        *result = b'-';
        index += 1;
    }

    if ieee_exponent == 0 && ieee_mantissa == 0 {
        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
        return sign as usize + 3;
    }

    let v = d2d(ieee_mantissa, ieee_exponent);

    let length = d2s::decimal_length(v.mantissa) as isize;
    let k = v.exponent as isize;
    let kk = length + k; // 10^(kk-1) <= v < 10^kk
    debug_assert!(k >= -324);

    if 0 <= k && kk <= 16 {
        // 1234e7 -> 12340000000.0
        write_mantissa_long(v.mantissa, result.offset(index + length));
        for i in length..kk {
            *result.offset(index + i) = b'0';
        }
        *result.offset(index + kk) = b'.';
        *result.offset(index + kk + 1) = b'0';
        index as usize + kk as usize + 2
    } else if 0 < kk && kk <= 16 {
        // 1234e-2 -> 12.34
        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
        *result.offset(index + kk) = b'.';
        index as usize + length as usize + 1
    } else if -5 < kk && kk <= 0 {
        // 1234e-6 -> 0.001234
        *result.offset(index) = b'0';
        *result.offset(index + 1) = b'.';
        let offset = 2 - kk;
        for i in 2..offset {
            *result.offset(index + i) = b'0';
        }
        write_mantissa_long(v.mantissa, result.offset(index + length + offset));
        index as usize + length as usize + offset as usize
    } else if length == 1 {
        // 1e30
        *result.offset(index) = b'0' + v.mantissa as u8;
        *result.offset(index + 1) = b'e';
        index as usize + 2 + write_exponent3(kk - 1, result.offset(index + 2))
    } else {
        // 1234e30 -> 1.234e33
        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
        *result.offset(index) = *result.offset(index + 1);
        *result.offset(index + 1) = b'.';
        *result.offset(index + length + 1) = b'e';
        index as usize
            + length as usize
            + 2
            + write_exponent3(kk - 1, result.offset(index + length + 2))
    }
}

/// Print f32 to the given buffer and return number of bytes written.
///
/// At most 16 bytes will be written.
///
/// ## Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_finite
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_nan
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_infinite
///
/// ## Safety
///
/// The `result` pointer argument must point to sufficiently many writable bytes
/// to hold Ryū's representation of `f`.
///
/// ## Example
///
/// ```edition2018
/// let f = 1.234f32;
///
/// unsafe {
///     let mut buffer: [u8; 16] = std::mem::uninitialized();
///     let n = ryu::raw::format32(f, &mut buffer[0]);
///     let s = std::str::from_utf8_unchecked(&buffer[..n]);
///     assert_eq!(s, "1.234");
/// }
/// ```
#[cfg_attr(must_use_return, must_use)]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn format32(f: f32, result: *mut u8) -> usize {
    let bits = mem::transmute::<f32, u32>(f);
    let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0;
    let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1);
    let ieee_exponent =
        ((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32;

    let mut index = 0isize;
    if sign {
        *result = b'-';
        index += 1;
    }

    if ieee_exponent == 0 && ieee_mantissa == 0 {
        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
        return sign as usize + 3;
    }

    let v = f2d(ieee_mantissa, ieee_exponent);

    let length = f2s::decimal_length(v.mantissa) as isize;
    let k = v.exponent as isize;
    let kk = length + k; // 10^(kk-1) <= v < 10^kk
    debug_assert!(k >= -45);

    if 0 <= k && kk <= 13 {
        // 1234e7 -> 12340000000.0
        write_mantissa(v.mantissa, result.offset(index + length));
        for i in length..kk {
            *result.offset(index + i) = b'0';
        }
        *result.offset(index + kk) = b'.';
        *result.offset(index + kk + 1) = b'0';
        index as usize + kk as usize + 2
    } else if 0 < kk && kk <= 13 {
        // 1234e-2 -> 12.34
        write_mantissa(v.mantissa, result.offset(index + length + 1));
        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
        *result.offset(index + kk) = b'.';
        index as usize + length as usize + 1
    } else if -6 < kk && kk <= 0 {
        // 1234e-6 -> 0.001234
        *result.offset(index) = b'0';
        *result.offset(index + 1) = b'.';
        let offset = 2 - kk;
        for i in 2..offset {
            *result.offset(index + i) = b'0';
        }
        write_mantissa(v.mantissa, result.offset(index + length + offset));
        index as usize + length as usize + offset as usize
    } else if length == 1 {
        // 1e30
        *result.offset(index) = b'0' + v.mantissa as u8;
        *result.offset(index + 1) = b'e';
        index as usize + 2 + write_exponent2(kk - 1, result.offset(index + 2))
    } else {
        // 1234e30 -> 1.234e33
        write_mantissa(v.mantissa, result.offset(index + length + 1));
        *result.offset(index) = *result.offset(index + 1);
        *result.offset(index + 1) = b'.';
        *result.offset(index + length + 1) = b'e';
        index as usize
            + length as usize
            + 2
            + write_exponent2(kk - 1, result.offset(index + length + 2))
    }
}
<commit_msg>Remove bounds checks in raw doc<commit_after>mod exponent;
mod mantissa;

use core::{mem, ptr};

use self::exponent::*;
use self::mantissa::*;
use d2s;
use d2s::*;
use f2s;
use f2s::*;

#[cfg(feature = "no-panic")]
use no_panic::no_panic;

/// Print f64 to the given buffer and return number of bytes written.
///
/// At most 24 bytes will be written.
///
/// ## Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
///
/// ## Safety
///
/// The `result` pointer argument must point to sufficiently many writable bytes
/// to hold Ryū's representation of `f`.
///
/// ## Example
///
/// ```edition2018
/// let f = 1.234f64;
///
/// unsafe {
///     let mut buffer: [u8; 24] = std::mem::uninitialized();
///     let len = ryu::raw::format64(f, buffer.as_mut_ptr());
///     let slice = std::slice::from_raw_parts(buffer.as_ptr(), len);
///     let print = std::str::from_utf8_unchecked(slice);
///     assert_eq!(print, "1.234");
/// }
/// ```
#[cfg_attr(must_use_return, must_use)]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn format64(f: f64, result: *mut u8) -> usize {
    let bits = mem::transmute::<f64, u64>(f);
    let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
    let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
    let ieee_exponent =
        (bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);

    let mut index = 0isize;
    if sign {
        *result = b'-';
        index += 1;
    }

    if ieee_exponent == 0 && ieee_mantissa == 0 {
        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
        return sign as usize + 3;
    }

    let v = d2d(ieee_mantissa, ieee_exponent);

    let length = d2s::decimal_length(v.mantissa) as isize;
    let k = v.exponent as isize;
    let kk = length + k; // 10^(kk-1) <= v < 10^kk
    debug_assert!(k >= -324);

    if 0 <= k && kk <= 16 {
        // 1234e7 -> 12340000000.0
        write_mantissa_long(v.mantissa, result.offset(index + length));
        for i in length..kk {
            *result.offset(index + i) = b'0';
        }
        *result.offset(index + kk) = b'.';
        *result.offset(index + kk + 1) = b'0';
        index as usize + kk as usize + 2
    } else if 0 < kk && kk <= 16 {
        // 1234e-2 -> 12.34
        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
        *result.offset(index + kk) = b'.';
        index as usize + length as usize + 1
    } else if -5 < kk && kk <= 0 {
        // 1234e-6 -> 0.001234
        *result.offset(index) = b'0';
        *result.offset(index + 1) = b'.';
        let offset = 2 - kk;
        for i in 2..offset {
            *result.offset(index + i) = b'0';
        }
        write_mantissa_long(v.mantissa, result.offset(index + length + offset));
        index as usize + length as usize + offset as usize
    } else if length == 1 {
        // 1e30
        *result.offset(index) = b'0' + v.mantissa as u8;
        *result.offset(index + 1) = b'e';
        index as usize + 2 + write_exponent3(kk - 1, result.offset(index + 2))
    } else {
        // 1234e30 -> 1.234e33
        write_mantissa_long(v.mantissa, result.offset(index + length + 1));
        *result.offset(index) = *result.offset(index + 1);
        *result.offset(index + 1) = b'.';
        *result.offset(index + length + 1) = b'e';
        index as usize
            + length as usize
            + 2
            + write_exponent3(kk - 1, result.offset(index + length + 2))
    }
}

/// Print f32 to the given buffer and return number of bytes written.
///
/// At most 16 bytes will be written.
///
/// ## Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_finite
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_nan
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_infinite
///
/// ## Safety
///
/// The `result` pointer argument must point to sufficiently many writable bytes
/// to hold Ryū's representation of `f`.
///
/// ## Example
///
/// ```edition2018
/// let f = 1.234f32;
///
/// unsafe {
///     let mut buffer: [u8; 16] = std::mem::uninitialized();
///     let len = ryu::raw::format32(f, buffer.as_mut_ptr());
///     let slice = std::slice::from_raw_parts(buffer.as_ptr(), len);
///     let print = std::str::from_utf8_unchecked(slice);
///     assert_eq!(print, "1.234");
/// }
/// ```
#[cfg_attr(must_use_return, must_use)]
#[cfg_attr(feature = "no-panic", no_panic)]
pub unsafe fn format32(f: f32, result: *mut u8) -> usize {
    let bits = mem::transmute::<f32, u32>(f);
    let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0;
    let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1);
    let ieee_exponent =
        ((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32;

    let mut index = 0isize;
    if sign {
        *result = b'-';
        index += 1;
    }

    if ieee_exponent == 0 && ieee_mantissa == 0 {
        ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
        return sign as usize + 3;
    }

    let v = f2d(ieee_mantissa, ieee_exponent);

    let length = f2s::decimal_length(v.mantissa) as isize;
    let k = v.exponent as isize;
    let kk = length + k; // 10^(kk-1) <= v < 10^kk
    debug_assert!(k >= -45);

    if 0 <= k && kk <= 13 {
        // 1234e7 -> 12340000000.0
        write_mantissa(v.mantissa, result.offset(index + length));
        for i in length..kk {
            *result.offset(index + i) = b'0';
        }
        *result.offset(index + kk) = b'.';
        *result.offset(index + kk + 1) = b'0';
        index as usize + kk as usize + 2
    } else if 0 < kk && kk <= 13 {
        // 1234e-2 -> 12.34
        write_mantissa(v.mantissa, result.offset(index + length + 1));
        ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
        *result.offset(index + kk) = b'.';
        index as usize + length as usize + 1
    } else if -6 < kk && kk <= 0 {
        // 1234e-6 -> 0.001234
        *result.offset(index) = b'0';
        *result.offset(index + 1) = b'.';
        let offset = 2 - kk;
        for i in 2..offset {
            *result.offset(index + i) = b'0';
        }
        write_mantissa(v.mantissa, result.offset(index + length + offset));
        index as usize + length as usize + offset as usize
    } else if length == 1 {
        // 1e30
        *result.offset(index) = b'0' + v.mantissa as u8;
        *result.offset(index + 1) = b'e';
        index as usize + 2 + write_exponent2(kk - 1, result.offset(index + 2))
    } else {
        // 1234e30 -> 1.234e33
        write_mantissa(v.mantissa, result.offset(index + length + 1));
        *result.offset(index) = *result.offset(index + 1);
        *result.offset(index + 1) = b'.';
        *result.offset(index + length + 1) = b'e';
        index as usize
            + length as usize
            + 2
            + write_exponent2(kk - 1, result.offset(index + length + 2))
    }
}
<|endoftext|>


<commit_before>use hir::Semantics;
use ide_db::{
    base_db::{FileId, FilePosition, FileRange, SourceDatabase},
    RootDatabase, SymbolKind,
};
use syntax::TextRange;

use crate::{
    file_structure::file_structure,
    fn_references::find_all_methods,
    goto_implementation::goto_implementation,
    references::find_all_refs,
    runnables::{runnables, Runnable},
    NavigationTarget, RunnableKind,
};

// Feature: Annotations
//
// Provides user with annotations above items for looking up references or impl blocks
// and running/debugging binaries.
pub struct Annotation {
    pub range: TextRange,
    pub kind: AnnotationKind,
}

pub enum AnnotationKind {
    Runnable { debug: bool, runnable: Runnable },
    HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
    HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
}

pub struct AnnotationConfig {
    pub binary_target: bool,
    pub annotate_runnables: bool,
    pub annotate_impls: bool,
    pub annotate_references: bool,
    pub annotate_method_references: bool,
    pub run: bool,
    pub debug: bool,
}

pub(crate) fn annotations(
    db: &RootDatabase,
    file_id: FileId,
    config: AnnotationConfig,
) -> Vec<Annotation> {
    let mut annotations = Vec::default();

    if config.annotate_runnables {
        for runnable in runnables(db, file_id) {
            if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target {
                continue;
            }

            let action = runnable.action();
            let range = runnable.nav.full_range;

            if config.run {
                annotations.push(Annotation {
                    range,
                    // FIXME: This one allocates without reason if run is enabled, but debug is disabled
                    kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
                });
            }

            if action.debugee && config.debug {
                annotations.push(Annotation {
                    range,
                    kind: AnnotationKind::Runnable { debug: true, runnable },
                });
            }
        }
    }

    file_structure(&db.parse(file_id).tree())
        .into_iter()
        .filter(|node| {
            matches!(
                node.kind,
                SymbolKind::Trait
                    | SymbolKind::Struct
                    | SymbolKind::Enum
                    | SymbolKind::Union
                    | SymbolKind::Const
            )
        })
        .for_each(|node| {
            if config.annotate_impls && node.kind != SymbolKind::Const {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasImpls {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }

            if config.annotate_references {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasReferences {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }
        });

    if config.annotate_method_references {
        annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
            range: method.range,
            kind: AnnotationKind::HasReferences {
                position: FilePosition { file_id, offset: method.range.start() },
                data: None,
            },
        }));
    }

    annotations
}

pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
    match annotation.kind {
        AnnotationKind::HasImpls { position, ref mut data } => {
            *data = goto_implementation(db, position).map(|range| range.info);
        }
        AnnotationKind::HasReferences { position, ref mut data } => {
            *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
                result
                    .references
                    .into_iter()
                    .map(|(_, access)| access.into_iter())
                    .flatten()
                    .map(|(range, _)| FileRange { file_id: position.file_id, range })
                    .collect()
            });
        }
        _ => {}
    };

    annotation
}
<commit_msg>Fix incorrect references annotation<commit_after>use hir::Semantics;
use ide_db::{
    base_db::{FileId, FilePosition, FileRange, SourceDatabase},
    RootDatabase, SymbolKind,
};
use syntax::TextRange;

use crate::{
    file_structure::file_structure,
    fn_references::find_all_methods,
    goto_implementation::goto_implementation,
    references::find_all_refs,
    runnables::{runnables, Runnable},
    NavigationTarget, RunnableKind,
};

// Feature: Annotations
//
// Provides user with annotations above items for looking up references or impl blocks
// and running/debugging binaries.
pub struct Annotation {
    pub range: TextRange,
    pub kind: AnnotationKind,
}

pub enum AnnotationKind {
    Runnable { debug: bool, runnable: Runnable },
    HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
    HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
}

pub struct AnnotationConfig {
    pub binary_target: bool,
    pub annotate_runnables: bool,
    pub annotate_impls: bool,
    pub annotate_references: bool,
    pub annotate_method_references: bool,
    pub run: bool,
    pub debug: bool,
}

pub(crate) fn annotations(
    db: &RootDatabase,
    file_id: FileId,
    config: AnnotationConfig,
) -> Vec<Annotation> {
    let mut annotations = Vec::default();

    if config.annotate_runnables {
        for runnable in runnables(db, file_id) {
            if !matches!(runnable.kind, RunnableKind::Bin) || !config.binary_target {
                continue;
            }

            let action = runnable.action();
            let range = runnable.nav.full_range;

            if config.run {
                annotations.push(Annotation {
                    range,
                    // FIXME: This one allocates without reason if run is enabled, but debug is disabled
                    kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
                });
            }

            if action.debugee && config.debug {
                annotations.push(Annotation {
                    range,
                    kind: AnnotationKind::Runnable { debug: true, runnable },
                });
            }
        }
    }

    file_structure(&db.parse(file_id).tree())
        .into_iter()
        .filter(|node| {
            matches!(
                node.kind,
                SymbolKind::Trait
                    | SymbolKind::Struct
                    | SymbolKind::Enum
                    | SymbolKind::Union
                    | SymbolKind::Const
            )
        })
        .for_each(|node| {
            if config.annotate_impls && node.kind != SymbolKind::Const {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasImpls {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }

            if config.annotate_references {
                annotations.push(Annotation {
                    range: node.node_range,
                    kind: AnnotationKind::HasReferences {
                        position: FilePosition { file_id, offset: node.navigation_range.start() },
                        data: None,
                    },
                });
            }
        });

    if config.annotate_method_references {
        annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
            range: method.range,
            kind: AnnotationKind::HasReferences {
                position: FilePosition { file_id, offset: method.range.start() },
                data: None,
            },
        }));
    }

    annotations
}

pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
    match annotation.kind {
        AnnotationKind::HasImpls { position, ref mut data } => {
            *data = goto_implementation(db, position).map(|range| range.info);
        }
        AnnotationKind::HasReferences { position, ref mut data } => {
            *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
                result
                    .references
                    .into_iter()
                    .map(|(file_id, access)| {
                        access.into_iter().map(move |(range, _)| FileRange { file_id, range })
                    })
                    .flatten()
                    .collect()
            });
        }
        _ => {}
    };

    annotation
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use crate::config::Config;
use crate::layout::{Layout, Tall};
use crate::layout::LayoutMsg;
use crate::stack::Stack;
use crate::xlib_window_system::XlibWindowSystem;
use std::cmp;
use x11::xlib::Window;
use serde::{Serialize, Deserialize};

pub struct WorkspaceInfo {
    pub tags: String,
    pub layout: String,
    pub current: bool,
    pub visible: bool,
    pub urgent: bool,
}

pub struct WorkspaceConfig {
    pub tag: String,
    pub screen: usize,
    pub layout: Box<dyn Layout>,
}

pub enum MoveOp {
    Up,
    Down,
    Swap,
}

#[derive(Serialize, Deserialize)]
pub struct Workspace {
    pub(crate) managed: Stack,
    pub(crate) unmanaged: Stack,
    pub(crate) tag: String,
    pub screen: usize,
    pub visible: bool,
}

impl Default for Workspace {
    fn default() -> Self {
        Self {
            managed: Stack::new(Some(Tall::new(1, 0.5, 0.05))),
            unmanaged: Stack::new(None),
            tag: String::new(),
            screen: 0,
            visible: false,
        }
    }
}

impl Workspace {
    pub fn deserialize(xws: &XlibWindowSystem, tag: &str, data: &str) -> Result<Workspace> {
        let mut data_iter = data.split(':');

        // get a list of all windows from the Xerver used for filterint obsolete windows
        let windows = xws.get_windows();

        let screen = data_iter.next()
            .ok_or_else(|| anyhow!("missing workspace screen data"))?
            .parse::<usize>()
            .context("failed to parse workspace screen value")?;

        let visible = data_iter.next()
            .ok_or_else(|| anyhow!("missing workspace visibility data"))?
            .parse::<bool>()
            .context("failed to parse workspace visibility value")?;

        let managed = Stack::deserialize(&mut data_iter, &windows)?;
        let unmanaged = Stack::deserialize(&mut data_iter, &windows)?;

        Ok(Workspace {
            managed,
            unmanaged,
            tag: tag.to_string(),
            screen,
            visible,
        })
    }

    pub fn all(&self) -> Vec<Window> {
        self.unmanaged.all_windows().iter().chain(self.managed.all_windows().iter()).copied().collect()
    }
/*
    fn all_visible(&self) -> Vec<Window> {
        self.unmanaged.visible.iter().chain(self.managed.stack.visible.iter()).copied().collect()
    }
*/
    fn all_urgent(&self) -> Vec<Window> {
        self.unmanaged.urgent.iter().chain(self.managed.urgent.iter()).copied().collect()
    }

    pub fn get_layout(&self) -> Option<&dyn Layout> {
        self.managed.layout.as_ref().map(|x| x.as_ref())
    }

    pub fn send_layout_message(&mut self, msg: LayoutMsg) {
        if let Some(layout) = self.managed.layout.as_mut() {
            layout.send_msg(msg);
        }
    }

    pub fn get_tag(&self) -> &str {
        &self.tag
    }

    pub fn get_screen(&self) -> usize {
        self.screen
    }

    pub fn set_screen(&mut self, screen: usize) {
        self.screen = screen
    }

    pub fn is_unmanaged(&self, window: Window) -> bool {
        self.unmanaged.contains(window)
    }

    pub fn is_managed(&self, window: Window) -> bool {
        self.managed.contains(window)
    }

    pub fn is_urgent(&self) -> bool {
        self.managed.is_urgent() || self.unmanaged.is_urgent()
    }

    pub fn is_visible(&self) -> bool {
        self.visible
    }

    pub fn focused_window(&self) -> Option<Window> {
        if self.unmanaged.focused_window().is_none() {
            self.managed.focused_window()
        } else {
            self.unmanaged.focused_window()
        }
    }

    pub fn add_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if !xws.is_window_floating(window) {
            debug!("Add Managed: {}", window);
            self.managed.add_window(window);

            if self.unmanaged.len() > 0 {
                debug!("Restacking");
                xws.restack_windows(self.all());
            }
        } else {
            self.unmanaged.add_window(window);
            debug!("Add Unmanaged: {}", window);
        }

        if self.visible {
            self.redraw(xws, config);
            xws.show_window(window);
        }
    }

    pub fn nest_layout(&mut self, layout: Box<dyn Layout>) {
        if self.managed.len() > 1 {
            self.managed.add_container(layout);
        } else  if let Some(s) = self.managed.all_container_mut().first_mut() {
            if s.len() < 1 {
                s.layout = Some(layout);
            } else {
                self.managed.add_container(layout);
            }
        } else {
            self.managed.add_container(layout);
        }
    }

    pub fn set_urgency(&mut self, urgent: bool, xws: &XlibWindowSystem,  config: &Config, window: Window) {
        if !urgent {
            debug!("unset urgent {}", window);
            self.remove_urgent_window(window);
            self.redraw(xws, config);
        } else if urgent {
            debug!("set urgent {}", window);
            if self.is_managed(window) {
                self.managed.urgent.push(window);
            } else {
                self.unmanaged.urgent.push(window);
            }
            self.redraw(xws, config);
        }
    }

    fn remove_urgent_window(&mut self, window: Window) {
        let res = self.unmanaged.urgent
            .iter()
            .enumerate()
            .find(|&(_, &x)| x == window)
            .map(|(i, _)| i);

        if let Some(index) = res {
            if index < self.unmanaged.urgent.len() {
                self.unmanaged.urgent.remove(index - 1);
            }
        } else {
            self.managed.remove_urgent(window);
        }
    }

    fn remove_managed(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove managed window: {}", window);
        xws.unmap_window(window);
        self.managed.remove(window);

        if self.visible {
            self.redraw(xws, config);
        }
    }

    fn remove_unmanaged(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove unmanaged window: {}", window);
        xws.unmap_window(window);
        self.unmanaged.remove(window);
        self.unmanaged.focus = if self.unmanaged.nodes.is_empty() {
            None
        } else {
            Some(self.unmanaged.nodes.len() - 1)
        };

        if self.visible {
            self.redraw(xws, config);
        }
    }

    pub fn remove_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if self.managed.contains(window) {
            debug!("Remove Managed: {}", window);
            self.remove_managed(xws, config, window);
        } else if self.unmanaged.contains(window) {
            debug!("Remove Unmanaged: {}", window);
            self.remove_unmanaged(xws, config, window);
        }
    }
/*
    pub fn hide_window(&mut self, window: Window) {
        if self.managed.stack.contains(window) {
            self.managed.stack.hide(window);
        } else {
            self.unmanaged.hide(window);
        }
    }
*/
    pub fn focus_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if window == 0 || self.unmanaged.focused_window() == Some(window) || self.managed.focused_window() == Some(window) {
            return;
        }

        self.remove_window_highlight(xws, config);

        if self.unmanaged.contains(window) {
            self.unmanaged.focus_window(window);
        } else {
            self.managed.focus_window(window);
        }

        xws.focus_window(window, config.border_focus_color);
        self.redraw(xws, config);
    }

    pub fn remove_window_highlight(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn move_parent_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_parent_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    // TODO: impl managed and unmanaged focus mode incl switching
    pub fn move_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    pub fn move_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        let focused_window = self.focused_window();
        trace!("move window: {:?}", focused_window);

        if focused_window.is_none() {
            return;
        }

        self.managed.move_window(op);
        self.redraw(xws, config);
    }

    pub fn move_parent_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        let focused_window = self.focused_window();
        trace!("move parent window: {:?}", focused_window);

        if focused_window.is_none() {
            return;
        }

        self.managed.move_parent_window(op);
        self.redraw(xws, config);
    }

    pub fn contains(&self, window: Window) -> bool {
        self.all().iter().any(|&w| w == window)
    }

    pub fn unfocus(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("unfocus window: {}", window);
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn focus(&self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("focus window: {}", window);
            xws.focus_window(window, config.border_focus_color);
            xws.skip_enter_events();
        }
    }

    pub fn center_pointer(&self, xws: &XlibWindowSystem) {
        let screen = xws.get_screen_infos()[self.screen];
        xws.move_pointer((screen.x + (screen.width / 2)) as i32 - 1, (screen.y + (screen.height / 2)) as i32);
    }

    pub fn hide(&mut self, xws: &XlibWindowSystem) {
        self.visible = false;

        for &w in self.managed.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        for &w in self.unmanaged.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        if let Some(w) = self.focused_window() {
            xws.hide_window(w);
        }
    }

    pub fn show(&mut self, xws: &XlibWindowSystem, config: &Config) {
        self.visible = true;

        self.redraw(xws, config);
        for &w in self.managed.all_windows().iter() {
            xws.show_window(w);
        }

        for &w in self.unmanaged.all_windows().iter() {
            xws.show_window(w);
        }
    }

    pub fn redraw(&self, xws: &XlibWindowSystem, config: &Config) {
        trace!("Redraw...");

        let screen = xws.get_screen_infos()[self.screen];

        for (rect, window) in self.managed.apply_layout(screen, xws) {
            xws.setup_window(rect.x,
                rect.y,
                rect.width,
                rect.height,
                config.border_width,
                config.border_color,
                window);
        }

        for &window in self.unmanaged.all_windows().iter() {
            let mut rect = xws.get_geometry(window);
            rect.width = cmp::min(screen.width, rect.width + (2 * config.border_width));
            rect.height = cmp::min(screen.height, rect.height + (2 * config.border_width));

            xws.setup_window(screen.x + (screen.width - rect.width) / 2,
                            screen.y + (screen.height - rect.height) / 2,
                            rect.width,
                            rect.height,
                            config.border_width,
                            config.border_color,
                            window);
        }

        for &window in self.all_urgent().iter() {
            xws.set_window_border_color(window, config.border_urgent_color);
        }

        self.focus(xws, config);
    }
}
<commit_msg>Remove obsolete deserialize function<commit_after>#![allow(dead_code)]

use crate::config::Config;
use crate::layout::{Layout, Tall};
use crate::layout::LayoutMsg;
use crate::stack::Stack;
use crate::xlib_window_system::XlibWindowSystem;
use std::cmp;
use x11::xlib::Window;
use serde::{Serialize, Deserialize};

pub struct WorkspaceInfo {
    pub tags: String,
    pub layout: String,
    pub current: bool,
    pub visible: bool,
    pub urgent: bool,
}

pub struct WorkspaceConfig {
    pub tag: String,
    pub screen: usize,
    pub layout: Box<dyn Layout>,
}

pub enum MoveOp {
    Up,
    Down,
    Swap,
}

#[derive(Serialize, Deserialize)]
pub struct Workspace {
    pub(crate) managed: Stack,
    pub(crate) unmanaged: Stack,
    pub(crate) tag: String,
    pub screen: usize,
    pub visible: bool,
}

impl Default for Workspace {
    fn default() -> Self {
        Self {
            managed: Stack::new(Some(Tall::new(1, 0.5, 0.05))),
            unmanaged: Stack::new(None),
            tag: String::new(),
            screen: 0,
            visible: false,
        }
    }
}

impl Workspace {
    pub fn all(&self) -> Vec<Window> {
        self.unmanaged.all_windows().iter().chain(self.managed.all_windows().iter()).copied().collect()
    }

    fn all_urgent(&self) -> Vec<Window> {
        self.unmanaged.urgent.iter().chain(self.managed.urgent.iter()).copied().collect()
    }

    pub fn get_layout(&self) -> Option<&dyn Layout> {
        self.managed.layout.as_ref().map(|x| x.as_ref())
    }

    pub fn send_layout_message(&mut self, msg: LayoutMsg) {
        if let Some(layout) = self.managed.layout.as_mut() {
            layout.send_msg(msg);
        }
    }

    pub fn get_tag(&self) -> &str {
        &self.tag
    }

    pub fn get_screen(&self) -> usize {
        self.screen
    }

    pub fn set_screen(&mut self, screen: usize) {
        self.screen = screen
    }

    pub fn is_unmanaged(&self, window: Window) -> bool {
        self.unmanaged.contains(window)
    }

    pub fn is_managed(&self, window: Window) -> bool {
        self.managed.contains(window)
    }

    pub fn is_urgent(&self) -> bool {
        self.managed.is_urgent() || self.unmanaged.is_urgent()
    }

    pub fn is_visible(&self) -> bool {
        self.visible
    }

    pub fn focused_window(&self) -> Option<Window> {
        if self.unmanaged.focused_window().is_none() {
            self.managed.focused_window()
        } else {
            self.unmanaged.focused_window()
        }
    }

    pub fn add_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if !xws.is_window_floating(window) {
            debug!("Add Managed: {}", window);
            self.managed.add_window(window);

            if self.unmanaged.len() > 0 {
                debug!("Restacking");
                xws.restack_windows(self.all());
            }
        } else {
            self.unmanaged.add_window(window);
            debug!("Add Unmanaged: {}", window);
        }

        if self.visible {
            self.redraw(xws, config);
            xws.show_window(window);
        }
    }

    pub fn nest_layout(&mut self, layout: Box<dyn Layout>) {
        if self.managed.len() > 1 {
            self.managed.add_container(layout);
        } else  if let Some(s) = self.managed.all_container_mut().first_mut() {
            if s.len() < 1 {
                s.layout = Some(layout);
            } else {
                self.managed.add_container(layout);
            }
        } else {
            self.managed.add_container(layout);
        }
    }

    pub fn set_urgency(&mut self, urgent: bool, xws: &XlibWindowSystem,  config: &Config, window: Window) {
        if !urgent {
            debug!("unset urgent {}", window);
            self.remove_urgent_window(window);
            self.redraw(xws, config);
        } else if urgent {
            debug!("set urgent {}", window);
            if self.is_managed(window) {
                self.managed.urgent.push(window);
            } else {
                self.unmanaged.urgent.push(window);
            }
            self.redraw(xws, config);
        }
    }

    fn remove_urgent_window(&mut self, window: Window) {
        let res = self.unmanaged.urgent
            .iter()
            .enumerate()
            .find(|&(_, &x)| x == window)
            .map(|(i, _)| i);

        if let Some(index) = res {
            if index < self.unmanaged.urgent.len() {
                self.unmanaged.urgent.remove(index - 1);
            }
        } else {
            self.managed.remove_urgent(window);
        }
    }

    fn remove_managed(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove managed window: {}", window);
        xws.unmap_window(window);
        self.managed.remove(window);

        if self.visible {
            self.redraw(xws, config);
        }
    }

    fn remove_unmanaged(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        trace!("remove unmanaged window: {}", window);
        xws.unmap_window(window);
        self.unmanaged.remove(window);
        self.unmanaged.focus = if self.unmanaged.nodes.is_empty() {
            None
        } else {
            Some(self.unmanaged.nodes.len() - 1)
        };

        if self.visible {
            self.redraw(xws, config);
        }
    }

    pub fn remove_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if self.managed.contains(window) {
            debug!("Remove Managed: {}", window);
            self.remove_managed(xws, config, window);
        } else if self.unmanaged.contains(window) {
            debug!("Remove Unmanaged: {}", window);
            self.remove_unmanaged(xws, config, window);
        }
    }
/*
    pub fn hide_window(&mut self, window: Window) {
        if self.managed.stack.contains(window) {
            self.managed.stack.hide(window);
        } else {
            self.unmanaged.hide(window);
        }
    }
*/
    pub fn focus_window(&mut self, xws: &XlibWindowSystem, config: &Config, window: Window) {
        if window == 0 || self.unmanaged.focused_window() == Some(window) || self.managed.focused_window() == Some(window) {
            return;
        }

        self.remove_window_highlight(xws, config);

        if self.unmanaged.contains(window) {
            self.unmanaged.focus_window(window);
        } else {
            self.managed.focus_window(window);
        }

        xws.focus_window(window, config.border_focus_color);
        self.redraw(xws, config);
    }

    pub fn remove_window_highlight(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn move_parent_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_parent_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    // TODO: impl managed and unmanaged focus mode incl switching
    pub fn move_focus(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        if self.managed.move_focus(op).is_some() {
            self.redraw(xws, config);
        }
    }

    pub fn move_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        let focused_window = self.focused_window();
        trace!("move window: {:?}", focused_window);

        if focused_window.is_none() {
            return;
        }

        self.managed.move_window(op);
        self.redraw(xws, config);
    }

    pub fn move_parent_window(&mut self, xws: &XlibWindowSystem, config: &Config, op: MoveOp) {
        let focused_window = self.focused_window();
        trace!("move parent window: {:?}", focused_window);

        if focused_window.is_none() {
            return;
        }

        self.managed.move_parent_window(op);
        self.redraw(xws, config);
    }

    pub fn contains(&self, window: Window) -> bool {
        self.all().iter().any(|&w| w == window)
    }

    pub fn unfocus(&mut self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("unfocus window: {}", window);
            xws.set_window_border_color(window, config.border_color);
        }
    }

    pub fn focus(&self, xws: &XlibWindowSystem, config: &Config) {
        if let Some(window) = self.focused_window() {
            trace!("focus window: {}", window);
            xws.focus_window(window, config.border_focus_color);
            xws.skip_enter_events();
        }
    }

    pub fn center_pointer(&self, xws: &XlibWindowSystem) {
        let screen = xws.get_screen_infos()[self.screen];
        xws.move_pointer((screen.x + (screen.width / 2)) as i32 - 1, (screen.y + (screen.height / 2)) as i32);
    }

    pub fn hide(&mut self, xws: &XlibWindowSystem) {
        self.visible = false;

        for &w in self.managed.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        for &w in self.unmanaged.all_windows().iter().filter(|&w| Some(*w) != self.focused_window()) {
            xws.hide_window(w);
        }

        if let Some(w) = self.focused_window() {
            xws.hide_window(w);
        }
    }

    pub fn show(&mut self, xws: &XlibWindowSystem, config: &Config) {
        self.visible = true;

        self.redraw(xws, config);
        for &w in self.managed.all_windows().iter() {
            xws.show_window(w);
        }

        for &w in self.unmanaged.all_windows().iter() {
            xws.show_window(w);
        }
    }

    pub fn redraw(&self, xws: &XlibWindowSystem, config: &Config) {
        trace!("Redraw...");

        let screen = xws.get_screen_infos()[self.screen];

        for (rect, window) in self.managed.apply_layout(screen, xws) {
            xws.setup_window(rect.x,
                rect.y,
                rect.width,
                rect.height,
                config.border_width,
                config.border_color,
                window);
        }

        for &window in self.unmanaged.all_windows().iter() {
            let mut rect = xws.get_geometry(window);
            rect.width = cmp::min(screen.width, rect.width + (2 * config.border_width));
            rect.height = cmp::min(screen.height, rect.height + (2 * config.border_width));

            xws.setup_window(screen.x + (screen.width - rect.width) / 2,
                            screen.y + (screen.height - rect.height) / 2,
                            rect.width,
                            rect.height,
                            config.border_width,
                            config.border_color,
                            window);
        }

        for &window in self.all_urgent().iter() {
            xws.set_window_border_color(window, config.border_urgent_color);
        }

        self.focus(xws, config);
    }
}
<|endoftext|>