<commit_before>use collections::{Map, map};
use lr1::core::*;
use lr1::lookahead::*;
use lr1::example::*;
use grammar::repr::*;
use petgraph::{EdgeDirection, Graph};
use petgraph::graph::{Edges, NodeIndex};
use std::fmt::{Debug, Formatter, Error};

#[cfg(test)] mod test;

/// Trace graphs are used to summarize how it is that we came to be in
/// a state where we can take some particular shift/reduce action; put
/// another way, how it is that we came to be in a state with some
/// particular LR(1) item.
///
/// The nodes in the graph are each labeled with a TraceGraphNode and
/// hence take one of two forms:
///
/// - TraceGraphNode::Item -- represents an LR0 item. These nodes are
///   used for the starting/end points in the graph only.  Basically a
///   complete trace stretches from the start item to some end item,
///   and all intermediate nodes are nonterminals.
/// - TraceGraphNode::Nonterminal -- if this graph is for a shift,
///   then these represent items where the cursor is at the beginning:
///   `X = (*) ...`. If the graph is for a reduce, they represent
///   items where a reduce is possible without shifting any more
///   terminals (though further reductions may be needed): `X =
///   ... (*) ...s` where `FIRST(...s)` includes `\epsilon`.
///
/// The edges in the graph are also important. They are labeled with
/// `SymbolSets` instances, meaning that each carries a (prefix,
/// cursor, and suffix) tuple. The label on an edge `A -> B` means
/// that transitioning from a state containing `A` to a state
/// containing `B` is possible if you:
///
/// - shift the symbols in `prefix`
/// - `B` will produce the symbol in `cursor`
/// - shift the symbols in `suffix` after `B` is popped
pub struct TraceGraph<'grammar> {
    // A -L-> B means:
    //
    //     Transition from a state containing A to a state containing
    //     B by (pushing|popping) the symbols L.
    //
    // If this trace graph represents a shift backtrace, then the
    // labels are symbols that are pushed. Otherwise they are labels
    // that are popped.
    graph: Graph<TraceGraphNode<'grammar>, SymbolSets<'grammar>>,
    indices: Map<TraceGraphNode<'grammar>, NodeIndex>,
}

#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub enum TraceGraphNode<'grammar> {
    Nonterminal(NonterminalString),
    Item(LR0Item<'grammar>),
}

impl<'grammar> TraceGraph<'grammar> {
    pub fn new() -> TraceGraph<'grammar> {
        TraceGraph {
            graph: Graph::new(),
            indices: map(),
        }
    }

    pub fn add_node<T>(&mut self, node: T) -> NodeIndex
        where T: Into<TraceGraphNode<'grammar>>
    {
        let node = node.into();
        let graph = &mut self.graph;
        *self.indices.entry(node)
                     .or_insert_with(|| graph.add_node(node))
    }

    pub fn add_edge<F,T>(&mut self,
                         from: F,
                         to: T,
                         labels: SymbolSets<'grammar>)
        where F: Into<TraceGraphNode<'grammar>>,
              T: Into<TraceGraphNode<'grammar>>,
    {
        let from = self.add_node(from.into());
        let to = self.add_node(to.into());
        if !self.graph.edges_directed(from, EdgeDirection::Outgoing)
                      .any(|(t, &l)| t == to && l == labels)
        {
            self.graph.add_edge(from, to, labels);
        }
    }

    pub fn examples<'graph>(&'graph self,
                            lr0_item: LR0Item<'grammar>)
                            -> PathEnumerator<'graph, 'grammar>
    {
        PathEnumerator::new(self, lr0_item)
    }
}

impl<'grammar> Into<TraceGraphNode<'grammar>> for NonterminalString {
    fn into(self) -> TraceGraphNode<'grammar> {
        TraceGraphNode::Nonterminal(self)
    }
}

impl<'grammar, L: Lookahead> Into<TraceGraphNode<'grammar>> for Item<'grammar, L> {
    fn into(self) -> TraceGraphNode<'grammar> {
        TraceGraphNode::Item(self.to_lr0())
    }
}

// This just exists to help with the `Debug` impl
struct TraceGraphEdge<'grammar> {
    from: TraceGraphNode<'grammar>,
    to: TraceGraphNode<'grammar>,
    label: (&'grammar [Symbol], Option<&'grammar Symbol>, &'grammar [Symbol]),
}

impl<'grammar> Debug for TraceGraphEdge<'grammar> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt, "({:?} -{:?}-> {:?})", self.from, self.label, self.to)
    }
}

impl<'grammar> Debug for TraceGraph<'grammar> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        let mut s = fmt.debug_list();
        for (&node, &index) in &self.indices {
            for (target, label) in
                self.graph.edges_directed(index, EdgeDirection::Outgoing)
            {
                s.entry(&TraceGraphEdge { from: node,
                                          to: self.graph[target],
                                          label: (label.prefix,
                                                  label.cursor,
                                                  label.suffix) });
            }
        }
        s.finish()
    }
}

///////////////////////////////////////////////////////////////////////////
// PathEnumerator
//
// The path enumerater walks a trace graph searching for paths that
// start at a given item and terminate at another item. If such a path
// is found, you can then find the complete list of symbols by calling
// `symbols_and_cursor` and also get access to the state.

pub struct PathEnumerator<'graph, 'grammar: 'graph> {
    graph: &'graph TraceGraph<'grammar>,
    stack: Vec<EnumeratorState<'graph, 'grammar>>,

    // The list of symbols for the current item.
    symbols: Vec<Symbol>,

    cursor: usize,
}

struct EnumeratorState<'graph, 'grammar: 'graph> {
    index: NodeIndex,
    symbol_sets: SymbolSets<'grammar>,
    edges: Edges<'graph, SymbolSets<'grammar>>,
}

