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

use core::prelude::*;

use syntax::{ast, fold, attr};

use core::option;
use core::vec;

type in_cfg_pred = @fn(+attrs: ~[ast::attribute]) -> bool;

struct Context {
    in_cfg: in_cfg_pred
}

// Support conditional compilation by transforming the AST, stripping out
// any items that do not belong in the current configuration
pub fn strip_unconfigured_items(crate: @ast::crate) -> @ast::crate {
    do strip_items(crate) |attrs| {
        in_cfg(/*bad*/copy crate.node.config, attrs)
    }
}

pub fn strip_items(crate: @ast::crate, in_cfg: in_cfg_pred)
    -> @ast::crate {

    let ctxt = @Context { in_cfg: in_cfg };

    let precursor = @fold::AstFoldFns {
          fold_mod: |a,b| fold_mod(ctxt, a, b),
          fold_block: fold::wrap(|a,b| fold_block(ctxt, a, b) ),
          fold_foreign_mod: |a,b| fold_foreign_mod(ctxt, a, b),
          fold_item_underscore: |a,b| {
            // Bad copy.
            fold_item_underscore(ctxt, copy a, b)
          },
          .. *fold::default_ast_fold()};

    let fold = fold::make_fold(precursor);
    let res = @fold.fold_crate(&*crate);
    return res;
}

fn filter_item(cx: @Context, &&item: @ast::item) ->
   Option<@ast::item> {
    if item_in_cfg(cx, item) { option::Some(item) } else { option::None }
}

fn filter_view_item(cx: @Context, &&view_item: @ast::view_item
                   )-> Option<@ast::view_item> {
    if view_item_in_cfg(cx, view_item) {
        option::Some(view_item)
    } else {
        option::None
    }
}

fn fold_mod(cx: @Context, m: &ast::_mod, fld: @fold::ast_fold) -> ast::_mod {
    let filtered_items =
        m.items.filter_mapped(|a| filter_item(cx, *a));
    let filtered_view_items =
        m.view_items.filter_mapped(|a| filter_view_item(cx, *a));
    ast::_mod {
        view_items: filtered_view_items.map(|x| fld.fold_view_item(*x)),
        items: vec::filter_map(filtered_items, |x| fld.fold_item(x))
    }
}

fn filter_foreign_item(cx: @Context, &&item: @ast::foreign_item) ->
   Option<@ast::foreign_item> {
    if foreign_item_in_cfg(cx, item) {
        option::Some(item)
    } else { option::None }
}

fn fold_foreign_mod(
    cx: @Context,
    nm: &ast::foreign_mod,
    fld: @fold::ast_fold
) -> ast::foreign_mod {
    let filtered_items =
        nm.items.filter_mapped(|a| filter_foreign_item(cx, *a));
    let filtered_view_items =
        nm.view_items.filter_mapped(|a| filter_view_item(cx, *a));
    ast::foreign_mod {
        sort: nm.sort,
        abi: nm.abi,
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: filtered_items
    }
}

fn fold_item_underscore(cx: @Context, item: &ast::item_,
                        fld: @fold::ast_fold) -> ast::item_ {
    let item = match *item {
        ast::item_impl(ref a, b, c, ref methods) => {
            let methods = methods.filtered(|m| method_in_cfg(cx, *m) );
            ast::item_impl(/*bad*/ copy *a, b, c, methods)
        }
        ast::item_trait(ref a, ref b, ref methods) => {
            let methods = methods.filtered(|m| trait_method_in_cfg(cx, m) );
            ast::item_trait(/*bad*/copy *a, /*bad*/copy *b, methods)
        }
        ref item => /*bad*/ copy *item
    };

    fold::noop_fold_item_underscore(&item, fld)
}

fn filter_stmt(cx: @Context, &&stmt: @ast::stmt) ->
   Option<@ast::stmt> {
    match stmt.node {
      ast::stmt_decl(decl, _) => {
        match decl.node {
          ast::decl_item(item) => {
            if item_in_cfg(cx, item) {
                option::Some(stmt)
            } else { option::None }
          }
          _ => option::Some(stmt)
        }
      }
      _ => option::Some(stmt)
    }
}

fn fold_block(
    cx: @Context,
    b: &ast::blk_,
    fld: @fold::ast_fold
) -> ast::blk_ {
    let filtered_stmts =
        b.stmts.filter_mapped(|a| filter_stmt(cx, *a));
    ast::blk_ {
        view_items: /*bad*/copy b.view_items,
        stmts: vec::map(filtered_stmts, |x| fld.fold_stmt(*x)),
        expr: option::map(&b.expr, |x| fld.fold_expr(*x)),
        id: b.id,
        rules: b.rules,
    }
}