impl<'graph, 'grammar> PathEnumerator<'graph, 'grammar> {
    fn new(graph: &'graph TraceGraph<'grammar>,
           lr0_item: LR0Item<'grammar>)
           -> Self {
        let start_state = graph.indices[&TraceGraphNode::Item(lr0_item)];
        let mut enumerator = PathEnumerator {
            graph: graph,
            stack: vec![],
            symbols: vec![],
            cursor: 0,
        };
        let edges = enumerator.incoming_edges(start_state);
        enumerator.stack.push(EnumeratorState {
            index: start_state,
            symbol_sets: SymbolSets::new(),
            edges: edges,
        });
        enumerator.find_next_trace();
        enumerator
    }

    /// Advance to the next example. Returns false if there are no more
    /// examples.
    pub fn advance(&mut self) -> bool {
        // If we have not yet exhausted all the examples, then the top
        // of the stack should be the last target item that we
        // found. Pop it off.
        match self.stack.pop() {
            Some(top_state) => {
                assert!(match self.graph.graph[top_state.index] {
                    TraceGraphNode::Item(_) => true,
                    TraceGraphNode::Nonterminal(_) => false,
                });

                self.find_next_trace()
            }
            None => {
                false
            }
        }
    }

    fn incoming_edges(&self, index: NodeIndex) -> Edges<'graph, SymbolSets<'grammar>> {
        self.graph.graph.edges_directed(index, EdgeDirection::Incoming)
    }

    /// This is the main operation, written in CPS style and hence it
    /// can seem a bit confusing. The idea is that `find_next_trace`
    /// is called when we are ready to consider the next child of
    /// whatever is on the top of the stack. It simply withdraws
    /// that next child (if any) and hands it to `push_next`.
    fn find_next_trace(&mut self) -> bool {
        if !self.stack.is_empty() {
            let next_edge = {
                let top_of_stack = self.stack.last_mut().unwrap();
                top_of_stack.edges.next()
            };
            self.push_next_child_if_any(next_edge)
        } else {
            false
        }
    }

    /// Invoked with the next child (if any) of the node on the top of
    /// the stack.
    ///
    /// If `next` is `Some`, we simply call `push_next_child`.
    ///
    /// If `next` is `None`, then the node on the top of
    /// the stack *has* no next child, and so it is popped, and then
    /// we call `find_next_trace` again to start with the next child
    /// of the new top of the stack.
    fn push_next_child_if_any(&mut self,
                              next: Option<(NodeIndex, &'graph SymbolSets<'grammar>)>)
                              -> bool {
        if let Some((index, &symbol_sets)) = next {
            self.push_next_child(index, symbol_sets)
        } else {
            self.stack.pop();
            self.find_next_trace()
        }
    }

    /// Push the next child of the top of the stack onto the stack,
    /// making the child the new top.
    ///
    /// If the child is an `Item` node, we have found the next trace,
    /// and hence our search terminates. We push the symbols from this
    /// item node into the symbols vector and then call `found_trace`
    /// (which will ultimately return `true`).
    ///
    /// Otherwise, we check whether this new node would cause a cycle.
    /// If so, we do *not* push it, and instead just call
    /// `find_next_trace` again to proceed to the next child of the
    /// current top.
    ///
    /// Finally, if the new node would NOT cause a cycle, then we can
    /// push it onto the stack so that it becomes the new top, and
    /// call `find_next_trace` to start searching its children.
    fn push_next_child(&mut self,
                       index: NodeIndex,
                       symbol_sets: SymbolSets<'grammar>)
                       -> bool {
        match self.graph.graph[index] {
            TraceGraphNode::Item(_) => {
                // If we reached an item like
                //
                //     X = ...p (*) ...s
                //
                // then we are done, but we still need to push on the
                // symbols `...p`.
                let edges = self.incoming_edges(index);
                self.stack.push(EnumeratorState {
                    index: index,
                    symbol_sets: symbol_sets,
                    edges: edges,
                });
                self.found_trace()
            }
            TraceGraphNode::Nonterminal(_) => {
                // If this node already appears on the stack, do not
                // visit its children.
                if !self.stack.iter().any(|state| state.index == index) {
                    let edges = self.incoming_edges(index);
                    self.stack.push(EnumeratorState {
                        index: index,
                        symbol_sets: symbol_sets,
                        edges: edges,
                    });
                }
                self.find_next_trace()
            }
        }
    }

    // Assemble the `symbols` vector and `cursor`
    fn found_trace(&mut self)
                   -> bool {
        self.symbols.truncate(0);

        self.symbols.extend(
            self.stack.iter()
                      .rev()
                      .flat_map(|s| s.symbol_sets.prefix));

        self.cursor = self.symbols.len();

        self.symbols.extend(
            self.stack[1].symbol_sets.cursor);

        self.symbols.extend(
            self.stack.iter()
                      .flat_map(|s| s.symbol_sets.suffix));

        true
    }

    pub fn example(&self) -> Option<Example> {
        if self.stack.is_empty() {
            return None;
        }

        let mut symbols = vec![];

        symbols.extend(
            self.stack.iter()
                      .rev()
                      .flat_map(|s| s.symbol_sets.prefix)
                      .cloned()
                      .map(ExampleSymbol::Symbol));

        let cursor = symbols.len();

        match self.stack[1].symbol_sets.cursor {
            Some(&s) => symbols.push(ExampleSymbol::Symbol(s)),
            None => if self.stack[1].symbol_sets.prefix.is_empty() {
                symbols.push(ExampleSymbol::Epsilon)
            } else {
            },
        }

        symbols.extend(
            self.stack.iter()
                      .flat_map(|s| s.symbol_sets.suffix)
                      .cloned()
                      .map(ExampleSymbol::Symbol));

        let mut cursors = (0, symbols.len());

        let mut reductions: Vec<_> =
            self.stack[1..]
                .iter()
                .rev()
                .map(|state| {
                    let nonterminal = match self.graph.graph[state.index] {
                        TraceGraphNode::Nonterminal(nonterminal) => nonterminal,
                        TraceGraphNode::Item(item) => item.production.nonterminal,
                    };
                    let reduction = Reduction {
                        start: cursors.0,
                        end: cursors.1,
                        nonterminal: nonterminal
                    };
                    cursors.0 += state.symbol_sets.prefix.len();
                    cursors.1 -= state.symbol_sets.suffix.len();
                    reduction
                })
                .collect();
        reductions.reverse();

        Some(Example {
            symbols: symbols,
            cursor: cursor,
            reductions: reductions
        })
    }
}

impl<'graph, 'grammar> Iterator for PathEnumerator<'graph, 'grammar> {
    type Item = Example;

    fn next(&mut self) -> Option<Example> {
        let example = self.example();
        self.advance();
        example
    }
}
<commit_msg>remove dead code from trace graph enumerator<commit_after>use collections::{Map, map};
use lr1::core::*;
use lr1::lookahead::*;
use lr1::example::*;
use grammar::repr::*;
use petgraph::{EdgeDirection, Graph};
use petgraph::graph::{Edges, NodeIndex};
use std::fmt::{Debug, Formatter, Error};

#[cfg(test)] mod test;

/// Trace graphs are used to summarize how it is that we came to be in
/// a state where we can take some particular shift/reduce action; put
/// another way, how it is that we came to be in a state with some
/// particular LR(1) item.
///
/// The nodes in the graph are each labeled with a TraceGraphNode and
/// hence take one of two forms:
///
/// - TraceGraphNode::Item -- represents an LR0 item. These nodes are
///   used for the starting/end points in the graph only.  Basically a
///   complete trace stretches from the start item to some end item,
///   and all intermediate nodes are nonterminals.
/// - TraceGraphNode::Nonterminal -- if this graph is for a shift,
///   then these represent items where the cursor is at the beginning:
///   `X = (*) ...`. If the graph is for a reduce, they represent
///   items where a reduce is possible without shifting any more
///   terminals (though further reductions may be needed): `X =
///   ... (*) ...s` where `FIRST(...s)` includes `\epsilon`.
///
/// The edges in the graph are also important. They are labeled with
/// `SymbolSets` instances, meaning that each carries a (prefix,
/// cursor, and suffix) tuple. The label on an edge `A -> B` means
/// that transitioning from a state containing `A` to a state
/// containing `B` is possible if you:
///
/// - shift the symbols in `prefix`
/// - `B` will produce the symbol in `cursor`
/// - shift the symbols in `suffix` after `B` is popped
pub struct TraceGraph<'grammar> {
    // A -L-> B means:
    //
    //     Transition from a state containing A to a state containing
    //     B by (pushing|popping) the symbols L.
    //
    // If this trace graph represents a shift backtrace, then the
    // labels are symbols that are pushed. Otherwise they are labels
    // that are popped.
    graph: Graph<TraceGraphNode<'grammar>, SymbolSets<'grammar>>,
    indices: Map<TraceGraphNode<'grammar>, NodeIndex>,
}

#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub enum TraceGraphNode<'grammar> {
    Nonterminal(NonterminalString),
    Item(LR0Item<'grammar>),
}

impl<'grammar> TraceGraph<'grammar> {
    pub fn new() -> TraceGraph<'grammar> {
        TraceGraph {
            graph: Graph::new(),
            indices: map(),
        }
    }

    pub fn add_node<T>(&mut self, node: T) -> NodeIndex
        where T: Into<TraceGraphNode<'grammar>>
    {
        let node = node.into();
        let graph = &mut self.graph;
        *self.indices.entry(node)
                     .or_insert_with(|| graph.add_node(node))
    }

    pub fn add_edge<F,T>(&mut self,
                         from: F,
                         to: T,
                         labels: SymbolSets<'grammar>)
        where F: Into<TraceGraphNode<'grammar>>,
              T: Into<TraceGraphNode<'grammar>>,
    {
        let from = self.add_node(from.into());
        let to = self.add_node(to.into());
        if !self.graph.edges_directed(from, EdgeDirection::Outgoing)
                      .any(|(t, &l)| t == to && l == labels)
        {
            self.graph.add_edge(from, to, labels);
        }
    }

    pub fn examples<'graph>(&'graph self,
                            lr0_item: LR0Item<'grammar>)
                            -> PathEnumerator<'graph, 'grammar>
    {
        PathEnumerator::new(self, lr0_item)
    }
}

impl<'grammar> Into<TraceGraphNode<'grammar>> for NonterminalString {
    fn into(self) -> TraceGraphNode<'grammar> {
        TraceGraphNode::Nonterminal(self)
    }
}

impl<'grammar, L: Lookahead> Into<TraceGraphNode<'grammar>> for Item<'grammar, L> {
    fn into(self) -> TraceGraphNode<'grammar> {
        TraceGraphNode::Item(self.to_lr0())
    }
}

// This just exists to help with the `Debug` impl
struct TraceGraphEdge<'grammar> {
    from: TraceGraphNode<'grammar>,
    to: TraceGraphNode<'grammar>,
    label: (&'grammar [Symbol], Option<&'grammar Symbol>, &'grammar [Symbol]),
}

impl<'grammar> Debug for TraceGraphEdge<'grammar> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt, "({:?} -{:?}-> {:?})", self.from, self.label, self.to)
    }
}

impl<'grammar> Debug for TraceGraph<'grammar> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        let mut s = fmt.debug_list();
        for (&node, &index) in &self.indices {
            for (target, label) in
                self.graph.edges_directed(index, EdgeDirection::Outgoing)
            {
                s.entry(&TraceGraphEdge { from: node,
                                          to: self.graph[target],
                                          label: (label.prefix,
                                                  label.cursor,
                                                  label.suffix) });
            }
        }
        s.finish()
    }
}

///////////////////////////////////////////////////////////////////////////
// PathEnumerator
//
// The path enumerater walks a trace graph searching for paths that
// start at a given item and terminate at another item. If such a path
// is found, you can then find the complete list of symbols by calling
// `symbols_and_cursor` and also get access to the state.

pub struct PathEnumerator<'graph, 'grammar: 'graph> {
    graph: &'graph TraceGraph<'grammar>,
    stack: Vec<EnumeratorState<'graph, 'grammar>>,
}

struct EnumeratorState<'graph, 'grammar: 'graph> {
    index: NodeIndex,
    symbol_sets: SymbolSets<'grammar>,
    edges: Edges<'graph, SymbolSets<'grammar>>,
}

impl<'graph, 'grammar> PathEnumerator<'graph, 'grammar> {
    fn new(graph: &'graph TraceGraph<'grammar>,
           lr0_item: LR0Item<'grammar>)
           -> Self {
        let start_state = graph.indices[&TraceGraphNode::Item(lr0_item)];
        let mut enumerator = PathEnumerator {
            graph: graph,
            stack: vec![],
        };
        let edges = enumerator.incoming_edges(start_state);
        enumerator.stack.push(EnumeratorState {
            index: start_state,
            symbol_sets: SymbolSets::new(),
            edges: edges,
        });
        enumerator.find_next_trace();
        enumerator
    }

    /// Advance to the next example. Returns false if there are no more
    /// examples.
    pub fn advance(&mut self) -> bool {
        // If we have not yet exhausted all the examples, then the top
        // of the stack should be the last target item that we
        // found. Pop it off.
        match self.stack.pop() {
            Some(top_state) => {
                assert!(match self.graph.graph[top_state.index] {
                    TraceGraphNode::Item(_) => true,
                    TraceGraphNode::Nonterminal(_) => false,
                });

                self.find_next_trace()
            }
            None => {
                false
            }
        }
    }

    fn incoming_edges(&self, index: NodeIndex) -> Edges<'graph, SymbolSets<'grammar>> {
        self.graph.graph.edges_directed(index, EdgeDirection::Incoming)
    }

    /// This is the main operation, written in CPS style and hence it
    /// can seem a bit confusing. The idea is that `find_next_trace`
    /// is called when we are ready to consider the next child of
    /// whatever is on the top of the stack. It simply withdraws
    /// that next child (if any) and hands it to `push_next`.
    fn find_next_trace(&mut self) -> bool {
        if !self.stack.is_empty() {
            let next_edge = {
                let top_of_stack = self.stack.last_mut().unwrap();
                top_of_stack.edges.next()
            };
            self.push_next_child_if_any(next_edge)
        } else {
            false
        }
    }

    /// Invoked with the next child (if any) of the node on the top of
    /// the stack.
    ///
    /// If `next` is `Some`, we simply call `push_next_child`.
    ///
    /// If `next` is `None`, then the node on the top of
    /// the stack *has* no next child, and so it is popped, and then
    /// we call `find_next_trace` again to start with the next child
    /// of the new top of the stack.
    fn push_next_child_if_any(&mut self,
                              next: Option<(NodeIndex, &'graph SymbolSets<'grammar>)>)
                              -> bool {
        if let Some((index, &symbol_sets)) = next {
            self.push_next_child(index, symbol_sets)
        } else {
            self.stack.pop();
            self.find_next_trace()
        }
    }

    /// Push the next child of the top of the stack onto the stack,
    /// making the child the new top.
    ///
    /// If the child is an `Item` node, we have found the next trace,
    /// and hence our search terminates. We push the symbols from this
    /// item node into the symbols vector and then call `found_trace`
    /// (which will ultimately return `true`).
    ///
    /// Otherwise, we check whether this new node would cause a cycle.
    /// If so, we do *not* push it, and instead just call
    /// `find_next_trace` again to proceed to the next child of the
    /// current top.
    ///
    /// Finally, if the new node would NOT cause a cycle, then we can
    /// push it onto the stack so that it becomes the new top, and
    /// call `find_next_trace` to start searching its children.
    fn push_next_child(&mut self,
                       index: NodeIndex,
                       symbol_sets: SymbolSets<'grammar>)
                       -> bool {
        match self.graph.graph[index] {
            TraceGraphNode::Item(_) => {
                // If we reached an item like
                //
                //     X = ...p (*) ...s
                //
                // then we are done, but we still need to push on the
                // symbols `...p`.
                let edges = self.incoming_edges(index);
                self.stack.push(EnumeratorState {
                    index: index,
                    symbol_sets: symbol_sets,
                    edges: edges,
                });
                self.found_trace()
            }
            TraceGraphNode::Nonterminal(_) => {
                // If this node already appears on the stack, do not
                // visit its children.
                if !self.stack.iter().any(|state| state.index == index) {
                    let edges = self.incoming_edges(index);
                    self.stack.push(EnumeratorState {
                        index: index,
                        symbol_sets: symbol_sets,
                        edges: edges,
                    });
                }
                self.find_next_trace()
            }
        }
    }

    fn found_trace(&mut self) -> bool {
        true
    }

    pub fn example(&self) -> Option<Example> {
        if self.stack.is_empty() {
            return None;
        }

        let mut symbols = vec![];

        symbols.extend(
            self.stack.iter()
                      .rev()
                      .flat_map(|s| s.symbol_sets.prefix)
                      .cloned()
                      .map(ExampleSymbol::Symbol));

        let cursor = symbols.len();

        match self.stack[1].symbol_sets.cursor {
            Some(&s) => symbols.push(ExampleSymbol::Symbol(s)),
            None => if self.stack[1].symbol_sets.prefix.is_empty() {
                symbols.push(ExampleSymbol::Epsilon)
            } else {
            },
        }

        symbols.extend(
            self.stack.iter()
                      .flat_map(|s| s.symbol_sets.suffix)
                      .cloned()
                      .map(ExampleSymbol::Symbol));

        let mut cursors = (0, symbols.len());

        let mut reductions: Vec<_> =
            self.stack[1..]
                .iter()
                .rev()
                .map(|state| {
                    let nonterminal = match self.graph.graph[state.index] {
                        TraceGraphNode::Nonterminal(nonterminal) => nonterminal,
                        TraceGraphNode::Item(item) => item.production.nonterminal,
                    };
                    let reduction = Reduction {
                        start: cursors.0,
                        end: cursors.1,
                        nonterminal: nonterminal
                    };
                    cursors.0 += state.symbol_sets.prefix.len();
                    cursors.1 -= state.symbol_sets.suffix.len();
                    reduction
                })
                .collect();
        reductions.reverse();

        Some(Example {
            symbols: symbols,
            cursor: cursor,
            reductions: reductions
        })
    }
}

impl<'graph, 'grammar> Iterator for PathEnumerator<'graph, 'grammar> {
    type Item = Example;

    fn next(&mut self) -> Option<Example> {
        let example = self.example();
        self.advance();
        example
    }
}
<|endoftext|>


<commit_before>extern crate iron_cors2;
extern crate router;
extern crate iron;
extern crate unicase;
#[macro_use]
extern crate hyper;
extern crate mount;
use iron::Listening;
use self::router::Router;
use iron::prelude::*;
use iron::status;
use self::mount::Mount;
use self::hyper::Client;
use self::hyper::header::Headers;
use std::io::Read;
use iron::headers::Origin as OriginHeader;
use iron::headers::{AccessControlRequestMethod, AccessControlRequestHeaders,
                    AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlMaxAge,
                    AccessControlAllowMethods, AccessControlAllowCredentials};
use iron::method::Method::*;
use iron::middleware::Handler;
use iron_cors2::{CorsMiddleware, AllowedOrigins, Origin};
use std::str::FromStr;
use std::collections::HashSet;
use unicase::UniCase;

struct AutoServer {
    listening: Listening,
    port: u16,
}

impl AutoServer {
    pub fn new() -> AutoServer {
        AutoServer::with_cors(CorsMiddleware::new())
    }

    pub fn with_cors(cors: CorsMiddleware) -> AutoServer {
        let get_handler = |_: &mut Request| Ok(Response::with((status::ImATeapot, "")));
        let put_handler = |_: &mut Request| Ok(Response::with((status::BadRequest, "")));

        let mut router = Router::new();
        router.get("", get_handler, "get_a");
        router.put("", put_handler, "put_a");

        let mut chain = Chain::new(router);
        chain.link_around(cors);
        let mut mount = Mount::new();

        mount.mount("/a", chain);
        AutoServer::with_handler(mount)
    }

    pub fn with_handler<H: Handler>(handler: H) -> AutoServer {
        let l = Iron::new(handler).http(format!("localhost:0")).unwrap();
        let p = l.socket.port();
        AutoServer {
            listening: l,
            port: p,
        }
    }
}

impl Drop for AutoServer {
    fn drop(&mut self) {
        // Workaround for https://github.com/hyperium/hyper/issues/338
        self.listening.close().unwrap();
    }
}

#[test]
fn normal_request_possible() {
    let server = AutoServer::new();
    let client = Client::new();
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
    assert_eq!(res.status, status::ImATeapot);
}

fn to_string(res: &mut hyper::client::Response) -> String {
    let mut s = String::new();
    res.read_to_string(&mut s).unwrap();
    s
}

#[test]
fn preflight_to_nonexistent_route_fails() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/b", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::NotFound);
    assert_eq!(to_string(&mut res), "");
}

#[test]
fn preflight_without_origin_is_bad_request() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "Preflight request without Origin header");
}

#[test]
fn preflight_with_allowed_origin_sets_all_headers() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "");
    assert_eq!(res.status, status::NoContent);
    let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
    assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
    let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
    assert_eq!(format!("{}", allow_headers),
               "Content-Type, X-Requested-With");
    let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
    assert_eq!(format!("{}", allow_methods),
               "OPTIONS, GET, POST, PUT, DELETE, HEAD, TRACE, CONNECT, PATCH");
    let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
    assert_eq!(max_age.0, 60 * 60u32);
}