fn item_in_cfg(cx: @Context, item: @ast::item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn foreign_item_in_cfg(cx: @Context, item: @ast::foreign_item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn view_item_in_cfg(cx: @Context, item: @ast::view_item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn method_in_cfg(cx: @Context, meth: @ast::method) -> bool {
    return (cx.in_cfg)(/*bad*/copy meth.attrs);
}

fn trait_method_in_cfg(cx: @Context, meth: &ast::trait_method) -> bool {
    match *meth {
        ast::required(ref meth) => (cx.in_cfg)(/*bad*/copy meth.attrs),
        ast::provided(@ref meth) => (cx.in_cfg)(/*bad*/copy meth.attrs)
    }
}

// Determine if an item should be translated in the current crate
// configuration based on the item's attributes
fn in_cfg(+cfg: ast::crate_cfg, +attrs: ~[ast::attribute]) -> bool {
    metas_in_cfg(cfg, attr::attr_metas(attrs))
}

pub fn metas_in_cfg(cfg: ast::crate_cfg,
                    +metas: ~[@ast::meta_item]) -> bool {
    // The "cfg" attributes on the item
    let cfg_metas = attr::find_meta_items_by_name(metas, ~"cfg");

    // Pull the inner meta_items from the #[cfg(meta_item, ...)]  attributes,
    // so we can match against them. This is the list of configurations for
    // which the item is valid
    let cfg_metas =
        vec::concat(
            vec::filter_map(cfg_metas, |i| attr::get_meta_item_list(i)));

    let has_cfg_metas = vec::len(cfg_metas) > 0u;
    if !has_cfg_metas { return true; }

    for cfg_metas.each |cfg_mi| {
        if attr::contains(cfg, *cfg_mi) { return true; }
    }

    return false;
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>librustc: adjust logic for cfg attribute and add not predicate.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::prelude::*;

use syntax::{ast, fold, attr};

use core::option;
use core::vec;

type in_cfg_pred = @fn(+attrs: ~[ast::attribute]) -> bool;

struct Context {
    in_cfg: in_cfg_pred
}

// Support conditional compilation by transforming the AST, stripping out
// any items that do not belong in the current configuration
pub fn strip_unconfigured_items(crate: @ast::crate) -> @ast::crate {
    do strip_items(crate) |attrs| {
        in_cfg(/*bad*/copy crate.node.config, attrs)
    }
}

pub fn strip_items(crate: @ast::crate, in_cfg: in_cfg_pred)
    -> @ast::crate {

    let ctxt = @Context { in_cfg: in_cfg };

    let precursor = @fold::AstFoldFns {
          fold_mod: |a,b| fold_mod(ctxt, a, b),
          fold_block: fold::wrap(|a,b| fold_block(ctxt, a, b) ),
          fold_foreign_mod: |a,b| fold_foreign_mod(ctxt, a, b),
          fold_item_underscore: |a,b| {
            // Bad copy.
            fold_item_underscore(ctxt, copy a, b)
          },
          .. *fold::default_ast_fold()};

    let fold = fold::make_fold(precursor);
    let res = @fold.fold_crate(&*crate);
    return res;
}

fn filter_item(cx: @Context, &&item: @ast::item) ->
   Option<@ast::item> {
    if item_in_cfg(cx, item) { option::Some(item) } else { option::None }
}

fn filter_view_item(cx: @Context, &&view_item: @ast::view_item
                   )-> Option<@ast::view_item> {
    if view_item_in_cfg(cx, view_item) {
        option::Some(view_item)
    } else {
        option::None
    }
}

fn fold_mod(cx: @Context, m: &ast::_mod, fld: @fold::ast_fold) -> ast::_mod {
    let filtered_items =
        m.items.filter_mapped(|a| filter_item(cx, *a));
    let filtered_view_items =
        m.view_items.filter_mapped(|a| filter_view_item(cx, *a));
    ast::_mod {
        view_items: filtered_view_items.map(|x| fld.fold_view_item(*x)),
        items: vec::filter_map(filtered_items, |x| fld.fold_item(x))
    }
}

fn filter_foreign_item(cx: @Context, &&item: @ast::foreign_item) ->
   Option<@ast::foreign_item> {
    if foreign_item_in_cfg(cx, item) {
        option::Some(item)
    } else { option::None }
}

fn fold_foreign_mod(
    cx: @Context,
    nm: &ast::foreign_mod,
    fld: @fold::ast_fold
) -> ast::foreign_mod {
    let filtered_items =
        nm.items.filter_mapped(|a| filter_foreign_item(cx, *a));
    let filtered_view_items =
        nm.view_items.filter_mapped(|a| filter_view_item(cx, *a));
    ast::foreign_mod {
        sort: nm.sort,
        abi: nm.abi,
        view_items: vec::map(filtered_view_items, |x| fld.fold_view_item(*x)),
        items: filtered_items
    }
}

fn fold_item_underscore(cx: @Context, item: &ast::item_,
                        fld: @fold::ast_fold) -> ast::item_ {
    let item = match *item {
        ast::item_impl(ref a, b, c, ref methods) => {
            let methods = methods.filtered(|m| method_in_cfg(cx, *m) );
            ast::item_impl(/*bad*/ copy *a, b, c, methods)
        }
        ast::item_trait(ref a, ref b, ref methods) => {
            let methods = methods.filtered(|m| trait_method_in_cfg(cx, m) );
            ast::item_trait(/*bad*/copy *a, /*bad*/copy *b, methods)
        }
        ref item => /*bad*/ copy *item
    };

    fold::noop_fold_item_underscore(&item, fld)
}

fn filter_stmt(cx: @Context, &&stmt: @ast::stmt) ->
   Option<@ast::stmt> {
    match stmt.node {
      ast::stmt_decl(decl, _) => {
        match decl.node {
          ast::decl_item(item) => {
            if item_in_cfg(cx, item) {
                option::Some(stmt)
            } else { option::None }
          }
          _ => option::Some(stmt)
        }
      }
      _ => option::Some(stmt)
    }
}

fn fold_block(
    cx: @Context,
    b: &ast::blk_,
    fld: @fold::ast_fold
) -> ast::blk_ {
    let filtered_stmts =
        b.stmts.filter_mapped(|a| filter_stmt(cx, *a));
    ast::blk_ {
        view_items: /*bad*/copy b.view_items,
        stmts: vec::map(filtered_stmts, |x| fld.fold_stmt(*x)),
        expr: option::map(&b.expr, |x| fld.fold_expr(*x)),
        id: b.id,
        rules: b.rules,
    }
}

fn item_in_cfg(cx: @Context, item: @ast::item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn foreign_item_in_cfg(cx: @Context, item: @ast::foreign_item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn view_item_in_cfg(cx: @Context, item: @ast::view_item) -> bool {
    return (cx.in_cfg)(/*bad*/copy item.attrs);
}

fn method_in_cfg(cx: @Context, meth: @ast::method) -> bool {
    return (cx.in_cfg)(/*bad*/copy meth.attrs);
}

fn trait_method_in_cfg(cx: @Context, meth: &ast::trait_method) -> bool {
    match *meth {
        ast::required(ref meth) => (cx.in_cfg)(/*bad*/copy meth.attrs),
        ast::provided(@ref meth) => (cx.in_cfg)(/*bad*/copy meth.attrs)
    }
}

// Determine if an item should be translated in the current crate
// configuration based on the item's attributes
fn in_cfg(+cfg: ast::crate_cfg, +attrs: ~[ast::attribute]) -> bool {
    metas_in_cfg(cfg, attr::attr_metas(attrs))
}

pub fn metas_in_cfg(cfg: ast::crate_cfg,
                    +metas: ~[@ast::meta_item]) -> bool {
    // The "cfg" attributes on the item
    let cfg_metas = attr::find_meta_items_by_name(metas, ~"cfg");

    // Pull the inner meta_items from the #[cfg(meta_item, ...)]  attributes,
    // so we can match against them. This is the list of configurations for
    // which the item is valid
    let cfg_metas = vec::filter_map(cfg_metas, |i| attr::get_meta_item_list(i));

    if cfg_metas.all(|c| c.is_empty()) { return true; }

    cfg_metas.any(|cfg_meta| {
        cfg_meta.all(|cfg_mi| {
            match cfg_mi.node {
                ast::meta_list(s, ref it) if *s == ~"not"
                    => it.all(|mi| !attr::contains(cfg, *mi)),
                _ => attr::contains(cfg, *cfg_mi)
            }
        })
    })
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


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

/*!

A graph module for use in dataflow, region resolution, and elsewhere.

# Interface details

You customize the graph by specifying a "node data" type `N` and an
"edge data" type `E`. You can then later gain access (mutable or
immutable) to these "user-data" bits. Currently, you can only add
nodes or edges to the graph. You cannot remove or modify them once
added. This could be changed if we have a need.

# Implementation details

The main tricky thing about this code is the way that edges are
stored. The edges are stored in a central array, but they are also
threaded onto two linked lists for each node, one for incoming edges
and one for outgoing edges. Note that every edge is a member of some
incoming list and some outgoing list.  Basically you can load the
first index of the linked list from the node data structures (the
field `first_edge`) and then, for each edge, load the next index from
the field `next_edge`). Each of those fields is an array that should
be indexed by the direction (see the type `Direction`).

*/

#![allow(dead_code)] // still WIP

use std::uint;

pub struct Graph<N,E> {
    nodes: Vec<Node<N>> ,
    edges: Vec<Edge<E>> ,
}

pub struct Node<N> {
    first_edge: [EdgeIndex, ..2], // see module comment
    pub data: N,
}

pub struct Edge<E> {
    next_edge: [EdgeIndex, ..2], // see module comment
    source: NodeIndex,
    target: NodeIndex,
    pub data: E,
}

#[deriving(PartialEq)]
pub struct NodeIndex(pub uint);
pub static InvalidNodeIndex: NodeIndex = NodeIndex(uint::MAX);

#[deriving(PartialEq)]
pub struct EdgeIndex(pub uint);
pub static InvalidEdgeIndex: EdgeIndex = EdgeIndex(uint::MAX);

// Use a private field here to guarantee no more instances are created:
pub struct Direction { repr: uint }
pub static Outgoing: Direction = Direction { repr: 0 };
pub static Incoming: Direction = Direction { repr: 1 };

impl NodeIndex {
    fn get(&self) -> uint { let NodeIndex(v) = *self; v }
    /// Returns unique id (unique with respect to the graph holding associated node).
    pub fn node_id(&self) -> uint { self.get() }
}

impl EdgeIndex {
    fn get(&self) -> uint { let EdgeIndex(v) = *self; v }
    /// Returns unique id (unique with respect to the graph holding associated edge).
    pub fn edge_id(&self) -> uint { self.get() }
}

impl<N,E> Graph<N,E> {
    pub fn new() -> Graph<N,E> {
        Graph {
            nodes: Vec::new(),
            edges: Vec::new(),
        }
    }

    pub fn with_capacity(num_nodes: uint,
                         num_edges: uint) -> Graph<N,E> {
        Graph {
            nodes: Vec::with_capacity(num_nodes),
            edges: Vec::with_capacity(num_edges),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Simple accessors

    #[inline]
    pub fn all_nodes<'a>(&'a self) -> &'a [Node<N>] {
        let nodes: &'a [Node<N>] = self.nodes.as_slice();
        nodes
    }

    #[inline]
    pub fn all_edges<'a>(&'a self) -> &'a [Edge<E>] {
        let edges: &'a [Edge<E>] = self.edges.as_slice();
        edges
    }

    ///////////////////////////////////////////////////////////////////////////
    // Node construction

    pub fn next_node_index(&self) -> NodeIndex {
        NodeIndex(self.nodes.len())
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex {
        let idx = self.next_node_index();
        self.nodes.push(Node {
            first_edge: [InvalidEdgeIndex, InvalidEdgeIndex],
            data: data
        });
        idx
    }

    pub fn mut_node_data<'a>(&'a mut self, idx: NodeIndex) -> &'a mut N {
        &mut self.nodes.get_mut(idx.get()).data
    }

    pub fn node_data<'a>(&'a self, idx: NodeIndex) -> &'a N {
        &self.nodes.get(idx.get()).data
    }

    pub fn node<'a>(&'a self, idx: NodeIndex) -> &'a Node<N> {
        self.nodes.get(idx.get())
    }

    ///////////////////////////////////////////////////////////////////////////
    // Edge construction and queries

    pub fn next_edge_index(&self) -> EdgeIndex {
        EdgeIndex(self.edges.len())
    }

    pub fn add_edge(&mut self,
                    source: NodeIndex,
                    target: NodeIndex,
                    data: E) -> EdgeIndex {
        let idx = self.next_edge_index();

        // read current first of the list of edges from each node
        let source_first = self.nodes.get(source.get())
                                     .first_edge[Outgoing.repr];
        let target_first = self.nodes.get(target.get())
                                     .first_edge[Incoming.repr];

        // create the new edge, with the previous firsts from each node
        // as the next pointers
        self.edges.push(Edge {
            next_edge: [source_first, target_first],
            source: source,
            target: target,
            data: data
        });

        // adjust the firsts for each node target be the next object.
        self.nodes.get_mut(source.get()).first_edge[Outgoing.repr] = idx;
        self.nodes.get_mut(target.get()).first_edge[Incoming.repr] = idx;

        return idx;
    }

    pub fn mut_edge_data<'a>(&'a mut self, idx: EdgeIndex) -> &'a mut E {
        &mut self.edges.get_mut(idx.get()).data
    }

    pub fn edge_data<'a>(&'a self, idx: EdgeIndex) -> &'a E {
        &self.edges.get(idx.get()).data
    }

    pub fn edge<'a>(&'a self, idx: EdgeIndex) -> &'a Edge<E> {
        self.edges.get(idx.get())
    }

    pub fn first_adjacent(&self, node: NodeIndex, dir: Direction) -> EdgeIndex {
        //! Accesses the index of the first edge adjacent to `node`.
        //! This is useful if you wish to modify the graph while walking
        //! the linked list of edges.

        self.nodes.get(node.get()).first_edge[dir.repr]
    }

    pub fn next_adjacent(&self, edge: EdgeIndex, dir: Direction) -> EdgeIndex {
        //! Accesses the next edge in a given direction.
        //! This is useful if you wish to modify the graph while walking
        //! the linked list of edges.

        self.edges.get(edge.get()).next_edge[dir.repr]
    }

    ///////////////////////////////////////////////////////////////////////////
    // Iterating over nodes, edges

    pub fn each_node<'a>(&'a self, f: |NodeIndex, &'a Node<N>| -> bool) -> bool {
        //! Iterates over all edges defined in the graph.
        self.nodes.iter().enumerate().advance(|(i, node)| f(NodeIndex(i), node))
    }

    pub fn each_edge<'a>(&'a self, f: |EdgeIndex, &'a Edge<E>| -> bool) -> bool {
        //! Iterates over all edges defined in the graph
        self.edges.iter().enumerate().advance(|(i, edge)| f(EdgeIndex(i), edge))
    }

    pub fn each_outgoing_edge<'a>(&'a self,
                                  source: NodeIndex,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all outgoing edges from the node `from`

        self.each_adjacent_edge(source, Outgoing, f)
    }

    pub fn each_incoming_edge<'a>(&'a self,
                                  target: NodeIndex,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all incoming edges to the node `target`

        self.each_adjacent_edge(target, Incoming, f)
    }

    pub fn each_adjacent_edge<'a>(&'a self,
                                  node: NodeIndex,
                                  dir: Direction,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all edges adjacent to the node `node`
        //! in the direction `dir` (either `Outgoing` or `Incoming)

        let mut edge_idx = self.first_adjacent(node, dir);
        while edge_idx != InvalidEdgeIndex {
            let edge = self.edges.get(edge_idx.get());
            if !f(edge_idx, edge) {
                return false;
            }
            edge_idx = edge.next_edge[dir.repr];
        }
        return true;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Fixed-point iteration
    //
    // A common use for graphs in our compiler is to perform
    // fixed-point iteration. In this case, each edge represents a
    // constraint, and the nodes themselves are associated with
    // variables or other bitsets. This method facilitates such a
    // computation.

    pub fn iterate_until_fixed_point<'a>(&'a self,
                                         op: |iter_index: uint,
                                              edge_index: EdgeIndex,
                                              edge: &'a Edge<E>|
                                              -> bool) {
        let mut iteration = 0;
        let mut changed = true;
        while changed {
            changed = false;
            iteration += 1;
            for (i, edge) in self.edges.iter().enumerate() {
                changed |= op(iteration, EdgeIndex(i), edge);
            }
        }
    }
}