#[test]
fn disallowing_credentials_unsets_allow_credentials_header_in_response() {
    let c = CorsMiddleware { allow_credentials: false, ..CorsMiddleware::new() };
    let server = AutoServer::with_cors(c);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let allow_origin = res.headers.get::<AccessControlAllowCredentials>();
    assert_eq!(allow_origin, None);
}

#[test]
fn allowing_credentials_sets_allow_credentials_header_in_response() {
    let c = CorsMiddleware { allow_credentials: true, ..CorsMiddleware::new() };
    let server = AutoServer::with_cors(c);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let allow_origin = res.headers.get::<AccessControlAllowCredentials>();
    assert!(allow_origin.is_some());
}

#[test]
fn preflight_with_disallowed_origin_is_error() {
    let mut cors = CorsMiddleware::new();
    let origins: HashSet<Origin> =
        vec![Origin::parse("http://www.a.com").unwrap()].into_iter().collect();
    cors.allowed_origins = AllowedOrigins::Specific(origins);
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request requesting disallowed origin 'http://www.a.com:8080'");
}

header! { (NullableOrigin, "Origin") => [String] }

#[test]
fn preflight_with_null_origin_is_not_allowed() {
    // According to https://tools.ietf.org/id/draft-abarth-origin-03.html#rfc.section.6
    // you shouldn't be able to whitelist a "null" Origin.
    //
    // The "null" origin indicates that the Server has hid the Origin since it couldn't
    // property determine it and is set on data: and file: -Url and such.
    //
    // There have been real vulns due to this:
    // https://security.stackexchange.com/questions/145326/
    // how-did-the-facebook-originull-vulnerablity-of-access-control-allow-origin-null
    //
    // Seems as if Iron refuses to parse the Origin header if its null as is:
    // http://azerupi.github.io/mdBook/iron/headers/struct.Origin.html
    let mut cors = CorsMiddleware::new();
    let origins: HashSet<Origin> =
        vec![Origin::parse("http://www.a.com").unwrap()].into_iter().collect();
    cors.allowed_origins = AllowedOrigins::Specific(origins);
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(NullableOrigin("null".to_owned()));
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request without Origin header");
}

#[test]
fn preflight_with_disallowed_header_is_error() {
    let mut cors = CorsMiddleware::new();
    cors.allowed_headers = vec![];
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    let head_vec = vec![UniCase("DoesNotExist".to_owned())];
    headers.set(AccessControlRequestHeaders(head_vec));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request requesting disallowed header(s) DoesNotExist");
}

#[test]
fn options_without_method_is_normal_request() {
    // A requestion with options and OriginHeader but without
    // method is considers non-preflight
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(OriginHeader::from_str("http://a.com").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::Ok);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "");
}

#[test]
fn preflight_with_disallowed_method_is_error() {
    // A requestion with options and OriginHeader but without
    // method is considers non-preflight
    let cm = CorsMiddleware::new();
    let cm2 = CorsMiddleware { allowed_methods: vec![], ..cm };
    let server = AutoServer::with_cors(cm2);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Patch));
    headers.set(OriginHeader::from_str("http://a.com").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload,
               "Preflight request requesting disallowed method PATCH");
}

#[test]
fn normal_request_allows_origin() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::ImATeapot);
    let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
    assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
    assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
    assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
    assert!(res.headers.get::<AccessControlMaxAge>().is_none());
}

#[test]
fn normal_request_without_origin_is_passthrough() {
    let server = AutoServer::new();
    let client = Client::new();
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
    assert_eq!(res.status, status::ImATeapot);
    assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
    assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
    assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
    assert!(res.headers.get::<AccessControlMaxAge>().is_none());
}
<commit_msg>test for expose headers<commit_after>extern crate iron_cors2;
extern crate router;
extern crate iron;
extern crate unicase;
#[macro_use]
extern crate hyper;
extern crate mount;
use iron::Listening;
use self::router::Router;
use iron::prelude::*;
use iron::status;
use self::mount::Mount;
use self::hyper::Client;
use self::hyper::header::Headers;
use std::io::Read;
use iron::headers::Origin as OriginHeader;
use iron::headers::{AccessControlRequestMethod, AccessControlRequestHeaders,
                    AccessControlAllowOrigin, AccessControlAllowHeaders, AccessControlAllowMethods,
                    AccessControlAllowCredentials, AccessControlExposeHeaders, AccessControlMaxAge};
use iron::method::Method::*;
use iron::middleware::Handler;
use iron_cors2::{CorsMiddleware, AllowedOrigins, Origin};
use std::str::FromStr;
use std::collections::HashSet;
use unicase::UniCase;

struct AutoServer {
    listening: Listening,
    port: u16,
}

impl AutoServer {
    pub fn new() -> AutoServer {
        AutoServer::with_cors(CorsMiddleware::new())
    }

    pub fn with_cors(cors: CorsMiddleware) -> AutoServer {
        let get_handler = |_: &mut Request| Ok(Response::with((status::ImATeapot, "")));
        let put_handler = |_: &mut Request| Ok(Response::with((status::BadRequest, "")));

        let mut router = Router::new();
        router.get("", get_handler, "get_a");
        router.put("", put_handler, "put_a");

        let mut chain = Chain::new(router);
        chain.link_around(cors);
        let mut mount = Mount::new();

        mount.mount("/a", chain);
        AutoServer::with_handler(mount)
    }

    pub fn with_handler<H: Handler>(handler: H) -> AutoServer {
        let l = Iron::new(handler).http(format!("localhost:0")).unwrap();
        let p = l.socket.port();
        AutoServer {
            listening: l,
            port: p,
        }
    }
}

impl Drop for AutoServer {
    fn drop(&mut self) {
        // Workaround for https://github.com/hyperium/hyper/issues/338
        self.listening.close().unwrap();
    }
}

#[test]
fn normal_request_possible() {
    let server = AutoServer::new();
    let client = Client::new();
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
    assert_eq!(res.status, status::ImATeapot);
}

fn to_string(res: &mut hyper::client::Response) -> String {
    let mut s = String::new();
    res.read_to_string(&mut s).unwrap();
    s
}

#[test]
fn preflight_to_nonexistent_route_fails() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/b", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::NotFound);
    assert_eq!(to_string(&mut res), "");
}

#[test]
fn preflight_without_origin_is_bad_request() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "Preflight request without Origin header");
}

#[test]
fn preflight_with_allowed_origin_sets_all_headers() {
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "");
    assert_eq!(res.status, status::NoContent);
    let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
    assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
    let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
    assert_eq!(format!("{}", allow_headers),
               "Content-Type, X-Requested-With");
    let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
    assert_eq!(format!("{}", allow_methods),
               "OPTIONS, GET, POST, PUT, DELETE, HEAD, TRACE, CONNECT, PATCH");
    let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
    assert_eq!(max_age.0, 60 * 60u32);
}

#[test]
fn disallowing_credentials_unsets_allow_credentials_header_in_response() {
    let c = CorsMiddleware { allow_credentials: false, ..CorsMiddleware::new() };
    let server = AutoServer::with_cors(c);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let allow_origin = res.headers.get::<AccessControlAllowCredentials>();
    assert_eq!(allow_origin, None);
}

#[test]
fn allowing_credentials_sets_allow_credentials_header_in_response() {
    let c = CorsMiddleware { allow_credentials: true, ..CorsMiddleware::new() };
    let server = AutoServer::with_cors(c);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    let allow_origin = res.headers.get::<AccessControlAllowCredentials>();
    assert!(allow_origin.is_some());
}

#[test]
fn preflight_with_disallowed_origin_is_error() {
    let mut cors = CorsMiddleware::new();
    let origins: HashSet<Origin> =
        vec![Origin::parse("http://www.a.com").unwrap()].into_iter().collect();
    cors.allowed_origins = AllowedOrigins::Specific(origins);
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request requesting disallowed origin 'http://www.a.com:8080'");
}

header! { (NullableOrigin, "Origin") => [String] }

#[test]
fn preflight_with_null_origin_is_not_allowed() {
    // According to https://tools.ietf.org/id/draft-abarth-origin-03.html#rfc.section.6
    // you shouldn't be able to whitelist a "null" Origin.
    //
    // The "null" origin indicates that the Server has hid the Origin since it couldn't
    // property determine it and is set on data: and file: -Url and such.
    //
    // There have been real vulns due to this:
    // https://security.stackexchange.com/questions/145326/
    // how-did-the-facebook-originull-vulnerablity-of-access-control-allow-origin-null
    //
    // Seems as if Iron refuses to parse the Origin header if its null as is:
    // http://azerupi.github.io/mdBook/iron/headers/struct.Origin.html
    let mut cors = CorsMiddleware::new();
    let origins: HashSet<Origin> =
        vec![Origin::parse("http://www.a.com").unwrap()].into_iter().collect();
    cors.allowed_origins = AllowedOrigins::Specific(origins);
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    headers.set(NullableOrigin("null".to_owned()));
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request without Origin header");
}

#[test]
fn preflight_with_disallowed_header_is_error() {
    let mut cors = CorsMiddleware::new();
    cors.allowed_headers = vec![];
    let server = AutoServer::with_cors(cors);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Get));
    let head_vec = vec![UniCase("DoesNotExist".to_owned())];
    headers.set(AccessControlRequestHeaders(head_vec));
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    assert_eq!(to_string(&mut res),
               "Preflight request requesting disallowed header(s) DoesNotExist");
}

#[test]
fn options_without_method_is_normal_request() {
    // A requestion with options and OriginHeader but without
    // method is considers non-preflight
    let server = AutoServer::new();
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(OriginHeader::from_str("http://a.com").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::Ok);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload, "");
}

#[test]
fn preflight_with_disallowed_method_is_error() {
    // A requestion with options and OriginHeader but without
    // method is considers non-preflight
    let cm = CorsMiddleware::new();
    let cm2 = CorsMiddleware { allowed_methods: vec![], ..cm };
    let server = AutoServer::with_cors(cm2);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(AccessControlRequestMethod(Patch));
    headers.set(OriginHeader::from_str("http://a.com").unwrap());
    let mut res = client.request(Options, &format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::BadRequest);
    let mut payload = String::new();
    res.read_to_string(&mut payload).unwrap();
    assert_eq!(payload,
               "Preflight request requesting disallowed method PATCH");
}

#[test]
fn normal_request_allows_origin_and_exposes_headers() {
    let cm1 = CorsMiddleware::new();
    let cm = CorsMiddleware { exposed_headers : vec![UniCase("X-ExposeMe".to_owned())], .. cm1 };
    let server = AutoServer::with_cors(cm);
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(OriginHeader::from_str("http://www.a.com:8080").unwrap());
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port))
        .headers(headers)
        .send()
        .unwrap();
    assert_eq!(res.status, status::ImATeapot);
    let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
    assert_eq!(allow_origin.to_string(), "http://www.a.com:8080");
    let expose_headers = res.headers.get::<AccessControlExposeHeaders>().unwrap();
    assert_eq!(expose_headers.0, vec![UniCase("X-ExposeMe")]);
    assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
    assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
    assert!(res.headers.get::<AccessControlMaxAge>().is_none());
}

#[test]
fn normal_request_without_origin_is_passthrough() {
    let server = AutoServer::new();
    let client = Client::new();
    let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
    assert_eq!(res.status, status::ImATeapot);
    assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
    assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
    assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
    assert!(res.headers.get::<AccessControlExposeHeaders>().is_none());
    assert!(res.headers.get::<AccessControlMaxAge>().is_none());
}
<|endoftext|>


<commit_before>use cxx_test_suite::ffi;
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, *ffi::c_return_box());
    ffi::c_return_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, *ffi::c_try_return_box().unwrap());
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(2020)));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
    let shared_test_vec = vec![
        ffi::Shared { z: 1010 },
        ffi::Shared { z: 1011 }
    ];
    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(shared_test_vec));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_index(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_enum(ffi::Enum::AVal));
}

/*
// https://github.com/dtolnay/cxx/issues/232
#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}
*/

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(old_value, unique_ptr.set2(old_value));
    assert_eq!(old_value, unique_ptr.get2());
    assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap());
    assert!(unique_ptr.get_fail().is_err());
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
    Box::into_raw(Box::new(2020usize))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
    *r == 2020
}
<commit_msg>Format with rustfmt 1.4.20<commit_after>use cxx_test_suite::ffi;
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, *ffi::c_return_box());
    ffi::c_return_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, *ffi::c_try_return_box().unwrap());
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(2020)));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_index(test_vec.clone()));
    let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }];
    check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(
        shared_test_vec,
    ));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_index(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_enum(ffi::Enum::AVal));
}

/*
// https://github.com/dtolnay/cxx/issues/232
#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}
*/

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(old_value, unique_ptr.set2(old_value));
    assert_eq!(old_value, unique_ptr.get2());
    assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap());
    assert!(unique_ptr.get_fail().is_err());
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
    Box::into_raw(Box::new(2020usize))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
    *r == 2020
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};
use std::io::{Read, Seek, SeekFrom, ErrorKind, Result, Error};
use std::fs::{File, OpenOptions};
use std::str;

extern crate positioned_io;
extern crate byteorder;
use self::byteorder::LittleEndian;

use positioned_io::{ReadAt, WriteAt, Size, Cursor, SizeCursor, ByteIo, Slice};

#[test]
fn test_read_at() {
    let file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    file.read_exact_at(10, buf.as_mut()).unwrap();
    assert_eq!(&buf, b"3589");
}

#[test]
fn test_mixed_read() {
    let mut file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    file.read_exact(&mut buf[..]).unwrap();
    assert_eq!(&buf, b"3.14");
    file.read_exact_at(764, &mut buf[..]).unwrap();
    assert_eq!(&buf, b"9999");
    file.read_exact(&mut buf[..]).unwrap();
    assert_eq!(&buf, b"1592");
}

// A ReadAt that has weird behavior.
struct ReadCustom<I: ReadAt, F: Fn() -> Result<usize>> {
    i: I,
    fail: Cell<bool>,
    onfail: F,
}
impl<I: ReadAt, F: Fn() -> Result<usize>> ReadCustom<I, F> {
    fn new(i: I, f: F) -> Self {
        ReadCustom {
            i: i,
            fail: Cell::new(true),
            onfail: f,
        }
    }
}
impl<I: ReadAt, F: Fn() -> Result<usize>> ReadAt for ReadCustom<I, F> {
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize> {
        let fail = self.fail.get();
        self.fail.set(!fail);
        if fail {
            (self.onfail)()
        } else {
            self.i.read_at(pos, buf)
        }
    }
}

#[test]
fn test_read_fails() {
    // Test interrupts.
    let file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    {
        let interrupt = ReadCustom::new(&file,
                                    || Err(Error::new(ErrorKind::Interrupted, "interrupt!")));
        interrupt.read_exact_at(10, buf.as_mut()).unwrap();
        assert_eq!(&buf, b"3589");
    }

    // Test errors.
    {
        let fail = ReadCustom::new(&file, || Err(Error::new(ErrorKind::Other, "random fail")));
        assert!(fail.read_exact_at(10, buf.as_mut()).is_err());
    }

    // Test EOF.
    assert!(file.read_exact_at(1000000000, buf.as_mut()).is_err());
}

#[test]
fn test_size() {
    let file = File::open("tests/pi.txt").unwrap();
    let size = file.size().unwrap().unwrap();
    assert_eq!(size, 1000002);
}

#[test]
fn test_cursor() {
    let file = File::open("tests/pi.txt").unwrap();
    let mut curs = Cursor::new_pos(file, 10);
    let mut buf = [0; 4];
    {
        curs.read_exact(&mut buf).unwrap();
        assert_eq!(&buf, b"3589");
    }
    curs.seek(SeekFrom::Current(4)).unwrap();
    curs.read_exact(&mut buf).unwrap();
    assert_eq!(&buf, b"3846");
}

#[test]
fn test_size_cursor() {
    let file = File::open("Cargo.toml").unwrap();
    let mut curs = SizeCursor::new_pos(file, 10);
    let mut buf = [0; 4];
    curs.seek(SeekFrom::End(-2)).unwrap();
    assert_eq!(2, curs.read(&mut buf).unwrap());
    let s = str::from_utf8(buf.as_ref()).unwrap();
    assert!(s.contains("\n"));
}

#[test]
fn test_byteio() {
    let file = File::open("Cargo.toml").unwrap();
    let io: ByteIo<_, LittleEndian> = ByteIo::new(file);
    let r = io.read_i32_at(3).unwrap();
    assert_eq!(0x67616b63, r);
}

#[test]
fn test_vector() {
    // Write past the end.
    let mut v = vec![0, 1, 2, 3];
    let buf = [4, 5, 6, 7];
    (&mut v).write_all_at(2, &buf).unwrap();
    assert_eq!(vec![0, 1, 4, 5, 6, 7], v);
}