pub fn each_edge_index(max_edge_index: EdgeIndex, f: |EdgeIndex| -> bool) {
    let mut i = 0;
    let n = max_edge_index.get();
    while i < n {
        if !f(EdgeIndex(i)) {
            return;
        }
        i += 1;
    }
}

impl<E> Edge<E> {
    pub fn source(&self) -> NodeIndex {
        self.source
    }

    pub fn target(&self) -> NodeIndex {
        self.target
    }
}

#[cfg(test)]
mod test {
    use middle::graph::*;

    type TestNode = Node<&'static str>;
    type TestEdge = Edge<&'static str>;
    type TestGraph = Graph<&'static str, &'static str>;

    fn create_graph() -> TestGraph {
        let mut graph = Graph::new();

        // Create a simple graph
        //
        //    A -+> B --> C
        //       |  |     ^
        //       |  v     |
        //       F  D --> E

        let a = graph.add_node("A");
        let b = graph.add_node("B");
        let c = graph.add_node("C");
        let d = graph.add_node("D");
        let e = graph.add_node("E");
        let f = graph.add_node("F");

        graph.add_edge(a, b, "AB");
        graph.add_edge(b, c, "BC");
        graph.add_edge(b, d, "BD");
        graph.add_edge(d, e, "DE");
        graph.add_edge(e, c, "EC");
        graph.add_edge(f, b, "FB");

        return graph;
    }

    #[test]
    fn each_node() {
        let graph = create_graph();
        let expected = ["A", "B", "C", "D", "E", "F"];
        graph.each_node(|idx, node| {
            assert_eq!(&expected[idx.get()], graph.node_data(idx));
            assert_eq!(expected[idx.get()], node.data);
            true
        });
    }

    #[test]
    fn each_edge() {
        let graph = create_graph();
        let expected = ["AB", "BC", "BD", "DE", "EC", "FB"];
        graph.each_edge(|idx, edge| {
            assert_eq!(&expected[idx.get()], graph.edge_data(idx));
            assert_eq!(expected[idx.get()], edge.data);
            true
        });
    }

    fn test_adjacent_edges<N:PartialEq,E:PartialEq>(graph: &Graph<N,E>,
                                      start_index: NodeIndex,
                                      start_data: N,
                                      expected_incoming: &[(E,N)],
                                      expected_outgoing: &[(E,N)]) {
        assert!(graph.node_data(start_index) == &start_data);

        let mut counter = 0;
        graph.each_incoming_edge(start_index, |edge_index, edge| {
            assert!(graph.edge_data(edge_index) == &edge.data);
            assert!(counter < expected_incoming.len());
            debug!("counter={:?} expected={:?} edge_index={:?} edge={:?}",
                   counter, expected_incoming[counter], edge_index, edge);
            match expected_incoming[counter] {
                (ref e, ref n) => {
                    assert!(e == &edge.data);
                    assert!(n == graph.node_data(edge.source));
                    assert!(start_index == edge.target);
                }
            }
            counter += 1;
            true
        });
        assert_eq!(counter, expected_incoming.len());

        let mut counter = 0;
        graph.each_outgoing_edge(start_index, |edge_index, edge| {
            assert!(graph.edge_data(edge_index) == &edge.data);
            assert!(counter < expected_outgoing.len());
            debug!("counter={:?} expected={:?} edge_index={:?} edge={:?}",
                   counter, expected_outgoing[counter], edge_index, edge);
            match expected_outgoing[counter] {
                (ref e, ref n) => {
                    assert!(e == &edge.data);
                    assert!(start_index == edge.source);
                    assert!(n == graph.node_data(edge.target));
                }
            }
            counter += 1;
            true
        });
        assert_eq!(counter, expected_outgoing.len());
    }

    #[test]
    fn each_adjacent_from_a() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(0), "A",
                            [],
                            [("AB", "B")]);
    }

    #[test]
    fn each_adjacent_from_b() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(1), "B",
                            [("FB", "F"), ("AB", "A"),],
                            [("BD", "D"), ("BC", "C"),]);
    }

    #[test]
    fn each_adjacent_from_c() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(2), "C",
                            [("EC", "E"), ("BC", "B")],
                            []);
    }

    #[test]
    fn each_adjacent_from_d() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(3), "D",
                            [("BD", "B")],
                            [("DE", "E")]);
    }
}
<commit_msg>NodeIndex should derive `Show`.<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

A graph module for use in dataflow, region resolution, and elsewhere.

# Interface details

You customize the graph by specifying a "node data" type `N` and an
"edge data" type `E`. You can then later gain access (mutable or
immutable) to these "user-data" bits. Currently, you can only add
nodes or edges to the graph. You cannot remove or modify them once
added. This could be changed if we have a need.

# Implementation details

The main tricky thing about this code is the way that edges are
stored. The edges are stored in a central array, but they are also
threaded onto two linked lists for each node, one for incoming edges
and one for outgoing edges. Note that every edge is a member of some
incoming list and some outgoing list.  Basically you can load the
first index of the linked list from the node data structures (the
field `first_edge`) and then, for each edge, load the next index from
the field `next_edge`). Each of those fields is an array that should
be indexed by the direction (see the type `Direction`).

*/

#![allow(dead_code)] // still WIP

use std::uint;

pub struct Graph<N,E> {
    nodes: Vec<Node<N>> ,
    edges: Vec<Edge<E>> ,
}

pub struct Node<N> {
    first_edge: [EdgeIndex, ..2], // see module comment
    pub data: N,
}

pub struct Edge<E> {
    next_edge: [EdgeIndex, ..2], // see module comment
    source: NodeIndex,
    target: NodeIndex,
    pub data: E,
}

#[deriving(PartialEq, Show)]
pub struct NodeIndex(pub uint);
pub static InvalidNodeIndex: NodeIndex = NodeIndex(uint::MAX);

#[deriving(PartialEq)]
pub struct EdgeIndex(pub uint);
pub static InvalidEdgeIndex: EdgeIndex = EdgeIndex(uint::MAX);

// Use a private field here to guarantee no more instances are created:
pub struct Direction { repr: uint }
pub static Outgoing: Direction = Direction { repr: 0 };
pub static Incoming: Direction = Direction { repr: 1 };

impl NodeIndex {
    fn get(&self) -> uint { let NodeIndex(v) = *self; v }
    /// Returns unique id (unique with respect to the graph holding associated node).
    pub fn node_id(&self) -> uint { self.get() }
}

impl EdgeIndex {
    fn get(&self) -> uint { let EdgeIndex(v) = *self; v }
    /// Returns unique id (unique with respect to the graph holding associated edge).
    pub fn edge_id(&self) -> uint { self.get() }
}

impl<N,E> Graph<N,E> {
    pub fn new() -> Graph<N,E> {
        Graph {
            nodes: Vec::new(),
            edges: Vec::new(),
        }
    }

    pub fn with_capacity(num_nodes: uint,
                         num_edges: uint) -> Graph<N,E> {
        Graph {
            nodes: Vec::with_capacity(num_nodes),
            edges: Vec::with_capacity(num_edges),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Simple accessors

    #[inline]
    pub fn all_nodes<'a>(&'a self) -> &'a [Node<N>] {
        let nodes: &'a [Node<N>] = self.nodes.as_slice();
        nodes
    }

    #[inline]
    pub fn all_edges<'a>(&'a self) -> &'a [Edge<E>] {
        let edges: &'a [Edge<E>] = self.edges.as_slice();
        edges
    }

    ///////////////////////////////////////////////////////////////////////////
    // Node construction

    pub fn next_node_index(&self) -> NodeIndex {
        NodeIndex(self.nodes.len())
    }

    pub fn add_node(&mut self, data: N) -> NodeIndex {
        let idx = self.next_node_index();
        self.nodes.push(Node {
            first_edge: [InvalidEdgeIndex, InvalidEdgeIndex],
            data: data
        });
        idx
    }

    pub fn mut_node_data<'a>(&'a mut self, idx: NodeIndex) -> &'a mut N {
        &mut self.nodes.get_mut(idx.get()).data
    }

    pub fn node_data<'a>(&'a self, idx: NodeIndex) -> &'a N {
        &self.nodes.get(idx.get()).data
    }

    pub fn node<'a>(&'a self, idx: NodeIndex) -> &'a Node<N> {
        self.nodes.get(idx.get())
    }

    ///////////////////////////////////////////////////////////////////////////
    // Edge construction and queries

    pub fn next_edge_index(&self) -> EdgeIndex {
        EdgeIndex(self.edges.len())
    }

    pub fn add_edge(&mut self,
                    source: NodeIndex,
                    target: NodeIndex,
                    data: E) -> EdgeIndex {
        let idx = self.next_edge_index();

        // read current first of the list of edges from each node
        let source_first = self.nodes.get(source.get())
                                     .first_edge[Outgoing.repr];
        let target_first = self.nodes.get(target.get())
                                     .first_edge[Incoming.repr];

        // create the new edge, with the previous firsts from each node
        // as the next pointers
        self.edges.push(Edge {
            next_edge: [source_first, target_first],
            source: source,
            target: target,
            data: data
        });

        // adjust the firsts for each node target be the next object.
        self.nodes.get_mut(source.get()).first_edge[Outgoing.repr] = idx;
        self.nodes.get_mut(target.get()).first_edge[Incoming.repr] = idx;

        return idx;
    }

    pub fn mut_edge_data<'a>(&'a mut self, idx: EdgeIndex) -> &'a mut E {
        &mut self.edges.get_mut(idx.get()).data
    }

    pub fn edge_data<'a>(&'a self, idx: EdgeIndex) -> &'a E {
        &self.edges.get(idx.get()).data
    }

    pub fn edge<'a>(&'a self, idx: EdgeIndex) -> &'a Edge<E> {
        self.edges.get(idx.get())
    }

    pub fn first_adjacent(&self, node: NodeIndex, dir: Direction) -> EdgeIndex {
        //! Accesses the index of the first edge adjacent to `node`.
        //! This is useful if you wish to modify the graph while walking
        //! the linked list of edges.

        self.nodes.get(node.get()).first_edge[dir.repr]
    }

    pub fn next_adjacent(&self, edge: EdgeIndex, dir: Direction) -> EdgeIndex {
        //! Accesses the next edge in a given direction.
        //! This is useful if you wish to modify the graph while walking
        //! the linked list of edges.

        self.edges.get(edge.get()).next_edge[dir.repr]
    }

    ///////////////////////////////////////////////////////////////////////////
    // Iterating over nodes, edges

    pub fn each_node<'a>(&'a self, f: |NodeIndex, &'a Node<N>| -> bool) -> bool {
        //! Iterates over all edges defined in the graph.
        self.nodes.iter().enumerate().advance(|(i, node)| f(NodeIndex(i), node))
    }

    pub fn each_edge<'a>(&'a self, f: |EdgeIndex, &'a Edge<E>| -> bool) -> bool {
        //! Iterates over all edges defined in the graph
        self.edges.iter().enumerate().advance(|(i, edge)| f(EdgeIndex(i), edge))
    }

    pub fn each_outgoing_edge<'a>(&'a self,
                                  source: NodeIndex,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all outgoing edges from the node `from`

        self.each_adjacent_edge(source, Outgoing, f)
    }

    pub fn each_incoming_edge<'a>(&'a self,
                                  target: NodeIndex,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all incoming edges to the node `target`

        self.each_adjacent_edge(target, Incoming, f)
    }

    pub fn each_adjacent_edge<'a>(&'a self,
                                  node: NodeIndex,
                                  dir: Direction,
                                  f: |EdgeIndex, &'a Edge<E>| -> bool)
                                  -> bool {
        //! Iterates over all edges adjacent to the node `node`
        //! in the direction `dir` (either `Outgoing` or `Incoming)

        let mut edge_idx = self.first_adjacent(node, dir);
        while edge_idx != InvalidEdgeIndex {
            let edge = self.edges.get(edge_idx.get());
            if !f(edge_idx, edge) {
                return false;
            }
            edge_idx = edge.next_edge[dir.repr];
        }
        return true;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Fixed-point iteration
    //
    // A common use for graphs in our compiler is to perform
    // fixed-point iteration. In this case, each edge represents a
    // constraint, and the nodes themselves are associated with
    // variables or other bitsets. This method facilitates such a
    // computation.

    pub fn iterate_until_fixed_point<'a>(&'a self,
                                         op: |iter_index: uint,
                                              edge_index: EdgeIndex,
                                              edge: &'a Edge<E>|
                                              -> bool) {
        let mut iteration = 0;
        let mut changed = true;
        while changed {
            changed = false;
            iteration += 1;
            for (i, edge) in self.edges.iter().enumerate() {
                changed |= op(iteration, EdgeIndex(i), edge);
            }
        }
    }
}