#[test]
fn test_slice() {
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        // Read too much, limited by size.
        let mut buf = [9; 4];
        let slice = Slice::new(&mut v, 2, Some(3));
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 1);
        assert_eq!(buf, [4, 9, 9, 9]);

        // Read too much, limited by buf
        let mut buf = [9; 2];
        let bytes = slice.read_at(1, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [3, 4]);
    }

    {
        // Read too much, limited by original vec.
        let slice = Slice::new(&mut v, 2, Some(5));
        let mut buf = [9; 6];
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [4, 5, 9, 9, 9, 9]);
    }

    {
        // Read too much of unsized slice.
        let slice = Slice::new(&mut v, 2, None);
        let mut buf = [9; 6];
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [4, 5, 9, 9, 9, 9]);
    }

    // Interleaved read/write to same slice.
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        let mut slice = Slice::new(&mut v, 2, Some(3));
        let mut buf = [9; 3];
        {
            let bytes = slice.write_at(2, &buf).unwrap();
            assert_eq!(bytes, 1);
        }
        {
            let bytes = slice.read_at(1, &mut buf).unwrap();
            assert_eq!(bytes, 2);
        }
        assert_eq!(buf, [3, 9, 9]);
        {
            let bytes = slice.write_at(0, &buf[..1]).unwrap();
            assert_eq!(bytes, 1);
        }
    }
    assert_eq!(v, vec![0, 1, 3, 3, 9, 5]);

    // Write past end.
    let mut v = vec![0, 1, 2, 3];
    let buf = [9; 6];
    // Limited by size.
    {
        let mut slice = Slice::new(&mut v, 2, Some(5));
        let bytes = slice.write_at(1, &buf).unwrap();
        assert_eq!(bytes, 4);
    }
    assert_eq!(v, vec![0, 1, 2, 9, 9, 9, 9]);
    // No size.
    {
        let mut slice = Slice::new(&mut v, 2, None);
        let bytes = slice.write_at(1, &buf).unwrap();
        assert_eq!(bytes, 6);
    }
    assert_eq!(v, vec![0, 1, 2, 9, 9, 9, 9, 9, 9]);

    // Automatic-size constructor.
    let mut v = vec![0, 1, 2, 3];
    {
        let mut slice = Slice::new_to_end(&mut v, 1).unwrap();
        assert_eq!(slice.size().unwrap().unwrap(), 3);
        let bytes = slice.write_at(0, &buf).unwrap();
        assert_eq!(bytes, 3);
    }
    assert_eq!(v, vec![0, 9, 9, 9]);

    // Slice of slice.
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        let mut slice1 = Slice::new(&mut v, 1, Some(4));
        let mut slice2 = Slice::new(&mut slice1, 1, Some(2));
        let bytes = slice2.write_at(0, &buf).unwrap();
        assert_eq!(bytes, 2);
    }
    assert_eq!(v, vec![0, 1, 9, 9, 4, 5]);
}

#[test]
fn test_refcell() {
    let mut buf = [0; 3];

    // Make sure we can instantiate a bunch of combinations.
    let mut a = [0, 1, 2, 3];
    a.as_ref().read_at(0, &mut buf).unwrap();
    a.as_mut().write_at(0, &buf).unwrap();

    let mut v = vec![0, 1, 2, 3];
    v.read_at(0, &mut buf).unwrap();
    v.write_at(0, &buf).unwrap();

    let mut ra = &RefCell::new(a.as_mut());
    ra.read_at(0, &mut buf).unwrap();
    ra.write_at(0, &buf).unwrap();

    let mut rv = &RefCell::new(&mut v);
    rv.read_at(0, &mut buf).unwrap();
    rv.write_at(0, &buf).unwrap();

    // Test writing but still being able to use, thanks to RefCell.
    let mut v = vec![0, 1, 2, 3];
    let mut rv = &RefCell::new(&mut v);
    let buf = [9; 4];
    rv.write_at(0, &buf).unwrap();
    assert_eq!(*rv.borrow(), &vec![9; 4]);
}
<commit_msg>no longer need OpenOptions<commit_after>use std::cell::{Cell, RefCell};
use std::io::{Read, Seek, SeekFrom, ErrorKind, Result, Error};
use std::fs::File;
use std::str;

extern crate positioned_io;
extern crate byteorder;
use self::byteorder::LittleEndian;

use positioned_io::{ReadAt, WriteAt, Size, Cursor, SizeCursor, ByteIo, Slice};

#[test]
fn test_read_at() {
    let file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    file.read_exact_at(10, buf.as_mut()).unwrap();
    assert_eq!(&buf, b"3589");
}

#[test]
fn test_mixed_read() {
    let mut file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    file.read_exact(&mut buf[..]).unwrap();
    assert_eq!(&buf, b"3.14");
    file.read_exact_at(764, &mut buf[..]).unwrap();
    assert_eq!(&buf, b"9999");
    file.read_exact(&mut buf[..]).unwrap();
    assert_eq!(&buf, b"1592");
}

// A ReadAt that has weird behavior.
struct ReadCustom<I: ReadAt, F: Fn() -> Result<usize>> {
    i: I,
    fail: Cell<bool>,
    onfail: F,
}
impl<I: ReadAt, F: Fn() -> Result<usize>> ReadCustom<I, F> {
    fn new(i: I, f: F) -> Self {
        ReadCustom {
            i: i,
            fail: Cell::new(true),
            onfail: f,
        }
    }
}
impl<I: ReadAt, F: Fn() -> Result<usize>> ReadAt for ReadCustom<I, F> {
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize> {
        let fail = self.fail.get();
        self.fail.set(!fail);
        if fail {
            (self.onfail)()
        } else {
            self.i.read_at(pos, buf)
        }
    }
}

#[test]
fn test_read_fails() {
    // Test interrupts.
    let file = File::open("tests/pi.txt").unwrap();
    let mut buf = [0; 4];
    {
        let interrupt = ReadCustom::new(&file,
                                    || Err(Error::new(ErrorKind::Interrupted, "interrupt!")));
        interrupt.read_exact_at(10, buf.as_mut()).unwrap();
        assert_eq!(&buf, b"3589");
    }

    // Test errors.
    {
        let fail = ReadCustom::new(&file, || Err(Error::new(ErrorKind::Other, "random fail")));
        assert!(fail.read_exact_at(10, buf.as_mut()).is_err());
    }

    // Test EOF.
    assert!(file.read_exact_at(1000000000, buf.as_mut()).is_err());
}

#[test]
fn test_size() {
    let file = File::open("tests/pi.txt").unwrap();
    let size = file.size().unwrap().unwrap();
    assert_eq!(size, 1000002);
}

#[test]
fn test_cursor() {
    let file = File::open("tests/pi.txt").unwrap();
    let mut curs = Cursor::new_pos(file, 10);
    let mut buf = [0; 4];
    {
        curs.read_exact(&mut buf).unwrap();
        assert_eq!(&buf, b"3589");
    }
    curs.seek(SeekFrom::Current(4)).unwrap();
    curs.read_exact(&mut buf).unwrap();
    assert_eq!(&buf, b"3846");
}

#[test]
fn test_size_cursor() {
    let file = File::open("Cargo.toml").unwrap();
    let mut curs = SizeCursor::new_pos(file, 10);
    let mut buf = [0; 4];
    curs.seek(SeekFrom::End(-2)).unwrap();
    assert_eq!(2, curs.read(&mut buf).unwrap());
    let s = str::from_utf8(buf.as_ref()).unwrap();
    assert!(s.contains("\n"));
}

#[test]
fn test_byteio() {
    let file = File::open("Cargo.toml").unwrap();
    let io: ByteIo<_, LittleEndian> = ByteIo::new(file);
    let r = io.read_i32_at(3).unwrap();
    assert_eq!(0x67616b63, r);
}

#[test]
fn test_vector() {
    // Write past the end.
    let mut v = vec![0, 1, 2, 3];
    let buf = [4, 5, 6, 7];
    (&mut v).write_all_at(2, &buf).unwrap();
    assert_eq!(vec![0, 1, 4, 5, 6, 7], v);
}

#[test]
fn test_slice() {
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        // Read too much, limited by size.
        let mut buf = [9; 4];
        let slice = Slice::new(&mut v, 2, Some(3));
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 1);
        assert_eq!(buf, [4, 9, 9, 9]);

        // Read too much, limited by buf
        let mut buf = [9; 2];
        let bytes = slice.read_at(1, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [3, 4]);
    }

    {
        // Read too much, limited by original vec.
        let slice = Slice::new(&mut v, 2, Some(5));
        let mut buf = [9; 6];
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [4, 5, 9, 9, 9, 9]);
    }

    {
        // Read too much of unsized slice.
        let slice = Slice::new(&mut v, 2, None);
        let mut buf = [9; 6];
        let bytes = slice.read_at(2, &mut buf).unwrap();
        assert_eq!(bytes, 2);
        assert_eq!(buf, [4, 5, 9, 9, 9, 9]);
    }

    // Interleaved read/write to same slice.
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        let mut slice = Slice::new(&mut v, 2, Some(3));
        let mut buf = [9; 3];
        {
            let bytes = slice.write_at(2, &buf).unwrap();
            assert_eq!(bytes, 1);
        }
        {
            let bytes = slice.read_at(1, &mut buf).unwrap();
            assert_eq!(bytes, 2);
        }
        assert_eq!(buf, [3, 9, 9]);
        {
            let bytes = slice.write_at(0, &buf[..1]).unwrap();
            assert_eq!(bytes, 1);
        }
    }
    assert_eq!(v, vec![0, 1, 3, 3, 9, 5]);

    // Write past end.
    let mut v = vec![0, 1, 2, 3];
    let buf = [9; 6];
    // Limited by size.
    {
        let mut slice = Slice::new(&mut v, 2, Some(5));
        let bytes = slice.write_at(1, &buf).unwrap();
        assert_eq!(bytes, 4);
    }
    assert_eq!(v, vec![0, 1, 2, 9, 9, 9, 9]);
    // No size.
    {
        let mut slice = Slice::new(&mut v, 2, None);
        let bytes = slice.write_at(1, &buf).unwrap();
        assert_eq!(bytes, 6);
    }
    assert_eq!(v, vec![0, 1, 2, 9, 9, 9, 9, 9, 9]);

    // Automatic-size constructor.
    let mut v = vec![0, 1, 2, 3];
    {
        let mut slice = Slice::new_to_end(&mut v, 1).unwrap();
        assert_eq!(slice.size().unwrap().unwrap(), 3);
        let bytes = slice.write_at(0, &buf).unwrap();
        assert_eq!(bytes, 3);
    }
    assert_eq!(v, vec![0, 9, 9, 9]);

    // Slice of slice.
    let mut v = vec![0, 1, 2, 3, 4, 5];
    {
        let mut slice1 = Slice::new(&mut v, 1, Some(4));
        let mut slice2 = Slice::new(&mut slice1, 1, Some(2));
        let bytes = slice2.write_at(0, &buf).unwrap();
        assert_eq!(bytes, 2);
    }
    assert_eq!(v, vec![0, 1, 9, 9, 4, 5]);
}

#[test]
fn test_refcell() {
    let mut buf = [0; 3];

    // Make sure we can instantiate a bunch of combinations.
    let mut a = [0, 1, 2, 3];
    a.as_ref().read_at(0, &mut buf).unwrap();
    a.as_mut().write_at(0, &buf).unwrap();

    let mut v = vec![0, 1, 2, 3];
    v.read_at(0, &mut buf).unwrap();
    v.write_at(0, &buf).unwrap();

    let mut ra = &RefCell::new(a.as_mut());
    ra.read_at(0, &mut buf).unwrap();
    ra.write_at(0, &buf).unwrap();

    let mut rv = &RefCell::new(&mut v);
    rv.read_at(0, &mut buf).unwrap();
    rv.write_at(0, &buf).unwrap();

    // Test writing but still being able to use, thanks to RefCell.
    let mut v = vec![0, 1, 2, 3];
    let mut rv = &RefCell::new(&mut v);
    let buf = [9; 4];
    rv.write_at(0, &buf).unwrap();
    assert_eq!(*rv.borrow(), &vec![9; 4]);
}
<|endoftext|>


<commit_before>extern crate rusqlite;
extern crate r2d2;
extern crate r2d2_sqlite;

use std::sync::mpsc;
use std::thread;

use r2d2_sqlite::SQLiteConnectionManager;
use rusqlite::{SQLITE_OPEN_URI, SQLITE_OPEN_CREATE, SQLITE_OPEN_READ_WRITE};

#[test]
fn test_basic() {
    let manager = SQLiteConnectionManager::new(
        "file:dummy.db?mode=memory&cache=shared",
        SQLITE_OPEN_URI | SQLITE_OPEN_CREATE | SQLITE_OPEN_READ_WRITE,
    );
    let config = r2d2::Config::builder().pool_size(2).build();
    let pool = r2d2::Pool::new(config, manager).unwrap();

    let (s1, r1) = mpsc::channel();
    let (s2, r2) = mpsc::channel();

    let pool1 = pool.clone();
    let t1 = thread::spawn(move || {
        let conn = pool1.get().unwrap();
        s1.send(()).unwrap();
        r2.recv().unwrap();
        drop(conn);
    });

    let pool2 = pool.clone();
    let t2 = thread::spawn(move || {
        let conn = pool2.get().unwrap();
        s2.send(()).unwrap();
        r1.recv().unwrap();
        drop(conn);
    });

    t1.join().unwrap();
    t2.join().unwrap();

    pool.get().unwrap();
}

#[test]
fn test_is_valid() {
    let manager = SQLiteConnectionManager::new(
        "file:dummy.db?mode=memory&cache=shared",
        SQLITE_OPEN_URI | SQLITE_OPEN_CREATE | SQLITE_OPEN_READ_WRITE,
    );
    let config = r2d2::Config::builder()
        .pool_size(1)
        .test_on_check_out(true)
        .build();
    let pool = r2d2::Pool::new(config, manager).unwrap();

    pool.get().unwrap();
}
<commit_msg>Rustfmt<commit_after>extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rusqlite;

use std::sync::mpsc;
use std::thread;

use r2d2_sqlite::SQLiteConnectionManager;
use rusqlite::{SQLITE_OPEN_CREATE, SQLITE_OPEN_READ_WRITE, SQLITE_OPEN_URI};

#[test]
fn test_basic() {
    let manager = SQLiteConnectionManager::new(
        "file:dummy.db?mode=memory&cache=shared",
        SQLITE_OPEN_URI | SQLITE_OPEN_CREATE | SQLITE_OPEN_READ_WRITE,
    );
    let config = r2d2::Config::builder().pool_size(2).build();
    let pool = r2d2::Pool::new(config, manager).unwrap();

    let (s1, r1) = mpsc::channel();
    let (s2, r2) = mpsc::channel();

    let pool1 = pool.clone();
    let t1 = thread::spawn(move || {
        let conn = pool1.get().unwrap();
        s1.send(()).unwrap();
        r2.recv().unwrap();
        drop(conn);
    });

    let pool2 = pool.clone();
    let t2 = thread::spawn(move || {
        let conn = pool2.get().unwrap();
        s2.send(()).unwrap();
        r1.recv().unwrap();
        drop(conn);
    });

    t1.join().unwrap();
    t2.join().unwrap();

    pool.get().unwrap();
}

#[test]
fn test_is_valid() {
    let manager = SQLiteConnectionManager::new(
        "file:dummy.db?mode=memory&cache=shared",
        SQLITE_OPEN_URI | SQLITE_OPEN_CREATE | SQLITE_OPEN_READ_WRITE,
    );
    let config = r2d2::Config::builder()
        .pool_size(1)
        .test_on_check_out(true)
        .build();
    let pool = r2d2::Pool::new(config, manager).unwrap();

    pool.get().unwrap();
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::hashmap::HashMap;

pub trait Cache<K: Eq, V: Clone> {
    fn insert(&mut self, key: K, value: V);
    fn find(&mut self, key: &K) -> Option<V>;
    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V;
    fn evict_all(&mut self);
}

pub struct MonoCache<K, V> {
    entry: Option<(K,V)>,
}

impl<K: Clone + Eq, V: Clone> MonoCache<K,V> {
    pub fn new(_size: uint) -> MonoCache<K,V> {
        MonoCache { entry: None }
    }
}

impl<K: Clone + Eq, V: Clone> Cache<K,V> for MonoCache<K,V> {
    fn insert(&mut self, key: K, value: V) {
        self.entry = Some((key, value));
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entry {
            None => None,
            Some((ref k, ref v)) => if *k == *key { Some(v.clone()) } else { None }
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        match self.entry {
            None => {
                let value = blk(key);
                self.entry = Some((key.clone(), value.clone()));
                value
            },
            Some((ref _k, ref v)) => v.clone()
        }
    }

    fn evict_all(&mut self) {
        self.entry = None;
    }
}

#[test]
fn test_monocache() {
    let mut cache = MonoCache::new(10);
    let one = @"one";
    let two = @"two";
    cache.insert(1, one);

    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());
    /* FIXME: clarify behavior here:
    cache.find_or_create(&2, |_v| { two });
    assert!(cache.find(&2).is_some());
    assert!(cache.find(&1).is_none());
    */
}

pub struct HashCache<K, V> {
    entries: HashMap<K, V>,
}

impl<K: Clone + Eq + Hash, V: Clone> HashCache<K,V> {
    pub fn new() -> HashCache<K, V> {
        HashCache {
          entries: HashMap::new(),
        }
    }
}

impl<K: Clone + Eq + Hash, V: Clone> Cache<K,V> for HashCache<K,V> {
    fn insert(&mut self, key: K, value: V) {
        self.entries.insert(key, value);
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.find(key) {
            Some(v) => Some(v.clone()),
            None    => None,
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        self.entries.find_or_insert_with(key.clone(), blk).clone()
    }

    fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_hashcache() {
    let mut cache = HashCache::new();
    let one = @"one";
    let two = @"two";

    cache.insert(1, one);
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());

    cache.find_or_create(&2, |_v| { two });
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_some());
}

pub struct LRUCache<K, V> {
    entries: ~[(K, V)],
    cache_size: uint,
}

impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
    pub fn new(size: uint) -> LRUCache<K, V> {
        LRUCache {
          entries: ~[],
          cache_size: size,
        }
    }

    pub fn touch(&mut self, pos: uint) -> V {
        let last_index = self.entries.len() - 1;
        if pos != last_index {
            let entry = self.entries.remove(pos);
            self.entries.push(entry);
        }
        self.entries[last_index].second_ref().clone()
    }
}

impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
    fn insert(&mut self, key: K, val: V) {
        if self.entries.len() == self.cache_size {
            self.entries.remove(0);
        }
        self.entries.push((key, val));
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => Some(self.touch(pos)),
            None      => None,
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => self.touch(pos),
            None => {
                let val = blk(key);
                self.insert(key.clone(), val.clone());
                val
            }
        }
    }

    fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_lru_cache() {
    let one = @"one";
    let two = @"two";
    let three = @"three";
    let four = @"four";

    // Test normal insertion.
    let mut cache = LRUCache::new(2); // (_, _) (cache is empty)
    cache.insert(1, one);    // (1, _)
    cache.insert(2, two);    // (1, 2)
    cache.insert(3, three);  // (2, 3)

    assert!(cache.find(&1).is_none());  // (2, 3) (no change)
    assert!(cache.find(&3).is_some());  // (2, 3)
    assert!(cache.find(&2).is_some());  // (3, 2)

    // Test that LRU works (this insertion should replace 3, not 2).
    cache.insert(4, four); // (2, 4)

    assert!(cache.find(&1).is_none());  // (2, 4) (no change)
    assert!(cache.find(&2).is_some());  // (4, 2)
    assert!(cache.find(&3).is_none());  // (4, 2) (no change)
    assert!(cache.find(&4).is_some());  // (2, 4) (no change)

    // Test find_or_create.
    do cache.find_or_create(&1) |_| { one }; // (4, 1)

    assert!(cache.find(&1).is_some()); // (4, 1) (no change)
    assert!(cache.find(&2).is_none()); // (4, 1) (no change)
    assert!(cache.find(&3).is_none()); // (4, 1) (no change)
    assert!(cache.find(&4).is_some()); // (1, 4)
}
<commit_msg>auto merge of #1040 : kmcallister/servo/monocache, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::hashmap::HashMap;

pub trait Cache<K: Eq, V: Clone> {
    fn insert(&mut self, key: K, value: V);
    fn find(&mut self, key: &K) -> Option<V>;
    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V;
    fn evict_all(&mut self);
}

pub struct MonoCache<K, V> {
    entry: Option<(K,V)>,
}

impl<K: Clone + Eq, V: Clone> MonoCache<K,V> {
    pub fn new(_size: uint) -> MonoCache<K,V> {
        MonoCache { entry: None }
    }
}

impl<K: Clone + Eq, V: Clone> Cache<K,V> for MonoCache<K,V> {
    fn insert(&mut self, key: K, value: V) {
        self.entry = Some((key, value));
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entry {
            None => None,
            Some((ref k, ref v)) => if *k == *key { Some(v.clone()) } else { None }
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        match self.find(key) {
            Some(value) => value,
            None => {
                let value = blk(key);
                self.entry = Some((key.clone(), value.clone()));
                value
            }
        }
    }

    fn evict_all(&mut self) {
        self.entry = None;
    }
}

#[test]
fn test_monocache() {
    let mut cache = MonoCache::new(10);
    let one = @"one";
    let two = @"two";
    cache.insert(1, one);

    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());
    cache.find_or_create(&2, |_v| { two });
    assert!(cache.find(&2).is_some());
    assert!(cache.find(&1).is_none());
}

pub struct HashCache<K, V> {
    entries: HashMap<K, V>,
}

impl<K: Clone + Eq + Hash, V: Clone> HashCache<K,V> {
    pub fn new() -> HashCache<K, V> {
        HashCache {
          entries: HashMap::new(),
        }
    }
}

impl<K: Clone + Eq + Hash, V: Clone> Cache<K,V> for HashCache<K,V> {
    fn insert(&mut self, key: K, value: V) {
        self.entries.insert(key, value);
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.find(key) {
            Some(v) => Some(v.clone()),
            None    => None,
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        self.entries.find_or_insert_with(key.clone(), blk).clone()
    }

    fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_hashcache() {
    let mut cache = HashCache::new();
    let one = @"one";
    let two = @"two";

    cache.insert(1, one);
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_none());

    cache.find_or_create(&2, |_v| { two });
    assert!(cache.find(&1).is_some());
    assert!(cache.find(&2).is_some());
}

pub struct LRUCache<K, V> {
    entries: ~[(K, V)],
    cache_size: uint,
}

impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
    pub fn new(size: uint) -> LRUCache<K, V> {
        LRUCache {
          entries: ~[],
          cache_size: size,
        }
    }

    pub fn touch(&mut self, pos: uint) -> V {
        let last_index = self.entries.len() - 1;
        if pos != last_index {
            let entry = self.entries.remove(pos);
            self.entries.push(entry);
        }
        self.entries[last_index].second_ref().clone()
    }
}

impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
    fn insert(&mut self, key: K, val: V) {
        if self.entries.len() == self.cache_size {
            self.entries.remove(0);
        }
        self.entries.push((key, val));
    }

    fn find(&mut self, key: &K) -> Option<V> {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => Some(self.touch(pos)),
            None      => None,
        }
    }

    fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
        match self.entries.iter().position(|&(ref k, _)| *k == *key) {
            Some(pos) => self.touch(pos),
            None => {
                let val = blk(key);
                self.insert(key.clone(), val.clone());
                val
            }
        }
    }

    fn evict_all(&mut self) {
        self.entries.clear();
    }
}

#[test]
fn test_lru_cache() {
    let one = @"one";
    let two = @"two";
    let three = @"three";
    let four = @"four";

    // Test normal insertion.
    let mut cache = LRUCache::new(2); // (_, _) (cache is empty)
    cache.insert(1, one);    // (1, _)
    cache.insert(2, two);    // (1, 2)
    cache.insert(3, three);  // (2, 3)

    assert!(cache.find(&1).is_none());  // (2, 3) (no change)
    assert!(cache.find(&3).is_some());  // (2, 3)
    assert!(cache.find(&2).is_some());  // (3, 2)

    // Test that LRU works (this insertion should replace 3, not 2).
    cache.insert(4, four); // (2, 4)

    assert!(cache.find(&1).is_none());  // (2, 4) (no change)
    assert!(cache.find(&2).is_some());  // (4, 2)
    assert!(cache.find(&3).is_none());  // (4, 2) (no change)
    assert!(cache.find(&4).is_some());  // (2, 4) (no change)

    // Test find_or_create.
    do cache.find_or_create(&1) |_| { one }; // (4, 1)

    assert!(cache.find(&1).is_some()); // (4, 1) (no change)
    assert!(cache.find(&2).is_none()); // (4, 1) (no change)
    assert!(cache.find(&3).is_none()); // (4, 1) (no change)
    assert!(cache.find(&4).is_some()); // (1, 4)
}
<|endoftext|>


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

use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;

use {Args, Format, Message};

/// Using a value, select the appropriate message and format it.
pub struct SelectFormat<K> {
    /// The name of the variable whose value should be formatted.
    #[allow(dead_code)]
    variable_name: String,
    /// Given a value of a variable, this maps that to a message format.
    #[allow(dead_code)]
    mappings: HashMap<K, Message>,
    /// The message format to use if no valid mapping is found for
    /// the variable value.
    default: Message,
}

impl<K> SelectFormat<K>
    where K: Eq + Hash
{
    /// Construct a `SelectFormat`.
    pub fn new(variable_name: &str, default: Message) -> Self {
        SelectFormat {
            variable_name: variable_name.to_string(),
            mappings: HashMap::<K, Message>::new(),
            default: default,
        }
    }

    /// Map a value for a particular message.
    pub fn map(mut self, value: K, message: Message) -> Self {
        self.mappings.insert(value, message);
        self
    }
}

impl<K> Format for SelectFormat<K> {
    fn apply_format<'f>(&'f self, stream: &mut fmt::Write, args: &'f Args<'f>) -> fmt::Result {
        try!(self.default.write_message(stream, args));
        Ok(())
    }
}
<commit_msg>SelectFormat works now. (No parsing yet.)<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.

use std::collections::HashMap;
use std::fmt;

use {Args, Format, Message, Value};

/// Using a value, select the appropriate message and format it.
pub struct SelectFormat {
    /// The name of the variable whose value should be formatted.
    variable_name: String,
    /// Given a value of a variable, this maps that to a message format.
    mappings: HashMap<String, Message>,
    /// The message format to use if no valid mapping is found for
    /// the variable value.
    default: Message,
}

impl SelectFormat {
    /// Construct a `SelectFormat`.
    pub fn new(variable_name: &str, default: Message) -> Self {
        SelectFormat {
            variable_name: variable_name.to_string(),
            mappings: HashMap::<String, Message>::new(),
            default: default,
        }
    }

    /// Map a value for a particular message.
    pub fn map(&mut self, value: &str, message: Message) {
        self.mappings.insert(value.to_string(), message);
    }

    /// Given a value, determine which `Message` to use.
    pub fn lookup_message(&self, value: &str) -> &Message {
        self.mappings.get(value).unwrap_or(&self.default)
    }
}

impl Format for SelectFormat {
    fn apply_format<'f>(&'f self, stream: &mut fmt::Write, args: &'f Args<'f>) -> fmt::Result {
        if let Some(arg) = args.get(&self.variable_name) {
            let value = match *arg.value() {
                Value::Str(str) => str,
                _ => panic!("Wrong variable type."),
            };
            let message = self.lookup_message(&value);
            try!(message.write_message(stream, args));
            Ok(())
        } else {
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use icu::parse;
    use super::SelectFormat;
    use {arg, Format};

    #[test]
    fn it_works() {
        let mut fmt = SelectFormat::new("type", parse("Default").unwrap());
        fmt.map("block", parse("Block").unwrap());

        let mut output = String::new();
        fmt.apply_format(&mut output, &arg("type", "block")).unwrap();
        assert_eq!("Block", output);

        let mut output = String::new();
        fmt.apply_format(&mut output, &arg("type", "span")).unwrap();
        assert_eq!("Default", output);
    }
}
<|endoftext|>