pub fn each_edge_index(max_edge_index: EdgeIndex, f: |EdgeIndex| -> bool) {
    let mut i = 0;
    let n = max_edge_index.get();
    while i < n {
        if !f(EdgeIndex(i)) {
            return;
        }
        i += 1;
    }
}

impl<E> Edge<E> {
    pub fn source(&self) -> NodeIndex {
        self.source
    }

    pub fn target(&self) -> NodeIndex {
        self.target
    }
}

#[cfg(test)]
mod test {
    use middle::graph::*;

    type TestNode = Node<&'static str>;
    type TestEdge = Edge<&'static str>;
    type TestGraph = Graph<&'static str, &'static str>;

    fn create_graph() -> TestGraph {
        let mut graph = Graph::new();

        // Create a simple graph
        //
        //    A -+> B --> C
        //       |  |     ^
        //       |  v     |
        //       F  D --> E

        let a = graph.add_node("A");
        let b = graph.add_node("B");
        let c = graph.add_node("C");
        let d = graph.add_node("D");
        let e = graph.add_node("E");
        let f = graph.add_node("F");

        graph.add_edge(a, b, "AB");
        graph.add_edge(b, c, "BC");
        graph.add_edge(b, d, "BD");
        graph.add_edge(d, e, "DE");
        graph.add_edge(e, c, "EC");
        graph.add_edge(f, b, "FB");

        return graph;
    }

    #[test]
    fn each_node() {
        let graph = create_graph();
        let expected = ["A", "B", "C", "D", "E", "F"];
        graph.each_node(|idx, node| {
            assert_eq!(&expected[idx.get()], graph.node_data(idx));
            assert_eq!(expected[idx.get()], node.data);
            true
        });
    }

    #[test]
    fn each_edge() {
        let graph = create_graph();
        let expected = ["AB", "BC", "BD", "DE", "EC", "FB"];
        graph.each_edge(|idx, edge| {
            assert_eq!(&expected[idx.get()], graph.edge_data(idx));
            assert_eq!(expected[idx.get()], edge.data);
            true
        });
    }

    fn test_adjacent_edges<N:PartialEq,E:PartialEq>(graph: &Graph<N,E>,
                                      start_index: NodeIndex,
                                      start_data: N,
                                      expected_incoming: &[(E,N)],
                                      expected_outgoing: &[(E,N)]) {
        assert!(graph.node_data(start_index) == &start_data);

        let mut counter = 0;
        graph.each_incoming_edge(start_index, |edge_index, edge| {
            assert!(graph.edge_data(edge_index) == &edge.data);
            assert!(counter < expected_incoming.len());
            debug!("counter={:?} expected={:?} edge_index={:?} edge={:?}",
                   counter, expected_incoming[counter], edge_index, edge);
            match expected_incoming[counter] {
                (ref e, ref n) => {
                    assert!(e == &edge.data);
                    assert!(n == graph.node_data(edge.source));
                    assert!(start_index == edge.target);
                }
            }
            counter += 1;
            true
        });
        assert_eq!(counter, expected_incoming.len());

        let mut counter = 0;
        graph.each_outgoing_edge(start_index, |edge_index, edge| {
            assert!(graph.edge_data(edge_index) == &edge.data);
            assert!(counter < expected_outgoing.len());
            debug!("counter={:?} expected={:?} edge_index={:?} edge={:?}",
                   counter, expected_outgoing[counter], edge_index, edge);
            match expected_outgoing[counter] {
                (ref e, ref n) => {
                    assert!(e == &edge.data);
                    assert!(start_index == edge.source);
                    assert!(n == graph.node_data(edge.target));
                }
            }
            counter += 1;
            true
        });
        assert_eq!(counter, expected_outgoing.len());
    }

    #[test]
    fn each_adjacent_from_a() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(0), "A",
                            [],
                            [("AB", "B")]);
    }

    #[test]
    fn each_adjacent_from_b() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(1), "B",
                            [("FB", "F"), ("AB", "A"),],
                            [("BD", "D"), ("BC", "C"),]);
    }

    #[test]
    fn each_adjacent_from_c() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(2), "C",
                            [("EC", "E"), ("BC", "B")],
                            []);
    }

    #[test]
    fn each_adjacent_from_d() {
        let graph = create_graph();
        test_adjacent_edges(&graph, NodeIndex(3), "D",
                            [("BD", "B")],
                            [("DE", "E")]);
    }
}
<|endoftext|>


<commit_before>//! Platform-independent platform abstraction
//!
//! This is the platform-independent portion of the standard library's
//! platform abstraction layer, whereas `std::sys` is the
//! platform-specific portion.
//!
//! The relationship between `std::sys_common`, `std::sys` and the
//! rest of `std` is complex, with dependencies going in all
//! directions: `std` depending on `sys_common`, `sys_common`
//! depending on `sys`, and `sys` depending on `sys_common` and `std`.
//! Ideally `sys_common` would be split into two and the dependencies
//! between them all would form a dag, facilitating the extraction of
//! `std::sys` from the standard library.

#![allow(missing_docs)]
#![allow(missing_debug_implementations)]

use crate::sync::Once;
use crate::sys;

macro_rules! rtabort {
    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
}

macro_rules! rtassert {
    ($e:expr) => (if !$e {
        rtabort!(concat!("assertion failed: ", stringify!($e)));
    })
}

#[allow(unused_macros)] // not used on all platforms
macro_rules! rtunwrap {
    ($ok:ident, $e:expr) => (if let $ok(v) = $e {
        v
    } else {
        rtabort!(concat!("unwrap failed: ", stringify!($e)));
    })
}

pub mod alloc;
pub mod at_exit_imp;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod condvar;
pub mod io;
pub mod mutex;
#[cfg(any(rustdoc, // see `mod os`, docs are generated for multiple platforms
          unix,
          target_os = "redox",
          target_os = "cloudabi",
          target_arch = "wasm32",
          all(target_vendor = "fortanix", target_env = "sgx")))]
pub mod os_str_bytes;
pub mod poison;
pub mod remutex;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
pub mod thread_local;
pub mod util;
pub mod wtf8;
pub mod bytestring;
pub mod process;
pub mod fs;

cfg_if! {
    if #[cfg(any(target_os = "cloudabi",
                 target_os = "l4re",
                 target_os = "redox",
                 all(target_arch = "wasm32", not(target_os = "emscripten")),
                 all(target_vendor = "fortanix", target_env = "sgx")))] {
        pub use crate::sys::net;
    } else {
        pub mod net;
    }
}

#[cfg(feature = "backtrace")]
#[cfg(any(all(unix, not(target_os = "emscripten")),
          all(windows, target_env = "gnu"),
          target_os = "redox"))]
pub mod gnu;

// common error constructors

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInner<Inner: ?Sized> {
    fn as_inner(&self) -> &Inner;
}

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInnerMut<Inner: ?Sized> {
    fn as_inner_mut(&mut self) -> &mut Inner;
}

/// A trait for extracting representations from std types
#[doc(hidden)]
pub trait IntoInner<Inner> {
    fn into_inner(self) -> Inner;
}

/// A trait for creating std types from internal representations
#[doc(hidden)]
pub trait FromInner<Inner> {
    fn from_inner(inner: Inner) -> Self;
}

/// Enqueues a procedure to run when the main thread exits.
///
/// Currently these closures are only run once the main *Rust* thread exits.
/// Once the `at_exit` handlers begin running, more may be enqueued, but not
/// infinitely so. Eventually a handler registration will be forced to fail.
///
/// Returns `Ok` if the handler was successfully registered, meaning that the
/// closure will be run once the main thread exits. Returns `Err` to indicate
/// that the closure could not be registered, meaning that it is not scheduled
/// to be run.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
    if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}

/// One-time runtime cleanup.
pub fn cleanup() {
    static CLEANUP: Once = Once::new();
    CLEANUP.call_once(|| unsafe {
        sys::args::cleanup();
        sys::stack_overflow::cleanup();
        at_exit_imp::cleanup();
    });
}

// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).
#[allow(dead_code)] // not used on all platforms
pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
    let q = value / denom;
    let r = value % denom;
    // Decompose value as (value/denom*denom + value%denom),
    // substitute into (value*numer)/denom and simplify.
    // r < denom, so (denom*numer) is the upper bound of (r*numer)
    q * numer + r * numer / denom
}

#[test]
fn test_muldiv() {
    assert_eq!(mul_div_u64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
               1_000_000_000_001_000);
}
<commit_msg>Debug-print error when using rtunwrap<commit_after>//! Platform-independent platform abstraction
//!
//! This is the platform-independent portion of the standard library's
//! platform abstraction layer, whereas `std::sys` is the
//! platform-specific portion.
//!
//! The relationship between `std::sys_common`, `std::sys` and the
//! rest of `std` is complex, with dependencies going in all
//! directions: `std` depending on `sys_common`, `sys_common`
//! depending on `sys`, and `sys` depending on `sys_common` and `std`.
//! Ideally `sys_common` would be split into two and the dependencies
//! between them all would form a dag, facilitating the extraction of
//! `std::sys` from the standard library.

#![allow(missing_docs)]
#![allow(missing_debug_implementations)]

use crate::sync::Once;
use crate::sys;

macro_rules! rtabort {
    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
}

macro_rules! rtassert {
    ($e:expr) => (if !$e {
        rtabort!(concat!("assertion failed: ", stringify!($e)));
    })
}

#[allow(unused_macros)] // not used on all platforms
macro_rules! rtunwrap {
    ($ok:ident, $e:expr) => (match $e {
        $ok(v) => v,
        ref err => {
            let err = err.as_ref().map(|_|()); // map Ok/Some which might not be Debug
            rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
        },
    })
}

pub mod alloc;
pub mod at_exit_imp;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod condvar;
pub mod io;
pub mod mutex;
#[cfg(any(rustdoc, // see `mod os`, docs are generated for multiple platforms
          unix,
          target_os = "redox",
          target_os = "cloudabi",
          target_arch = "wasm32",
          all(target_vendor = "fortanix", target_env = "sgx")))]
pub mod os_str_bytes;
pub mod poison;
pub mod remutex;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
pub mod thread_local;
pub mod util;
pub mod wtf8;
pub mod bytestring;
pub mod process;
pub mod fs;

cfg_if! {
    if #[cfg(any(target_os = "cloudabi",
                 target_os = "l4re",
                 target_os = "redox",
                 all(target_arch = "wasm32", not(target_os = "emscripten")),
                 all(target_vendor = "fortanix", target_env = "sgx")))] {
        pub use crate::sys::net;
    } else {
        pub mod net;
    }
}

#[cfg(feature = "backtrace")]
#[cfg(any(all(unix, not(target_os = "emscripten")),
          all(windows, target_env = "gnu"),
          target_os = "redox"))]
pub mod gnu;

// common error constructors

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInner<Inner: ?Sized> {
    fn as_inner(&self) -> &Inner;
}

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInnerMut<Inner: ?Sized> {
    fn as_inner_mut(&mut self) -> &mut Inner;
}

/// A trait for extracting representations from std types
#[doc(hidden)]
pub trait IntoInner<Inner> {
    fn into_inner(self) -> Inner;
}

/// A trait for creating std types from internal representations
#[doc(hidden)]
pub trait FromInner<Inner> {
    fn from_inner(inner: Inner) -> Self;
}

/// Enqueues a procedure to run when the main thread exits.
///
/// Currently these closures are only run once the main *Rust* thread exits.
/// Once the `at_exit` handlers begin running, more may be enqueued, but not
/// infinitely so. Eventually a handler registration will be forced to fail.
///
/// Returns `Ok` if the handler was successfully registered, meaning that the
/// closure will be run once the main thread exits. Returns `Err` to indicate
/// that the closure could not be registered, meaning that it is not scheduled
/// to be run.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
    if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}

/// One-time runtime cleanup.
pub fn cleanup() {
    static CLEANUP: Once = Once::new();
    CLEANUP.call_once(|| unsafe {
        sys::args::cleanup();
        sys::stack_overflow::cleanup();
        at_exit_imp::cleanup();
    });
}

// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).
#[allow(dead_code)] // not used on all platforms
pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
    let q = value / denom;
    let r = value % denom;
    // Decompose value as (value/denom*denom + value%denom),
    // substitute into (value*numer)/denom and simplify.
    // r < denom, so (denom*numer) is the upper bound of (r*numer)
    q * numer + r * numer / denom
}

#[test]
fn test_muldiv() {
    assert_eq!(mul_div_u64( 1_000_000_000_001, 1_000_000_000, 1_000_000),
               1_000_000_000_001_000);
}
<|endoftext|>


<commit_before>use chrono::*;

#[derive(Eq, PartialEq, Clone, Debug)]
pub enum HttpError {
    ClientError4xx,
    ServerError5xx,
}

#[derive(PartialEq,Debug)]
pub enum LogEvent {
    Request(Request),
    Response(Response),
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Request {
    pub id: i32,
    pub time: DateTime<FixedOffset>,
    pub original_log_line: String,
}

impl Request {
    pub fn new_from_log_line(log_line: &str) -> Result<Request, &'static str> {
        let parts: Vec<&str> = log_line.split(' ').collect();

        let id = match parts.get(2) {
            Some(id) => id,
            None => return Err("Uncomprehensible request logline"),
        };

        // Shortest valid id format is "[1]"
        if id.len() < 3 {
            return Err("Uncomprehensible request logline");
        }

        let id_parsed: i32 = match id[1..id.len() - 1].parse() {
            Ok(id) => id,
            Err(_) => return Err("Uncomprehensible request logline"),
        };

        let date = &format!("{} {}", parts[0], parts[1]);

        let date_parsed = match DateTime::parse_from_str(date, "%d/%b/%Y:%H:%M:%S %z") {
            Ok(date_time) => date_time,
            Err(_) => return Err("Uncomprehensible request logline"),
        };

        Ok(Request {
            id: id_parsed,
            time: date_parsed,
            original_log_line: log_line.to_string(),
        })
    }
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Response {
    pub id: i32,
    pub response_time: Duration,
    pub original_log_line: String,
    pub http_error: Option<HttpError>,
}

impl Response {
    pub fn new_from_log_line(log_line: &str) -> Result<Response, &'static str> {
        let parts: Vec<&str> = log_line.split(' ').collect();

        let id = parts[2];

        // Shortest valid id format is "[1]"
        if id.len() < 3 {
            return Err("Uncomprehensible response logline");
        }

        let id_numeric: i32 = match id[1..id.len() - 1].parse() {
            Ok(number) => number,
            Err(_) => return Err("Uncomprehensible response logline"),
        };

        let response_time = parts[parts.len() - 1];
        if response_time.len() < 3 {
            return Err("Uncomprehensible response logline");
        }

        let response_time_duration = match response_time[0..response_time.len() - 2].parse() {
            Ok(number) => Duration::milliseconds(number),
            Err(_) => return Err("Uncomprehensible response logline"),
        };

        let http_error = match parts[4].chars().nth(0) {
            Some('4') => Some(HttpError::ClientError4xx),
            Some('5') => Some(HttpError::ServerError5xx),
            _ => None,
        };

        Ok(Response {
            id: id_numeric,
            response_time: response_time_duration,
            http_error: http_error,
            original_log_line: log_line.to_string(),
        })
    }
}

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

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected = Request {
            id: 2,
            time: DateTime::parse_from_str("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S %z")
                .unwrap(),
            original_log_line: line.clone(),
        };

        let result = Request::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_request_line_bad_format() {
        let line = "08/A16:09:58:47 justsomegarbage".to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_format_but_enough_parts() {
        let line = "just some garbage with more parts at the end".to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_date_format() {
        let line = "99/XYZ/9999:09:99:99 +9900 [02] -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_id_format() {
        let line = "08/Apr/2016:09:58:47 +0200 2 -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            response_time: Duration::milliseconds(10),
            original_log_line: line.clone(),
            http_error: None,
        };

        let result = Response::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line_inconsistent_space() {
        let line = "06/Apr/2016:14:54:16 +0200 [200] <- 200 text/html; charset=utf-8 250ms"
            .to_string();

        let expected = Response {
            id: 200,
            response_time: Duration::milliseconds(250),
            original_log_line: line.clone(),
            http_error: None,
        };

        let result = Response::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line_bad_id_format() {
        let line = "08/Apr/2016:09:58:48 +0200 2 <- 200 text/html 10ms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_id_format_no_number() {
        let line = "08/Apr/2016:09:58:48 +0200 [XXX] <- 200 text/html 10ms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_response_time_too_short() {
        let line = "08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html X".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_response_time_not_a_number() {
        let line = "08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html XXXms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_client_error() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 400 text/html 10ms".to_string();

        let result = Response::new_from_log_line(&line).unwrap().http_error;
        let expected = Some(HttpError::ClientError4xx);

        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_server_error() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 500 text/html 10ms".to_string();

        let result = Response::new_from_log_line(&line).unwrap().http_error;
        let expected = Some(HttpError::ServerError5xx);

        assert_eq!(result, expected);
    }

    #[test]
    fn test_log_event_type() {
        let request_line =
            "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();
        let response_line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let _ = LogEvent::Request(Request::new_from_log_line(&request_line).unwrap());
        let _ = LogEvent::Response(Response::new_from_log_line(&response_line).unwrap());
    }
}
<commit_msg>More robust Response line parsing (avoid panic)<commit_after>use chrono::*;

#[derive(Eq, PartialEq, Clone, Debug)]
pub enum HttpError {
    ClientError4xx,
    ServerError5xx,
}

#[derive(PartialEq,Debug)]
pub enum LogEvent {
    Request(Request),
    Response(Response),
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Request {
    pub id: i32,
    pub time: DateTime<FixedOffset>,
    pub original_log_line: String,
}

impl Request {
    pub fn new_from_log_line(log_line: &str) -> Result<Request, &'static str> {
        let parts: Vec<&str> = log_line.split(' ').collect();

        let id = match parts.get(2) {
            Some(id) => id,
            None => return Err("Uncomprehensible request logline"),
        };

        // Shortest valid id format is "[1]"
        if id.len() < 3 {
            return Err("Uncomprehensible request logline");
        }

        let id_parsed: i32 =
            match id.chars().skip(1).take(id.len() - 2).collect::<String>().parse() {
                Ok(id) => id,
                Err(_) => return Err("Uncomprehensible request logline"),
            };

        let date = &format!("{} {}", parts[0], parts[1]);

        let date_parsed = match DateTime::parse_from_str(date, "%d/%b/%Y:%H:%M:%S %z") {
            Ok(date_time) => date_time,
            Err(_) => return Err("Uncomprehensible request logline"),
        };

        Ok(Request {
            id: id_parsed,
            time: date_parsed,
            original_log_line: log_line.to_string(),
        })
    }
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Response {
    pub id: i32,
    pub response_time: Duration,
    pub original_log_line: String,
    pub http_error: Option<HttpError>,
}

impl Response {
    pub fn new_from_log_line(log_line: &str) -> Result<Response, &'static str> {
        let parts: Vec<&str> = log_line.split(' ').collect();

        let id = match parts.get(2) {
            Some(id) => id,
            None => return Err("Uncomprehensible request logline"),
        };

        // Shortest valid id format is "[1]"
        if id.len() < 3 {
            return Err("Uncomprehensible response logline");
        }

        let id_numeric: i32 =
            match id.chars().skip(1).take(id.len() - 2).collect::<String>().parse() {
                Ok(number) => number,
                Err(_) => return Err("Uncomprehensible response logline"),
            };

        let response_time = parts[parts.len() - 1];
        if response_time.len() < 3 {
            return Err("Uncomprehensible response logline");
        }

        let response_time_duration =
            match response_time.chars().take(response_time.len() - 2).collect::<String>().parse() {
                Ok(number) => Duration::milliseconds(number),
                Err(_) => return Err("Uncomprehensible response logline"),
            };

        let http_error = match parts.get(4) {
            Some(part) => {
                match part.chars().nth(0) {
                    Some('4') => Some(HttpError::ClientError4xx),
                    Some('5') => Some(HttpError::ServerError5xx),
                    _ => None,
                }
            }
            None => return Err("Uncomprehensible response logline"),
        };

        Ok(Response {
            id: id_numeric,
            response_time: response_time_duration,
            http_error: http_error,
            original_log_line: log_line.to_string(),
        })
    }
}

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

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected = Request {
            id: 2,
            time: DateTime::parse_from_str("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S %z")
                .unwrap(),
            original_log_line: line.clone(),
        };

        let result = Request::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_request_line_bad_format() {
        let line = "08/A16:09:58:47 justsomegarbage".to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_format_but_enough_parts() {
        let line = "just some garbage with more parts at the end".to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_date_format() {
        let line = "99/XYZ/9999:09:99:99 +9900 [02] -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_request_line_bad_id_format() {
        let line = "08/Apr/2016:09:58:47 +0200 2 -> GET /content/some/other.html HTTP/1.1"
            .to_string();

        let expected: Result<Request, &'static str> = Err("Uncomprehensible request logline");
        let result: Result<Request, &'static str> = Request::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            response_time: Duration::milliseconds(10),
            original_log_line: line.clone(),
            http_error: None,
        };

        let result = Response::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line_inconsistent_space() {
        let line = "06/Apr/2016:14:54:16 +0200 [200] <- 200 text/html; charset=utf-8 250ms"
            .to_string();

        let expected = Response {
            id: 200,
            response_time: Duration::milliseconds(250),
            original_log_line: line.clone(),
            http_error: None,
        };

        let result = Response::new_from_log_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line_bad_id_format() {
        let line = "08/Apr/2016:09:58:48 +0200 2 <- 200 text/html 10ms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_id_format_no_number() {
        let line = "08/Apr/2016:09:58:48 +0200 [XXX] <- 200 text/html 10ms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_response_time_too_short() {
        let line = "08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html X".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_bad_response_time_not_a_number() {
        let line = "08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html XXXms".to_string();

        let expected: Result<Response, &'static str> = Err("Uncomprehensible response logline");
        let result: Result<Response, &'static str> = Response::new_from_log_line(&line);

        assert_eq!(result.is_err(), true);
        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_client_error() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 400 text/html 10ms".to_string();

        let result = Response::new_from_log_line(&line).unwrap().http_error;
        let expected = Some(HttpError::ClientError4xx);

        assert_eq!(result, expected);
    }

    #[test]
    fn test_parse_response_line_server_error() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 500 text/html 10ms".to_string();

        let result = Response::new_from_log_line(&line).unwrap().http_error;
        let expected = Some(HttpError::ServerError5xx);

        assert_eq!(result, expected);
    }

    #[test]
    fn test_log_event_type() {
        let request_line =
            "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();
        let response_line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let _ = LogEvent::Request(Request::new_from_log_line(&request_line).unwrap());
        let _ = LogEvent::Response(Response::new_from_log_line(&response_line).unwrap());
    }

    #[test]
    fn test_no_panic_at_umlaut_char_boundary() {
        let line = "AAÜ".to_string();
        let _result: Result<Response, &'static str> = Response::new_from_log_line(&line);
    }

    #[test]
    fn test_no_panic_at_missing_http_result() {
        let line = "54/d(Ict>S-y ISt0<-y> 2222222I".to_string();
        let _result: Result<Response, &'static str> = Response::new_from_log_line(&line);
    }

    #[test]
    fn test_no_panic_at_response_time_char_boundary() {
        let line = "54/dI-(>cSty ISt0ctypeIcE*2<\n-y> 2222 IS<-y> 45402)9я/".to_string();
        let _result: Result<Response, &'static str> = Response::new_from_log_line(&line);
    }
}
<|endoftext|>


<commit_before>use futures::future;
use futures::Future;
use hyper::Uri;
use mktemp::Temp;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http_client;
use papers::DocumentSpec;
use error::Error;

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since mktemp::Temp implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new("template.tex"));
        Ok(Workspace {
            document_spec,
            handle: remote.handle().unwrap(),
            template_path,
            dir,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            template_path: _,
            dir: _,
        } = self;

        let DocumentSpec {
            assets_urls: _,
            callback_url: _,
            template_url,
            variables,
        } = document_spec;

        // Download the template and populate it
        let work = http_client::download_file(&handle.clone(), template_url.0)
            .and_then(|bytes| {
                future::result(::std::string::String::from_utf8(bytes))
                    .map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=Vec<u8>, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
        } = self;

        let DocumentSpec {
            assets_urls,
            callback_url: _,
            template_url,
            variables,
        } = document_spec;

        // First download the template and populate it
        let work = http_client::download_file(&handle.clone(), template_url.0)
                .and_then(|bytes| {
                    future::result(::std::string::String::from_utf8(bytes))
                        .map_err(Error::from)
                        .map(|template_string| (handle, template_string))
                }).and_then(move |(handle, template_string)| {
                    Tera::one_off(&template_string, &variables, false)
                        .map_err(Error::from)
                        .map(|latex_string| (handle, latex_string))
                }).and_then(|(handle, latex_string)| {
                    let mut file = ::std::fs::File::open(template_path.clone()).unwrap();
                    file.write_all(latex_string.as_bytes()).unwrap();
                    future::ok((handle, template_path))
                })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(handle, template_path)| {
                    let named = assets_urls.into_iter().filter_map(|url| {
                        let inner = url.0;
                        extract_file_name_from_uri(&inner).map(|file_name| (file_name, inner))
                    });

                    let inner_handle = handle.clone();

                    let download_named = move |(name, url)| {
                        let mut path = dir.to_path_buf();
                        path.push(name);

                        http_client::download_file(&inner_handle, url)
                            .map(move |bytes| ::std::fs::File::open(&path).unwrap().write_all(&bytes))
                            .map_err(Error::from)
                    };
                    future::join_all(named.map(download_named))
                        .map(|result| (handle, template_path, result))
                })

        // Then run latex
                .and_then(|(handle, template_path, _)| {
                    Command::new("pdflatex")
                        .arg(template_path.clone().to_str().unwrap())
                        .status_async(&handle.clone())
                        .map_err(Error::from)
                }).and_then(|exit_status| {
                    if exit_status.success() {
                        future::ok(())
                    } else {
                        future::err(Error::LatexFailed)
                    }
                }).map(|_| Vec::new());

        Box::new(work)
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<commit_msg>:sparkles: Construct the path to generated pdf<commit_after>use futures::future;
use futures::Future;
use hyper::Uri;
use mktemp::Temp;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http_client;
use papers::DocumentSpec;
use error::Error;

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since mktemp::Temp implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new("out.tex"));
        Ok(Workspace {
            document_spec,
            handle: remote.handle().unwrap(),
            template_path,
            dir,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            template_path: _,
            dir: _,
        } = self;

        let DocumentSpec {
            assets_urls: _,
            callback_url: _,
            template_url,
            variables,
        } = document_spec;

        // Download the template and populate it
        let work = http_client::download_file(&handle.clone(), template_url.0)
            .and_then(|bytes| {
                future::result(::std::string::String::from_utf8(bytes))
                    .map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=Vec<u8>, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
        } = self;

        let DocumentSpec {
            assets_urls,
            callback_url: _,
            template_url,
            variables,
        } = document_spec;

        let out_tex_path = dir.to_path_buf();
        let out_pdf_path = dir.to_path_buf();

        // First download the template and populate it
        let work = http_client::download_file(&handle.clone(), template_url.0)
                .and_then(|bytes| {
                    future::result(::std::string::String::from_utf8(bytes))
                        .map_err(Error::from)
                        .map(|template_string| (handle, template_string))
                }).and_then(move |(handle, template_string)| {
                    Tera::one_off(&template_string, &variables, false)
                        .map_err(Error::from)
                        .map(|latex_string| (handle, latex_string))
                }).and_then(|(handle, latex_string)| {
                    let mut file = ::std::fs::File::open(template_path.clone()).unwrap();
                    file.write_all(latex_string.as_bytes()).unwrap();
                    future::ok((handle, template_path))
                })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(handle, template_path)| {
                    let named = assets_urls.into_iter().filter_map(|url| {
                        let inner = url.0;
                        extract_file_name_from_uri(&inner).map(|file_name| (file_name, inner))
                    });

                    let inner_handle = handle.clone();

                    let download_named = move |(name, url)| {
                        let mut path = out_tex_path.clone();
                        path.push(name);

                        http_client::download_file(&inner_handle, url)
                            .map(move |bytes| ::std::fs::File::open(&path).unwrap().write_all(&bytes))
                            .map_err(Error::from)
                    };
                    future::join_all(named.map(download_named))
                        .map(|result| (handle, template_path, result))
                })

        // Then run latex
                .and_then(|(handle, template_path, _)| {
                    Command::new("pdflatex")
                        .arg(template_path.clone().to_str().unwrap())
                        .status_async(&handle.clone())
                        .map_err(Error::from)
                }).and_then(|exit_status| {
                    if exit_status.success() {
                        future::ok(())
                    } else {
                        future::err(Error::LatexFailed)
                    }
                })

        // Then construct the path to the generated PDF
                .and_then(|_| {
                    let mut path = out_pdf_path;
                    path.push(Path::new("out.pdf"));
                    future::ok(())
                }).map(|_| Vec::new());

        Box::new(work)
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<|endoftext|>