<commit_before>//! RethinkDB protocol implementation in Rust

extern crate protobuf;

mod proto;

/// *Arrays* are lists of zero or more elements.
///
/// ```json
/// [1, 2, 3]
/// []
/// [{user: 'Bob', posts: 23}, {user: 'Jason', posts: 10}]
/// ```
/// Anything valid in a JSON array is valid in RethinkDB.
/// The elements may be any of the basic values, objects,
/// or other arrays. Arrays in RethinkDB are loaded fully
/// into memory before they’re returned to the user, so
/// they’re inefficient at large sizes. RethinkDB defaults
/// to supporting arrays of up to 100,000 elements; this
/// may be set to a different value at runtime for reading
/// by using the `array_limit` option to run.
pub struct Array(proto::Term);

/// *Booleans* are `true` and `false`
pub struct Bool(proto::Term);

/// *Databases* are RethinkDB databases.
///
/// This is the return type of `db`.
pub struct Db(proto::Term);

/// *Functions* can be passed as parameters to certain ReQL
/// commands.
pub struct Function(proto::Term);

/// *Grouped data* is created by the `group` command.
///
/// The command partitions a stream into multiple groups
/// based on specified fields or functions. ReQL commands
/// called on GroupedData operate on each group
/// individually. For more details, read the group
/// documentation. Depending on the input to group,
/// grouped data may have the type of GroupedStream.
pub struct GroupedData(proto::Term);
pub struct GroupedStream(proto::Term);

/// *Minval* and *maxval* are used with some commands such
/// as `between` to specify absolute lower and upper bounds
/// (e.g., `between(r.minval, 1000)` would return all
/// documents in a table whose primary key is less than
/// 1000).
pub struct MaxVal(proto::Term);
pub struct MinVal(proto::Term);

/// *Null* is a value distinct from the number zero, an
/// empty set, or a zero-length string.
///
/// Natively this is
/// `None`. It is often used to explicitly denote the
/// absence of any other value. The root node of a tree
/// structure might have a parent of `null`, or a required
/// but as yet non-initialized key might be given a value
/// of `null`.
pub struct Null(proto::Term);

/// *Numbers* are any real number: `5`, `3.14159`, `-42`.
///
/// RethinkDB uses double precision (64-bit) floating point
/// numbers internally. Neither infinity nor NaN are allowed.
pub struct Number(proto::Term);

/// *Objects* are JSON data objects, standard key-value pairs.
///
/// ```json
/// {
///     username: 'bob',
///     posts: 23,
///     favorites: {
///         color: 'blue',
///         food: 'tacos'
///     },
///     friends: ['agatha', 'jason']
/// }
/// ```
///
/// Any valid JSON object is a valid RethinkDB object, so
/// values can be any of the basic values, arrays, or
/// other objects. Documents in a RethinkDB database are
/// objects. Like JSON, key names must be strings, not
/// integers.
pub struct Object(proto::Term);

/// *Binary* objects are similar to BLOBs in SQL databases:
/// files, images and other binary data.
///
/// See Storing binary objects for details.
pub struct Binary(proto::Term);

/// *Geometry* data types for geospatial support, including
/// points, lines, and polygons.
pub struct Geometry(proto::Term);

/// *Times* are RethinkDB’s native date/time type, stored
/// with millisecond precision.
///
/// You can use native date/time types in supported
/// languages, as the conversion will be done by the driver.
/// See Dates and times in RethinkDB for details.
pub struct Time(proto::Term);

/// *Selections* represent subsets of tables, for example,
/// the return values of `filter` or `get`.
///
/// There are three kinds of selections: *Selection<Object>*,
/// *Selection<Array>* and *Selection<Stream>*. The
/// difference between selections and their non-selection
/// counterparts is that selections are writable—their
/// return values can be passed as inputs to ReQL commands
/// that modify the database. For instance, the get command
/// will return a Selection<Object> that could then be
/// passed to an update or delete command.
///
/// (Note: *singleSelection* is an older term for
/// Selection<Object>; they mean the same thing.)
/// Some commands (`order_by` and `between`) return a data
/// type similar to a selection called a *table_slice*.
/// In most cases a table_slice behaves identically to a
/// selection, but `between` can only be called on a table
/// or a table_slice, not any other kind of selection.
pub struct Selection<T>(T);

/// *Streams* are lists like arrays, but they’re loaded in
/// a lazy fashion.
///
/// Operations that return streams return a `cursor`.
/// A cursor is a pointer into the result set. Instead of
/// reading the results all at once like an array, you
/// loop over the results, retrieving the next member of the
/// set with each iteration. This makes it possible to
/// efficiently work with large result sets.
///
/// (See “Working with Streams,” below, for some tips.)
/// Streams are read-only; you can’t pass one as an input
/// to an ReQL command meant to modify its input like
/// `update` or `delete`.
pub struct Stream(proto::Term);

/// *Strings* are any valid UTF-8 string: `"superhero"`,
/// `"ünnëcëssärÿ ümläüts"`. Strings may include the null
/// code point (U+0000).
pub struct String(proto::Term);

/// *Tables* are RethinkDB database tables.
///
/// They behave like selections—they’re writable, as you can
/// insert and delete documents in them. ReQL methods that
/// use an index, like `get_all`, are only available on
/// tables.
pub struct Table(proto::Term);
pub struct TableSlice(proto::Term);
<commit_msg>Prototype strong typing<commit_after>//! RethinkDB protocol implementation in Rust

extern crate protobuf;

mod proto;

pub use proto::{
    VersionDummy_Version as Version,
    Term_TermType as TermType,
    Query_QueryType as QueryType,
    Response_ResponseType as ResponseType,
    Response_ErrorType as ErrorType,
};

use std::string::String as StdString;

use proto::{
    Term, Datum,
    Datum_DatumType as DatumType,
};

use protobuf::repeated::RepeatedField;

macro_rules! implement {
    (Selection<$dt:ident>) => {
        impl DataType for Selection<$dt> {}

        impl Selection<$dt> {
            pub fn new() -> Selection<$dt> {
                Selection::<$dt>($dt(Term::new()))
            }
        }

        impl From<Selection<$dt>> for Term {
            fn from(t: Selection<$dt>) -> Term {
                From::from(t.0)
            }
        }

        impl From<Term> for Selection<$dt> {
            fn from(t: Term) -> Selection<$dt> {
                Selection::<$dt>($dt(t))
            }
        }
    };

    ($dt:ident) => {
        #[derive(Debug, Clone)]
        pub struct $dt(Term);

        impl DataType for $dt {}

        impl $dt {
            pub fn new() -> $dt {
                $dt(Term::new())
            }
        }

        impl From<$dt> for Term {
            fn from(t: $dt) -> Term {
                t.0
            }
        }

        impl From<Term> for $dt {
            fn from(t: Term) -> $dt {
                $dt(t)
            }
        }
    };
}

/// *Arrays* are lists of zero or more elements.
///
/// ```json
/// [1, 2, 3]
/// []
/// [{user: 'Bob', posts: 23}, {user: 'Jason', posts: 10}]
/// ```
/// Anything valid in a JSON array is valid in RethinkDB.
/// The elements may be any of the basic values, objects,
/// or other arrays. Arrays in RethinkDB are loaded fully
/// into memory before they’re returned to the user, so
/// they’re inefficient at large sizes. RethinkDB defaults
/// to supporting arrays of up to 100,000 elements; this
/// may be set to a different value at runtime for reading
/// by using the `array_limit` option to run.
implement! { Array }

/// *Booleans* are `true` and `false`
implement! { Bool }

/// *Databases* are RethinkDB databases.
///
/// This is the return type of `db`.
implement! { Db }

/// *Functions* can be passed as parameters to certain ReQL
/// commands.
implement! { Function }

/// *Grouped data* is created by the `group` command.
///
/// The command partitions a stream into multiple groups
/// based on specified fields or functions. ReQL commands
/// called on GroupedData operate on each group
/// individually. For more details, read the group
/// documentation. Depending on the input to group,
/// grouped data may have the type of GroupedStream.
implement! { GroupedData }
implement! { GroupedStream }

/// *Minval* and *maxval* are used with some commands such
/// as `between` to specify absolute lower and upper bounds
/// (e.g., `between(r.minval, 1000)` would return all
/// documents in a table whose primary key is less than
/// 1000).
implement! { MaxVal }
implement! { MinVal }

/// *Null* is a value distinct from the number zero, an
/// empty set, or a zero-length string.
///
/// Natively this is
/// `None`. It is often used to explicitly denote the
/// absence of any other value. The root node of a tree
/// structure might have a parent of `null`, or a required
/// but as yet non-initialized key might be given a value
/// of `null`.
implement! { Null }

/// *Numbers* are any real number: `5`, `3.14159`, `-42`.
///
/// RethinkDB uses double precision (64-bit) floating point
/// numbers internally. Neither infinity nor NaN are allowed.
implement! { Number }

/// *Objects* are JSON data objects, standard key-value pairs.
///
/// ```json
/// {
///     username: 'bob',
///     posts: 23,
///     favorites: {
///         color: 'blue',
///         food: 'tacos'
///     },
///     friends: ['agatha', 'jason']
/// }
/// ```
///
/// Any valid JSON object is a valid RethinkDB object, so
/// values can be any of the basic values, arrays, or
/// other objects. Documents in a RethinkDB database are
/// objects. Like JSON, key names must be strings, not
/// integers.
implement! { Object }

/// *Binary* objects are similar to BLOBs in SQL databases:
/// files, images and other binary data.
///
/// See Storing binary objects for details.
implement! { Binary }

/// *Geometry* data types for geospatial support, including
/// points, lines, and polygons.
implement! { Geometry }

/// *Times* are RethinkDB’s native date/time type, stored
/// with millisecond precision.
///
/// You can use native date/time types in supported
/// languages, as the conversion will be done by the driver.
/// See Dates and times in RethinkDB for details.
implement! { Time }

/// *Streams* are lists like arrays, but they’re loaded in
/// a lazy fashion.
///
/// Operations that return streams return a `cursor`.
/// A cursor is a pointer into the result set. Instead of
/// reading the results all at once like an array, you
/// loop over the results, retrieving the next member of the
/// set with each iteration. This makes it possible to
/// efficiently work with large result sets.
///
/// (See “Working with Streams,” below, for some tips.)
/// Streams are read-only; you can’t pass one as an input
/// to an ReQL command meant to modify its input like
/// `update` or `delete`.
implement! { Stream }

/// *Strings* are any valid UTF-8 string: `"superhero"`,
/// `"ünnëcëssärÿ ümläüts"`. Strings may include the null
/// code point (U+0000).
implement! { String }

/// *Tables* are RethinkDB database tables.
///
/// They behave like selections—they’re writable, as you can
/// insert and delete documents in them. ReQL methods that
/// use an index, like `get_all`, are only available on
/// tables.
implement! { Table }
implement! { TableSlice }

/// *Selections* represent subsets of tables, for example,
/// the return values of `filter` or `get`.
///
/// There are three kinds of selections: *Selection<Object>*,
/// *Selection<Array>* and *Selection<Stream>*. The
/// difference between selections and their non-selection
/// counterparts is that selections are writable—their
/// return values can be passed as inputs to ReQL commands
/// that modify the database. For instance, the get command
/// will return a Selection<Object> that could then be
/// passed to an update or delete command.
///
/// (Note: *singleSelection* is an older term for
/// Selection<Object>; they mean the same thing.)
/// Some commands (`order_by` and `between`) return a data
/// type similar to a selection called a *table_slice*.
/// In most cases a table_slice behaves identically to a
/// selection, but `between` can only be called on a table
/// or a table_slice, not any other kind of selection.
#[derive(Debug, Clone)]
pub struct Selection<T>(T);
implement! { Selection<Object> }
implement! { Selection<Array> }
implement! { Selection<Stream> }

pub trait DataType : Sized + From<Term> + Into<Term> {}

#[derive(Debug, Clone)]
pub struct Command(Term);

impl Command {
    pub fn new<T>(cmd_type: TermType, prev_cmd: Option<T>) -> Command
        where T: DataType
        {
            let mut term = Term::new();
            term.set_field_type(cmd_type);
            if let Some(cmd) = prev_cmd {
                let args = RepeatedField::from_vec(vec![cmd.into()]);
                term.set_args(args);
            }
            Command(term)
        }

    pub fn set_args<T>(&mut self, args: T)
        where T: DataType
        {
            self.0.mut_args().push(args.into());
        }

    pub fn set_opts(&mut self, opts: Object)
        {
            let mut term: Term = opts.into();
            let opts = term.take_optargs().into_vec();
            for opt in opts.into_iter() {
                self.0.mut_optargs().push(opt);
            }
        }

    pub fn into<T>(self) -> T
        where T: From<Term>
        {
            From::from(self.0)
        }
}

impl<T> From<T> for String
    where T: Into<StdString>
{
    fn from(t: T) -> String {
        let mut datum = Datum::new();
        datum.set_field_type(DatumType::R_STR);
        datum.set_r_str(t.into());
        let mut output = String::new();
        output.0.set_field_type(TermType::DATUM);
        output.0.set_datum(datum);
        output
    }
}

<|endoftext|>


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

/*!

The `Ord` and `Eq` comparison traits

This module contains the definition of both `Ord` and `Eq` which define
the common interfaces for doing comparison. Both are language items
that the compiler uses to implement the comparison operators. Rust code
may implement `Ord` to overload the `<`, `<=`, `>`, and `>=` operators,
and `Eq` to overload the `==` and `!=` operators.

*/

/**
* Trait for values that can be compared for equality
* and inequality.
*
* Eventually this may be simplified to only require
* an `eq` method, with the other generated from
* a default implementation. However it should
* remain possible to implement `ne` separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="eq"]
pub trait Eq {
    pure fn eq(&self, other: &Self) -> bool;
    pure fn ne(&self, other: &Self) -> bool;
}

#[deriving_eq]
pub enum Ordering { Less, Equal, Greater }

/// Trait for types that form a total order
pub trait TotalOrd {
    pure fn cmp(&self, other: &Self) -> Ordering;
}

pure fn icmp<T: Ord>(a: &T, b: &T) -> Ordering {
    if *a < *b { Less }
    else if *a > *b { Greater }
    else { Equal }
}

impl TotalOrd for u8 {
    #[inline(always)]
    pure fn cmp(&self, other: &u8) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u16 {
    #[inline(always)]
    pure fn cmp(&self, other: &u16) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u32 {
    #[inline(always)]
    pure fn cmp(&self, other: &u32) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u64 {
    #[inline(always)]
    pure fn cmp(&self, other: &u64) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i8 {
    #[inline(always)]
    pure fn cmp(&self, other: &i8) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i16 {
    #[inline(always)]
    pure fn cmp(&self, other: &i16) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i32 {
    #[inline(always)]
    pure fn cmp(&self, other: &i32) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i64 {
    #[inline(always)]
    pure fn cmp(&self, other: &i64) -> Ordering { icmp(self, other) }
}

impl TotalOrd for int {
    #[inline(always)]
    pure fn cmp(&self, other: &int) -> Ordering { icmp(self, other) }
}

impl TotalOrd for uint {
    #[inline(always)]
    pure fn cmp(&self, other: &uint) -> Ordering { icmp(self, other) }
}

/**
* Trait for values that can be compared for a sort-order.
*
* Eventually this may be simplified to only require
* an `le` method, with the others generated from
* default implementations. However it should remain
* possible to implement the others separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="ord"]
pub trait Ord {
    pure fn lt(&self, other: &Self) -> bool;
    pure fn le(&self, other: &Self) -> bool;
    pure fn ge(&self, other: &Self) -> bool;
    pure fn gt(&self, other: &Self) -> bool;
}

#[inline(always)]
pub pure fn lt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).lt(v2)
}

#[inline(always)]
pub pure fn le<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).le(v2)
}

#[inline(always)]
pub pure fn eq<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).eq(v2)
}

#[inline(always)]
pub pure fn ne<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).ne(v2)
}

#[inline(always)]
pub pure fn ge<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).ge(v2)
}

#[inline(always)]
pub pure fn gt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).gt(v2)
}

#[inline(always)]
pub pure fn min<T:Ord>(v1: T, v2: T) -> T {
    if v1 < v2 { v1 } else { v2 }
}

#[inline(always)]
pub pure fn max<T:Ord>(v1: T, v2: T) -> T {
    if v1 > v2 { v1 } else { v2 }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_int() {
        assert 5.cmp(&10) == Less;
        assert 10.cmp(&5) == Greater;
        assert 5.cmp(&5) == Equal;
        assert (-5).cmp(&12) == Less;
        assert 12.cmp(-5) == Greater;
    }
}
<commit_msg>auto merge of #5206 : thestinger/rust/cmp, r=brson<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.

/*!

The `Ord` and `Eq` comparison traits

This module contains the definition of both `Ord` and `Eq` which define
the common interfaces for doing comparison. Both are language items
that the compiler uses to implement the comparison operators. Rust code
may implement `Ord` to overload the `<`, `<=`, `>`, and `>=` operators,
and `Eq` to overload the `==` and `!=` operators.

*/

/**
* Trait for values that can be compared for equality
* and inequality.
*
* Eventually this may be simplified to only require
* an `eq` method, with the other generated from
* a default implementation. However it should
* remain possible to implement `ne` separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="eq"]
pub trait Eq {
    pure fn eq(&self, other: &Self) -> bool;
    pure fn ne(&self, other: &Self) -> bool;
}

#[deriving_eq]
pub enum Ordering { Less, Equal, Greater }

/// Trait for types that form a total order
pub trait TotalOrd {
    pure fn cmp(&self, other: &Self) -> Ordering;
}

#[inline(always)]
pure fn icmp<T: Ord>(a: &T, b: &T) -> Ordering {
    if *a < *b { Less }
    else if *a > *b { Greater }
    else { Equal }
}

impl TotalOrd for u8 {
    #[inline(always)]
    pure fn cmp(&self, other: &u8) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u16 {
    #[inline(always)]
    pure fn cmp(&self, other: &u16) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u32 {
    #[inline(always)]
    pure fn cmp(&self, other: &u32) -> Ordering { icmp(self, other) }
}

impl TotalOrd for u64 {
    #[inline(always)]
    pure fn cmp(&self, other: &u64) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i8 {
    #[inline(always)]
    pure fn cmp(&self, other: &i8) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i16 {
    #[inline(always)]
    pure fn cmp(&self, other: &i16) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i32 {
    #[inline(always)]
    pure fn cmp(&self, other: &i32) -> Ordering { icmp(self, other) }
}

impl TotalOrd for i64 {
    #[inline(always)]
    pure fn cmp(&self, other: &i64) -> Ordering { icmp(self, other) }
}

impl TotalOrd for int {
    #[inline(always)]
    pure fn cmp(&self, other: &int) -> Ordering { icmp(self, other) }
}

impl TotalOrd for uint {
    #[inline(always)]
    pure fn cmp(&self, other: &uint) -> Ordering { icmp(self, other) }
}

/**
* Trait for values that can be compared for a sort-order.
*
* Eventually this may be simplified to only require
* an `le` method, with the others generated from
* default implementations. However it should remain
* possible to implement the others separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="ord"]
pub trait Ord {
    pure fn lt(&self, other: &Self) -> bool;
    pure fn le(&self, other: &Self) -> bool;
    pure fn ge(&self, other: &Self) -> bool;
    pure fn gt(&self, other: &Self) -> bool;
}

#[inline(always)]
pub pure fn lt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).lt(v2)
}

#[inline(always)]
pub pure fn le<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).le(v2)
}

#[inline(always)]
pub pure fn eq<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).eq(v2)
}

#[inline(always)]
pub pure fn ne<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).ne(v2)
}

#[inline(always)]
pub pure fn ge<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).ge(v2)
}

#[inline(always)]
pub pure fn gt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).gt(v2)
}

#[inline(always)]
pub pure fn min<T:Ord>(v1: T, v2: T) -> T {
    if v1 < v2 { v1 } else { v2 }
}

#[inline(always)]
pub pure fn max<T:Ord>(v1: T, v2: T) -> T {
    if v1 > v2 { v1 } else { v2 }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_int() {
        assert 5.cmp(&10) == Less;
        assert 10.cmp(&5) == Greater;
        assert 5.cmp(&5) == Equal;
        assert (-5).cmp(&12) == Less;
        assert 12.cmp(-5) == Greater;
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;

use proc_macro::TokenStream;

mod representation;
mod elm;
mod derive;

/// Marker trait that allows to tie in the procedural macro tool chain.
trait Elm {}

#[proc_macro_derive(Elm)]
pub fn generate_elm(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    let ast = syn::parse_derive_input(&source).unwrap();

    derive::generate_elm(&ast);

    empty_token_stream()
}

fn empty_token_stream() -> TokenStream {
    "".parse().unwrap()
}
<commit_msg>Document libary<commit_after>//! Provides easy communication between [Elm](http://elm-lang.org/) and
//! [Rust](https://www.rust-lang.org/en-US/) by leveraging
//! [serde](https://serde.rs/).
//!
//! ## Usage
//! Notice that at the moment som of this is dreamcode.
//!
//! Lets say we have some models in Rust.
//!
//! ```rust
//! enum Message {
//!   FriendRequest(User),
//!   Message(User, String),
//! }
//!
//! struct User {
//!   name: String
//! }
//! ```
//!
//! We want to generated the corresponding models in Elm. For this we need a
//! dependency on the `serde_elm` crate. Include the following line in your
//! `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//! serde_elm = "0.1.0"
//! ```
//!
//! Next we need to make our project aware of the crate and the functionality it
//! exposes. Import it in either `main.rs` or `lib.rs`. Don't forget to annotate the
//! import with the `macro_use` annotation.
//!
//! ```rust
//! #[macro_use]
//! extern crate serde_elm;
//! ```
//!
//! Now it is time to derive the corresponding models in Elm. Annotate the models
//! with `derive(Elm)`.
//!
//! ```rust
//! #[derive(Elm)]
//! enum Message {
//!   FriendRequest(User),
//!   Message(User, String),
//! }
//!
//! #[derive(Elm)]
//! struct User {
//!   name: String
//! }
//! ```
//!
//! Now every time your models change and you compile your code the corresponding
//! Elm models will be generated. You can find them in the `generated` directory in
//! your projects root. The are called after the corresponding Rust definitions.
//! I.e. `Message.elm` and `User.elm` in this case.
//!
//! ```elm
//! module Message exposing (..)
//!
//! type Message =
//!     FriendRequest User
//!   | Message User String
//! ```
//!
//! ```elm
//! module User exposing (..)
//!
//! type alias User =
//!   {
//!     name: String
//!   }
//! ```
extern crate proc_macro;
extern crate syn;

use proc_macro::TokenStream;

mod representation;
mod elm;
mod derive;

/// Marker trait that allows to tie in the procedural macro tool chain.
trait Elm {}

/// Writes Elm model, serializers and deserializers to the `generated`
/// directory.
#[proc_macro_derive(Elm)]
pub fn generate_elm(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    let ast = syn::parse_derive_input(&source).unwrap();

    derive::generate_elm(&ast);

    empty_token_stream()
}

fn empty_token_stream() -> TokenStream {
    "".parse().unwrap()
}
<|endoftext|>


<commit_before>#![macro_use]

#[macro_use]
extern crate log;
extern crate libc;

mod util;

mod blocksplitter;
#[cfg(feature = "longest-match-cache")]
mod cache;
mod deflate;
mod gzip_container;
mod hash;
mod katajainen;
mod lz77;
mod squeeze;
mod tree;
mod zlib_container;

/// Options used throughout the program.
pub struct Options {
    /// Whether to print output
    pub verbose: bool,
    /// Whether to print more detailed output
    pub verbose_more: bool,
    /// Maximum amount of times to rerun forward and backward pass to optimize LZ77
    /// compression cost. Good values: 10, 15 for small files, 5 for files over
    /// several MB in size or it will be too slow.
    pub numiterations: i32,
    /// If true, splits the data in multiple deflate blocks with optimal choice
    /// for the block boundaries. Block splitting gives better compression. Default:
    /// true (1).
    pub blocksplitting: bool,
    /// If true, chooses the optimal block split points only after doing the iterative
    /// LZ77 compression. If false, chooses the block split points first, then does
    /// iterative LZ77 on each individual block. Depending on the file, either first
    /// or last gives the best compression. Default: false (0).
    pub blocksplittinglast: bool,
    /// Maximum amount of blocks to split into (0 for unlimited, but this can give
    /// extreme results that hurt compression on some files). Default value: 15.
    pub blocksplittingmax: i32,
}

impl Options {
    pub fn new() -> Options {
        Options {
            verbose: false,
            verbose_more: false,
            numiterations: 15,
            blocksplitting: true,
            blocksplittinglast: false,
            blocksplittingmax: 15,
        }
    }
}

/// Output format
#[derive(Clone, Copy)]
pub enum Format {
    GZIP,
    ZLIB,
    DEFLATE,
}

/**
 * Compresses according to the given output format and appends the result to the
 * output.
 *
 * options: global program options
 * output_type: the output format to use
 * out: pointer to the dynamic output array to which the result is appended. Must
 *   be freed after use
 * outsize: pointer to the dynamic output array size
 */
pub unsafe fn compress(options: *const Options, output_type: Format, input: *const u8, insize: usize, out: *mut *mut u8, outsize: *mut usize) {
    match output_type {
        Format::GZIP    => gzip_container::compress(options, input, insize, out, outsize),
        Format::ZLIB    => zlib_container::compress(options, input, insize, out, outsize),
        Format::DEFLATE => {
            let mut bp: u8 = 0;
            deflate::deflate(options, 2 /* Dynamic block */, true, input, insize, &mut bp, out, outsize)
        },
    }
}

#[cfg(test)]
mod test {
    extern crate flate2;

    use libc::{c_void, size_t};
    use libc::funcs::c95::string::memcmp;
    use std::io::Read;
    use std::ptr::null_mut;
    use std::slice;

    use self::flate2::read::{DeflateDecoder, GzDecoder, ZlibDecoder};

    use super::*;

    unsafe fn roundtrip(format: Format, bytes: &[u8]) {
        let options = Options { verbose: false, verbose_more: false, .. Options::new() };
        let mut compressed: *mut u8 = null_mut();
        let mut compressed_size: usize = 0;
        compress(&options, format, bytes.as_ptr(), bytes.len(), &mut compressed, &mut compressed_size);
        let decompressed = match format {
            Format::GZIP => {
                let mut d = GzDecoder::new(slice::from_raw_parts(compressed, compressed_size)).unwrap();
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
            Format::ZLIB => {
                let mut d = ZlibDecoder::new(slice::from_raw_parts(compressed, compressed_size));
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
            Format::DEFLATE => {
                let mut d = DeflateDecoder::new(slice::from_raw_parts(compressed, compressed_size));
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
        };
        assert!(memcmp(bytes.as_ptr() as *const c_void, decompressed.as_ptr() as *const c_void, bytes.len() as size_t) == 0);
    }

    #[test]
    fn roundtrips() {
        unsafe {
            for format in vec![Format::GZIP, Format::ZLIB, Format::DEFLATE] {
                roundtrip(format, b"1");
                roundtrip(format, b"foobar");
            }
        }
    }

    #[test]
    #[ignore]
    fn compress_zero_bytes() {
        unsafe {
            for format in vec![Format::GZIP, Format::ZLIB, Format::DEFLATE] {
                roundtrip(format, b"");
            }
        }
    }

    #[test]
    fn test1() {
        unsafe {
            let options = Options { verbose: false, verbose_more: false, .. Options::new() };
            let bytes = b"44098c409eb5a2f2";
            let mut compressed: *mut u8 = null_mut();
            let mut compressed_size: usize = 0;
            compress(&options, Format::DEFLATE, bytes.as_ptr(), bytes.len(), &mut compressed, &mut compressed_size);
            assert_eq!(slice::from_raw_parts(compressed, compressed_size), [0x33, 0x31, 0x31, 0xb0, 0xb4, 0x48, 0x06, 0x12, 0xa9, 0x49, 0xa6, 0x89, 0x46, 0x69, 0x46, 0x00]);
        }
    }
}
<commit_msg>test cleanup<commit_after>#![macro_use]

#[macro_use]
extern crate log;
extern crate libc;

mod util;

mod blocksplitter;
#[cfg(feature = "longest-match-cache")]
mod cache;
mod deflate;
mod gzip_container;
mod hash;
mod katajainen;
mod lz77;
mod squeeze;
mod tree;
mod zlib_container;

/// Options used throughout the program.
pub struct Options {
    /// Whether to print output
    pub verbose: bool,
    /// Whether to print more detailed output
    pub verbose_more: bool,
    /// Maximum amount of times to rerun forward and backward pass to optimize LZ77
    /// compression cost. Good values: 10, 15 for small files, 5 for files over
    /// several MB in size or it will be too slow.
    pub numiterations: i32,
    /// If true, splits the data in multiple deflate blocks with optimal choice
    /// for the block boundaries. Block splitting gives better compression. Default:
    /// true (1).
    pub blocksplitting: bool,
    /// If true, chooses the optimal block split points only after doing the iterative
    /// LZ77 compression. If false, chooses the block split points first, then does
    /// iterative LZ77 on each individual block. Depending on the file, either first
    /// or last gives the best compression. Default: false (0).
    pub blocksplittinglast: bool,
    /// Maximum amount of blocks to split into (0 for unlimited, but this can give
    /// extreme results that hurt compression on some files). Default value: 15.
    pub blocksplittingmax: i32,
}

impl Options {
    pub fn new() -> Options {
        Options {
            verbose: false,
            verbose_more: false,
            numiterations: 15,
            blocksplitting: true,
            blocksplittinglast: false,
            blocksplittingmax: 15,
        }
    }
}

/// Output format
#[derive(Clone, Copy)]
pub enum Format {
    GZIP,
    ZLIB,
    DEFLATE,
}

/**
 * Compresses according to the given output format and appends the result to the
 * output.
 *
 * options: global program options
 * output_type: the output format to use
 * out: pointer to the dynamic output array to which the result is appended. Must
 *   be freed after use
 * outsize: pointer to the dynamic output array size
 */
pub unsafe fn compress(options: *const Options, output_type: Format, input: *const u8, insize: usize, out: *mut *mut u8, outsize: *mut usize) {
    match output_type {
        Format::GZIP    => gzip_container::compress(options, input, insize, out, outsize),
        Format::ZLIB    => zlib_container::compress(options, input, insize, out, outsize),
        Format::DEFLATE => {
            let mut bp: u8 = 0;
            deflate::deflate(options, 2 /* Dynamic block */, true, input, insize, &mut bp, out, outsize)
        },
    }
}

#[cfg(test)]
mod test {
    extern crate flate2;

    use libc::{c_void, size_t};
    use libc::funcs::c95::string::memcmp;
    use std::io::Read;
    use std::ptr::null_mut;
    use std::slice;

    use self::flate2::read::{DeflateDecoder, GzDecoder, ZlibDecoder};

    use super::*;

    unsafe fn roundtrip(format: Format, bytes: &[u8]) {
        let options = Options { verbose: false, verbose_more: false, .. Options::new() };
        let mut compressed: *mut u8 = null_mut();
        let mut compressed_size: usize = 0;
        compress(&options, format, bytes.as_ptr(), bytes.len(), &mut compressed, &mut compressed_size);
        let decompressed = match format {
            Format::GZIP => {
                let mut d = GzDecoder::new(slice::from_raw_parts(compressed, compressed_size)).unwrap();
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
            Format::ZLIB => {
                let mut d = ZlibDecoder::new(slice::from_raw_parts(compressed, compressed_size));
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
            Format::DEFLATE => {
                let mut d = DeflateDecoder::new(slice::from_raw_parts(compressed, compressed_size));
                let mut s = Vec::new();
                d.read_to_end(&mut s).unwrap();
                s
            }
        };
        assert!(memcmp(bytes.as_ptr() as *const c_void, decompressed.as_ptr() as *const c_void, bytes.len() as size_t) == 0);
    }

    #[test]
    fn roundtrips() {
        unsafe {
            for format in vec![Format::GZIP, Format::ZLIB, Format::DEFLATE] {
                roundtrip(format, b"1");
                roundtrip(format, b"foobar");
            }
        }
    }

    #[test]
    #[ignore]
    fn compress_zero_bytes() {
        unsafe {
            for format in vec![Format::GZIP, Format::ZLIB, Format::DEFLATE] {
                roundtrip(format, b"");
            }
        }
    }

    fn expect_compressed(format: Format, data: &[u8], compressed: &[u8]) {
        unsafe {
            let options = Options { verbose: false, verbose_more: false, .. Options::new() };
            let mut result: *mut u8 = null_mut();
            let mut result_size: usize = 0;
            compress(&options, format, data.as_ptr(), data.len(), &mut result, &mut result_size);
            assert_eq!(slice::from_raw_parts(result, result_size), compressed);
        }
    }

    #[test]
    fn test_compression() {
        expect_compressed(Format::DEFLATE, b"44098c409eb5a2f2", &[0x33, 0x31, 0x31, 0xb0, 0xb4, 0x48, 0x06, 0x12, 0xa9, 0x49, 0xa6, 0x89, 0x46, 0x69, 0x46, 0x00]);
    }
}
<|endoftext|>


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

#![allow(missing_doc)]
#![experimental]

//! Contains struct definitions for the layout of compiler built-in types.
//!
//! They can be used as targets of transmutes in unsafe code for manipulating
//! the raw representations directly.
//!
//! Their definition should always match the ABI defined in `rustc::back::abi`.

use mem;

/// The representation of a Rust slice
pub struct Slice<T> {
    pub data: *const T,
    pub len: uint,
}

/// The representation of a Rust closure
pub struct Closure {
    pub code: *mut (),
    pub env: *mut (),
}

/// The representation of a Rust procedure (`proc()`)
pub struct Procedure {
    pub code: *mut (),
    pub env: *mut (),
}

/// The representation of a Rust trait object.
///
/// This struct does not have a `Repr` implementation
/// because there is no way to refer to all trait objects generically.
pub struct TraitObject {
    pub data: *mut (),
    pub vtable: *mut (),
}

/// This trait is meant to map equivalences between raw structs and their
/// corresponding rust values.
pub trait Repr<T> {
    /// This function "unwraps" a rust value (without consuming it) into its raw
    /// struct representation. This can be used to read/write different values
    /// for the struct. This is a safe method because by default it does not
    /// enable write-access to the fields of the return value in safe code.
    #[inline]
    fn repr(&self) -> T { unsafe { mem::transmute_copy(self) } }
}

impl<'a, T> Repr<Slice<T>> for &'a [T] {}
impl<'a> Repr<Slice<u8>> for &'a str {}

<commit_msg>make the core::raw struct representation defined<commit_after>// Copyright 2013 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.

#![allow(missing_doc)]
#![experimental]

//! Contains struct definitions for the layout of compiler built-in types.
//!
//! They can be used as targets of transmutes in unsafe code for manipulating
//! the raw representations directly.
//!
//! Their definition should always match the ABI defined in `rustc::back::abi`.

use mem;

/// The representation of a Rust slice
#[repr(C)]
pub struct Slice<T> {
    pub data: *const T,
    pub len: uint,
}

/// The representation of a Rust closure
#[repr(C)]
pub struct Closure {
    pub code: *mut (),
    pub env: *mut (),
}

/// The representation of a Rust procedure (`proc()`)
#[repr(C)]
pub struct Procedure {
    pub code: *mut (),
    pub env: *mut (),
}

/// The representation of a Rust trait object.
///
/// This struct does not have a `Repr` implementation
/// because there is no way to refer to all trait objects generically.
#[repr(C)]
pub struct TraitObject {
    pub data: *mut (),
    pub vtable: *mut (),
}

/// This trait is meant to map equivalences between raw structs and their
/// corresponding rust values.
pub trait Repr<T> {
    /// This function "unwraps" a rust value (without consuming it) into its raw
    /// struct representation. This can be used to read/write different values
    /// for the struct. This is a safe method because by default it does not
    /// enable write-access to the fields of the return value in safe code.
    #[inline]
    fn repr(&self) -> T { unsafe { mem::transmute_copy(self) } }
}

impl<'a, T> Repr<Slice<T>> for &'a [T] {}
impl<'a> Repr<Slice<u8>> for &'a str {}
<|endoftext|>


<commit_before>
//! **mprovision** is a tool that helps iOS developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
#[cfg(test)]
extern crate tempdir;
extern crate plist;
extern crate chrono;

use std::fs::{self, DirEntry, File};
use std::path::{Path, PathBuf};
use std::env;
use std::io::{self, Read};
use plist::PlistEvent::*;

pub use error::Error;
pub use profile::Profile;

mod error;
mod profile;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn files<P>(path: P) -> Result<Box<Iterator<Item = DirEntry>>>
    where P: AsRef<Path>
{
    let entries = try!(fs::read_dir(&path));
    let filtered = entries.filter_map(|entry| entry.ok())
                          .filter_map(|entry| {
                              if let Some(ext) = entry.path().extension() {
                                  if ext == "mobileprovision" {
                                      return Some(entry);
                                  }
                              }
                              None
                          });
    Ok(Box::new(filtered))
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    env::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or(Error::Own("'HOME' environment variable is not set \
            or equal to the empty string.".to_owned()))
}

pub fn with_path<P, F, T>(path: Option<P>, f: F) -> Result<T>
    where P: AsRef<Path>,
          F: FnOnce(&AsRef<Path>) -> Result<T>,
{
    if let Some(path) = path {
        f(&path)
    } else {
        let path = try!(directory());
        f(&path)
    }
}

pub fn profiles<P>(path: P) -> Result<Box<Iterator<Item = Result<Profile>>>> where P: AsRef<Path> {
    Ok(Box::new(try!(files(&path)).map(|entry| profile_from_file(entry.path()))))
}

pub fn valid_profiles<P>(path: P) -> Result<Box<Iterator<Item = Profile>>> where P: AsRef<Path> {
    Ok(Box::new(try!(profiles(path)).filter(Result::is_ok).map(|r| r.unwrap())))
}

pub struct SearchInfo {
    pub total: usize,
    pub profiles: Vec<Profile>,
}

pub fn search<P>(path: P, s: &str) -> Result<SearchInfo>
    where P: AsRef<Path>
{
    let mut total = 0;
    let profiles = try!(valid_profiles(path)).filter(|profile| {
        total += 1;
        profile.contains(s)
    }).collect();

    Ok(SearchInfo {
        total: total,
        profiles: profiles,
    })
}

/// Returns instance of the `Profile` parsed from a file.
pub fn profile_from_file<P>(path: P) -> Result<Profile>
    where P: AsRef<Path>
{
    let mut file = try!(File::open(path.as_ref()));
    let mut buf = Vec::new();
    try!(file.read_to_end(&mut buf));
    profile_from_data(&buf).map(|mut p| {
        p.path = path.as_ref().to_owned();
        p
    }).ok_or(Error::Own("Couldn't parse file.".into()))
}

/// Returns instance of the `Profile` parsed from a `data`.
pub fn profile_from_data(data: &[u8]) -> Option<Profile> {
    if let Some(data) = find_plist(data) {
        let mut profile = Profile::empty();
        let mut iter = plist::xml::EventReader::new(io::Cursor::new(data)).into_iter();
        while let Some(item) = iter.next() {
            if let Ok(StringValue(key)) = item {
                if key == "UUID" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.uuid = value;
                    }
                }
                if key == "Name" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.name = value;
                    }
                }
                if key == "application-identifier" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.app_identifier = value;
                    }
                }
                if key == "CreationDate" {
                    if let Some(Ok(DateValue(value))) = iter.next() {
                        profile.creation_date = value;
                    }
                }
                if key == "ExpirationDate" {
                    if let Some(Ok(DateValue(value))) = iter.next() {
                        profile.expiration_date = value;
                    }
                }
            }
        }
        Some(profile)
    } else {
        None
    }
}

/// Returns an index where the `needle` starts in the `data`.
fn find(data: &[u8], needle: &[u8]) -> Option<usize> {
    let needle_len = needle.len();
    for (i, _) in data.iter().enumerate() {
        if i + needle_len > data.len() {
            return None;
        }
        if &data[i..i + needle_len] == needle {
            return Some(i);
        }
    }
    None
}

/// Returns a plist content in a `data`.
fn find_plist(data: &[u8]) -> Option<&[u8]> {
    let prefix = b"<?xml version=";
    let suffix = b"</plist>";

    let start_i = find(data, prefix);
    let end_i = find(data, suffix).map(|i| i + suffix.len());

    if let (Some(start_i), Some(end_i)) = (start_i, end_i) {
        if end_i < data.len() {
            return Some(&data[start_i..end_i]);
        }
    }

    None
}

#[cfg(test)]
mod tests {
    use expectest::prelude::*;
    use tempdir::TempDir;
    use std::fs::File;
    use super::files;

    #[test]
    fn filter_mobileprovision_files() {
        let temp_dir = TempDir::new("test").unwrap();
        let result = files(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = files(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<commit_msg>Run rustfmt on lib.<commit_after>
//! **mprovision** is a tool that helps iOS developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
#[cfg(test)]
extern crate tempdir;
extern crate plist;
extern crate chrono;

use std::fs::{self, DirEntry, File};
use std::path::{Path, PathBuf};
use std::env;
use std::io::{self, Read};
use plist::PlistEvent::*;

pub use error::Error;
pub use profile::Profile;

mod error;
mod profile;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn files<P>(path: P) -> Result<Box<Iterator<Item = DirEntry>>>
    where P: AsRef<Path>
{
    let entries = try!(fs::read_dir(&path));
    let filtered = entries.filter_map(|entry| entry.ok())
                          .filter_map(|entry| {
                              if let Some(ext) = entry.path().extension() {
                                  if ext == "mobileprovision" {
                                      return Some(entry);
                                  }
                              }
                              None
                          });
    Ok(Box::new(filtered))
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    env::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or(Error::Own("'HOME' environment variable is not set or equal to the empty string."
                              .to_owned()))
}

pub fn with_path<P, F, T>(path: Option<P>, f: F) -> Result<T>
    where P: AsRef<Path>,
          F: FnOnce(&AsRef<Path>) -> Result<T>
{
    if let Some(path) = path {
        f(&path)
    } else {
        let path = try!(directory());
        f(&path)
    }
}

pub fn profiles<P>(path: P) -> Result<Box<Iterator<Item = Result<Profile>>>>
    where P: AsRef<Path>
{
    Ok(Box::new(try!(files(&path)).map(|entry| profile_from_file(entry.path()))))
}

pub fn valid_profiles<P>(path: P) -> Result<Box<Iterator<Item = Profile>>>
    where P: AsRef<Path>
{
    Ok(Box::new(try!(profiles(path)).filter(Result::is_ok).map(|r| r.unwrap())))
}

pub struct SearchInfo {
    pub total: usize,
    pub profiles: Vec<Profile>,
}

pub fn search<P>(path: P, s: &str) -> Result<SearchInfo>
    where P: AsRef<Path>
{
    let mut total = 0;
    let profiles = try!(valid_profiles(path))
                       .filter(|profile| {
                           total += 1;
                           profile.contains(s)
                       })
                       .collect();

    Ok(SearchInfo {
        total: total,
        profiles: profiles,
    })
}

/// Returns instance of the `Profile` parsed from a file.
pub fn profile_from_file<P>(path: P) -> Result<Profile>
    where P: AsRef<Path>
{
    let mut file = try!(File::open(path.as_ref()));
    let mut buf = Vec::new();
    try!(file.read_to_end(&mut buf));
    profile_from_data(&buf)
        .map(|mut p| {
            p.path = path.as_ref().to_owned();
            p
        })
        .ok_or(Error::Own("Couldn't parse file.".into()))
}

/// Returns instance of the `Profile` parsed from a `data`.
pub fn profile_from_data(data: &[u8]) -> Option<Profile> {
    if let Some(data) = find_plist(data) {
        let mut profile = Profile::empty();
        let mut iter = plist::xml::EventReader::new(io::Cursor::new(data)).into_iter();
        while let Some(item) = iter.next() {
            if let Ok(StringValue(key)) = item {
                if key == "UUID" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.uuid = value;
                    }
                }
                if key == "Name" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.name = value;
                    }
                }
                if key == "application-identifier" {
                    if let Some(Ok(StringValue(value))) = iter.next() {
                        profile.app_identifier = value;
                    }
                }
                if key == "CreationDate" {
                    if let Some(Ok(DateValue(value))) = iter.next() {
                        profile.creation_date = value;
                    }
                }
                if key == "ExpirationDate" {
                    if let Some(Ok(DateValue(value))) = iter.next() {
                        profile.expiration_date = value;
                    }
                }
            }
        }
        Some(profile)
    } else {
        None
    }
}

/// Returns an index where the `needle` starts in the `data`.
fn find(data: &[u8], needle: &[u8]) -> Option<usize> {
    let needle_len = needle.len();
    for (i, _) in data.iter().enumerate() {
        if i + needle_len > data.len() {
            return None;
        }
        if &data[i..i + needle_len] == needle {
            return Some(i);
        }
    }
    None
}

/// Returns a plist content in a `data`.
fn find_plist(data: &[u8]) -> Option<&[u8]> {
    let prefix = b"<?xml version=";
    let suffix = b"</plist>";

    let start_i = find(data, prefix);
    let end_i = find(data, suffix).map(|i| i + suffix.len());

    if let (Some(start_i), Some(end_i)) = (start_i, end_i) {
        if end_i < data.len() {
            return Some(&data[start_i..end_i]);
        }
    }

    None
}

#[cfg(test)]
mod tests {
    use expectest::prelude::*;
    use tempdir::TempDir;
    use std::fs::File;
    use super::files;

    #[test]
    fn filter_mobileprovision_files() {
        let temp_dir = TempDir::new("test").unwrap();
        let result = files(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = files(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate cairo_sys as ffi;
extern crate libc;
extern crate glib;
extern crate c_vec;

pub use ffi::enums;

pub use ffi::cairo_rectangle_t as Rectangle;
pub use ffi::cairo_rectangle_int_t as RectangleInt;

pub use self::context::{
    Context,
    RectangleVec,
};

pub use self::paths::{
    Path,
    PathSegments,
    PathSegment
};

pub use self::enums::{
    Status,
    Antialias,
    FillRule,
    LineCap,
    LineJoin,
    Operator,
    PathDataType,
    Format,
    SurfaceType,
};

pub use self::patterns::{
    //Traits
    Pattern,
    Gradient,

    //Structs
    LinearGradient,
    RadialGradient,
    SolidPattern,
    SurfacePattern,
};

#[cfg(feature = "1.12")]
pub use self::patterns::{
    Mesh,
    MeshCorner,
};

pub use self::fonts::{
    FontFace,
    ScaledFont,
    FontOptions,

    Glyph,
    FontExtents,
    TextExtents,
    TextCluster,
};

pub use self::matrices::{
    Matrix,
    MatrixTrait,
};

pub use image_surface::ImageSurface;
pub use surface::Surface;

pub mod prelude;

mod fonts;
mod context;
mod error;
mod image_surface;
#[cfg(feature = "png")]
mod image_surface_png;
mod paths;
mod patterns;
mod surface;
mod matrices;
<commit_msg>Clean up a bit<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate cairo_sys as ffi;
extern crate libc;
extern crate glib;
extern crate c_vec;

pub use ffi::enums;
pub use ffi::cairo_rectangle_t as Rectangle;
pub use ffi::cairo_rectangle_int_t as RectangleInt;

pub use context::{
    Context,
    RectangleVec,
};

pub use paths::{
    Path,
    PathSegments,
    PathSegment
};

pub use enums::{
    Status,
    Antialias,
    FillRule,
    LineCap,
    LineJoin,
    Operator,
    PathDataType,
    Format,
    SurfaceType,
};

pub use patterns::{
    //Traits
    Pattern,
    Gradient,

    //Structs
    LinearGradient,
    RadialGradient,
    SolidPattern,
    SurfacePattern,
};

#[cfg(feature = "1.12")]
pub use patterns::{
    Mesh,
    MeshCorner,
};

pub use fonts::{
    FontFace,
    ScaledFont,
    FontOptions,

    Glyph,
    FontExtents,
    TextExtents,
    TextCluster,
};

pub use matrices::{
    Matrix,
    MatrixTrait,
};

pub use image_surface::ImageSurface;
pub use surface::Surface;

pub mod prelude;

mod fonts;
mod context;
mod error;
mod image_surface;
#[cfg(feature = "png")]
mod image_surface_png;
mod paths;
mod patterns;
mod surface;
mod matrices;
<|endoftext|>


<commit_before>extern crate crossbeam;

use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::process::{Command, Output, ExitStatus};
use std::thread::JoinHandle;

mod pipe;

pub fn cmd<T: AsRef<OsStr>>(argv: &[T]) -> Expression<'static> {
    let argv_vec = argv.iter().map(|arg| arg.as_ref().to_owned()).collect();
    Expression {
        inner: ExpressionInner::ArgvCommand(argv_vec),
        ioargs: IoArgs::new(),
    }
}

pub fn sh<T: AsRef<OsStr>>(command: T) -> Expression<'static> {
    Expression {
        inner: ExpressionInner::ShCommand(command.as_ref().to_owned()),
        ioargs: IoArgs::new(),
    }
}

pub fn pipe<'a>(left: Expression<'a>, right: Expression<'a>) -> Expression<'a> {
    Expression {
        inner: ExpressionInner::Pipe(Box::new(left), Box::new(right)),
        ioargs: IoArgs::new(),
    }
}

pub fn then<'a>(left: Expression<'a>, right: Expression<'a>) -> Expression<'a> {
    Expression {
        inner: ExpressionInner::Then(Box::new(left), Box::new(right)),
        ioargs: IoArgs::new(),
    }
}

#[derive(Debug)]
pub struct Expression<'a> {
    inner: ExpressionInner<'a>,
    ioargs: IoArgs<'a>,
}

#[derive(Debug)]
enum ExpressionInner<'a> {
    ArgvCommand(Vec<OsString>),
    ShCommand(OsString),
    Pipe(Box<Expression<'a>>, Box<Expression<'a>>),
    Then(Box<Expression<'a>>, Box<Expression<'a>>),
}

impl<'a> Expression<'a> {
    pub fn run(&self) -> Result<Output, Error> {
        unreachable!();
    }

    pub fn read(&self) -> Result<String, Error> {
        let (handle, reader) = pipe_with_reader_thread();
        let mut context = IoContext::new();
        context.stdout = handle;
        let status = try!(self.exec(context));
        let stdout_vec = try!(reader.join().unwrap());
        if !status.success() {
            return Err(Error::Status(Output {
                status: status,
                stdout: stdout_vec,
                stderr: Vec::new(),
            }));
        }
        let stdout_string = try!(std::str::from_utf8(&stdout_vec))
                                .trim_right_matches('\n')
                                .to_owned();
        Ok(stdout_string)
    }

    fn exec(&self, parent_context: IoContext) -> io::Result<ExitStatus> {
        self.ioargs.with_child_context(parent_context, |context| {
            match self.inner {
                ExpressionInner::ArgvCommand(ref argv) => exec_argv(argv, context),
                ExpressionInner::ShCommand(ref command) => exec_sh(command, context),
                ExpressionInner::Pipe(ref left, ref right) => exec_pipe(left, right, context),
                ExpressionInner::Then(ref left, ref right) => exec_then(left, right, context),
            }
        })
    }

    pub fn stdin(&mut self, arg: InputArg<'a>) -> &mut Self {
        self.ioargs.stdin = arg;
        self
    }

    pub fn stdout(&mut self, arg: OutputArg<'a>) -> &mut Self {
        self.ioargs.stdout = arg;
        self
    }

    pub fn stderr(&mut self, arg: OutputArg<'a>) -> &mut Self {
        self.ioargs.stderr = arg;
        self
    }
}

fn exec_argv(argv: &Vec<OsString>, context: IoContext) -> io::Result<ExitStatus> {
    let mut command = Command::new(&argv[0]);
    command.args(&argv[1..]);
    command.stdin(context.stdin.to_stdio());
    command.stdout(context.stdout.to_stdio());
    command.stderr(context.stderr.to_stdio());
    command.status()
}

fn exec_sh(command: &OsStr, context: IoContext) -> io::Result<ExitStatus> {
    // TODO: What shell should we be using here, really?
    // TODO: Figure out how cmd.exe works on Windows.
    let mut argv = Vec::new();
    argv.push("bash".into());
    argv.push("-c".into());
    argv.push(command.to_owned());
    exec_argv(&argv, context)
}

fn exec_pipe(left: &Expression, right: &Expression, context: IoContext) -> io::Result<ExitStatus> {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let left_context = IoContext {
        stdin: context.stdin,
        stdout: write_pipe,
        stderr: context.stderr.clone(),
    };
    let right_context = IoContext {
        stdin: read_pipe,
        stdout: context.stdout,
        stderr: context.stderr,
    };

    let (left_result, right_result) = crossbeam::scope(|scope| {
        let left_joiner = scope.spawn(|| left.exec(left_context));
        let right_result = right.exec(right_context);
        let left_result = left_joiner.join();
        (left_result, right_result)
    });

    let right_status = try!(right_result);
    let left_status = try!(left_result);
    if !right_status.success() {
        Ok(right_status)
    } else {
        Ok(left_status)
    }
}

fn exec_then(left: &Expression, right: &Expression, context: IoContext) -> io::Result<ExitStatus> {
    let status = try!(left.exec(context.clone()));
    if !status.success() {
        Ok(status)
    } else {
        right.exec(context)
    }
}

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Utf8(std::str::Utf8Error),
    Status(Output),
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<std::str::Utf8Error> for Error {
    fn from(err: std::str::Utf8Error) -> Error {
        Error::Utf8(err)
    }
}

// IoArgs store the redirections and other settings associated with an expression. At execution
// time, IoArgs are used to modify an IoContext, which contains the actual pipes that child process
#[derive(Debug)]
struct IoArgs<'a> {
    stdin: InputArg<'a>,
    stdout: OutputArg<'a>,
    stderr: OutputArg<'a>,
}

impl<'a> IoArgs<'a> {
    fn new() -> IoArgs<'static> {
        IoArgs {
            stdin: InputArg::Inherit,
            stdout: OutputArg::Inherit,
            stderr: OutputArg::Inherit,
        }
    }

    fn with_child_context<F, T>(&self, parent_context: IoContext, inner: F) -> io::Result<T>
        where F: FnOnce(IoContext) -> io::Result<T>
    {
        crossbeam::scope(|scope| {
            let parent_context = parent_context;  // move it into the closure
            let (stdin, maybe_stdin_thread) = try!(self.stdin
                                                       .update_handle(parent_context.stdin, scope));
            let stdout = try!(self.stdout.update_handle(parent_context.stdout));
            let stderr = try!(self.stderr.update_handle(parent_context.stderr));
            let (stdout, stderr) = apply_swaps(stdout, stderr, &self.stdout, &self.stderr);

            let child_context = IoContext {
                stdin: stdin,
                stdout: stdout,
                stderr: stderr,
            };

            let ret = try!(inner(child_context));

            if let Some(thread) = maybe_stdin_thread {
                try!(thread.join());
            }

            Ok(ret)
        })
    }
}

// returns (new_stdout, new_stderr)
fn apply_swaps(stdout_handle: pipe::Handle,
               stderr_handle: pipe::Handle,
               stdout_arg: &OutputArg,
               stderr_arg: &OutputArg)
               -> (pipe::Handle, pipe::Handle) {
    // TODO: These clones are a little excessive.
    let new_stdout = if let &OutputArg::Stderr = stdout_arg {
        stderr_handle.clone()
    } else {
        stdout_handle.clone()
    };
    let new_stderr = if let &OutputArg::Stdout = stderr_arg {
        stdout_handle.clone()
    } else {
        stderr_handle.clone()
    };
    (new_stdout, new_stderr)
}

#[derive(Debug)]
pub enum InputArg<'a> {
    Inherit,
    Null,
    Path(Cow<'a, Path>),
    FileRef(&'a File),
    FileOwned(File),
    Bytes(Cow<'a, [u8]>),
}

impl<'a> InputArg<'a> {
    fn update_handle(&'a self,
                     parent_handle: pipe::Handle,
                     scope: &crossbeam::Scope<'a>)
                     -> io::Result<(pipe::Handle, Option<WriterThreadJoiner>)> {
        let mut maybe_thread = None;
        let handle = match self {
            &InputArg::Inherit => parent_handle,
            &InputArg::Null => pipe::Handle::from_file(try!(File::open("/dev/null"))),  // TODO: Windows
            &InputArg::Path(ref p) => pipe::Handle::from_file(try!(File::open(&p))),
            &InputArg::FileRef(ref f) => pipe::Handle::dup_file(f),
            &InputArg::FileOwned(ref f) => pipe::Handle::dup_file(f),
            &InputArg::Bytes(ref v) => {
                let (handle, thread) = pipe_with_writer_thread(v, scope);
                maybe_thread = Some(thread);
                handle
            }
        };
        Ok((handle, maybe_thread))
    }
}

// TODO: stdout/stderr swaps
#[derive(Debug)]
pub enum OutputArg<'a> {
    Inherit,
    Null,
    Stdout,
    Stderr,
    Path(Cow<'a, Path>),
    FileRef(&'a File),
    FileOwned(File),
}

impl<'a> OutputArg<'a> {
    fn update_handle(&self, parent_handle: pipe::Handle) -> io::Result<pipe::Handle> {
        let handle = match self {
            // Stdout and Stderr are no-ops here, but with_child_context interprets them. This is
            // because they refer to the *child's* stdout/stderr, with other redirections already
            // applied, rather than to the parent's.
            &OutputArg::Inherit | &OutputArg::Stdout | &OutputArg::Stderr => parent_handle,
            &OutputArg::Null => pipe::Handle::from_file(try!(File::create("/dev/null"))),  // TODO: Windows
            &OutputArg::Path(ref p) => pipe::Handle::from_file(try!(File::create(&p))),
            &OutputArg::FileRef(ref f) => pipe::Handle::dup_file(f),
            &OutputArg::FileOwned(ref f) => pipe::Handle::dup_file(f),
        };
        Ok(handle)
    }
}

// An IoContext represents the file descriptors child processes are talking to at execution time.
// It's initialized in run(), with dups of the stdin/stdout/stderr pipes, and then passed down to
// sub-expressions. Compound expressions will clone() it, and pipes/redirections will modify it
// according to their IoArgs.
#[derive(Clone, Debug)]
pub struct IoContext {
    stdin: pipe::Handle,
    stdout: pipe::Handle,
    stderr: pipe::Handle,
}

impl IoContext {
    fn new() -> IoContext {
        IoContext {
            stdin: pipe::Handle::stdin(),
            stdout: pipe::Handle::stdout(),
            stderr: pipe::Handle::stderr(),
        }
    }
}

fn pipe_with_reader_thread() -> (pipe::Handle, JoinHandle<io::Result<Vec<u8>>>) {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let thread = std::thread::spawn(move || {
        let mut read_file = read_pipe.to_file();
        let mut output = Vec::new();
        try!(read_file.read_to_end(&mut output));
        Ok(output)
    });
    (write_pipe, thread)
}

type WriterThreadJoiner = crossbeam::ScopedJoinHandle<io::Result<()>>;

fn pipe_with_writer_thread<'a>(input: &'a [u8],
                               scope: &crossbeam::Scope<'a>)
                               -> (pipe::Handle, WriterThreadJoiner) {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let thread = scope.spawn(move || {
        let mut write_file = write_pipe.to_file();
        try!(write_file.write_all(&input));
        Ok(())
    });
    (read_pipe, thread)
}

#[cfg(test)]
mod test {
    extern crate tempfile;

    use super::*;
    use std::borrow::Cow;
    use std::io::prelude::*;
    use std::io::SeekFrom;

    #[test]
    fn test_cmd() {
        let output = cmd(&["echo", "hi"]).read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_sh() {
        let output = sh("echo hi").read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_pipe() {
        let output = pipe(sh("echo hi"), sh("sed s/i/o/")).read().unwrap();
        assert_eq!("ho", output);
    }

    #[test]
    fn test_then() {
        let output = then(sh("echo -n hi"), sh("echo lo")).read().unwrap();
        assert_eq!("hilo", output);
    }

    #[test]
    fn test_input() {
        let mut expr = sh("sed s/f/g/");
        expr.stdin(InputArg::Bytes(Cow::Borrowed(b"foo")));
        let output = expr.read().unwrap();
        assert_eq!("goo", output);
    }

    #[test]
    fn test_null() {
        let mut expr = cmd(&["cat"]);
        expr.stdin(InputArg::Null);
        expr.stdout(OutputArg::Null);
        let output = expr.read().unwrap();
        assert_eq!("", output);
    }

    #[test]
    fn test_path() {
        let mut input_file = tempfile::NamedTempFile::new().unwrap();
        let mut output_file = tempfile::NamedTempFile::new().unwrap();
        println!("Here are the paths: {:?} {:?}",
                 input_file.path(),
                 output_file.path());
        input_file.write_all(b"foo").unwrap();
        let mut expr = sh("sed s/o/a/g");
        expr.stdin(InputArg::Path(Cow::Owned(input_file.path().to_owned())));
        expr.stdout(OutputArg::Path(Cow::Owned(output_file.path().to_owned())));
        let output = expr.read().unwrap();
        assert_eq!("", output);
        let mut file_output = String::new();
        output_file.read_to_string(&mut file_output).unwrap();
        assert_eq!("faa", file_output);
    }

    #[test]
    fn test_owned_input() {
        fn set_input(expr: &mut Expression) {
            let mystr = format!("I own this: {}", "foo");
            // This would be a lifetime error if we tried to use &mystr.
            expr.stdin(InputArg::Bytes(Cow::Owned(mystr.into_bytes())));
        }

        let mut c = cmd(&["cat"]);
        set_input(&mut c);
        let output = c.read().unwrap();
        assert_eq!("I own this: foo", output);
    }

    #[test]
    fn test_stderr_to_stdout() {
        let mut command = sh("echo hi >&2");
        command.stderr(OutputArg::Stdout);
        let output = command.read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_file() {
        let mut temp = tempfile::NamedTempFile::new().unwrap();
        temp.write_all(b"example").unwrap();
        temp.seek(SeekFrom::Start(0)).unwrap();
        let mut expr = cmd(&["cat"]);
        expr.stdin(InputArg::FileRef(&temp));
        let output = expr.read().unwrap();
        assert_eq!(output, "example");
    }
}
<commit_msg>add TODOs for figuring out what to do with Cow<commit_after>extern crate crossbeam;

use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::process::{Command, Output, ExitStatus};
use std::thread::JoinHandle;

mod pipe;

pub fn cmd<T: AsRef<OsStr>>(argv: &[T]) -> Expression<'static> {
    let argv_vec = argv.iter().map(|arg| arg.as_ref().to_owned()).collect();
    Expression {
        inner: ExpressionInner::ArgvCommand(argv_vec),
        ioargs: IoArgs::new(),
    }
}

pub fn sh<T: AsRef<OsStr>>(command: T) -> Expression<'static> {
    Expression {
        inner: ExpressionInner::ShCommand(command.as_ref().to_owned()),
        ioargs: IoArgs::new(),
    }
}

pub fn pipe<'a>(left: Expression<'a>, right: Expression<'a>) -> Expression<'a> {
    Expression {
        inner: ExpressionInner::Pipe(Box::new(left), Box::new(right)),
        ioargs: IoArgs::new(),
    }
}

pub fn then<'a>(left: Expression<'a>, right: Expression<'a>) -> Expression<'a> {
    Expression {
        inner: ExpressionInner::Then(Box::new(left), Box::new(right)),
        ioargs: IoArgs::new(),
    }
}

#[derive(Debug)]
pub struct Expression<'a> {
    inner: ExpressionInner<'a>,
    ioargs: IoArgs<'a>,
}

#[derive(Debug)]
enum ExpressionInner<'a> {
    ArgvCommand(Vec<OsString>),
    ShCommand(OsString),
    Pipe(Box<Expression<'a>>, Box<Expression<'a>>),
    Then(Box<Expression<'a>>, Box<Expression<'a>>),
}

impl<'a> Expression<'a> {
    pub fn run(&self) -> Result<Output, Error> {
        unreachable!();
    }

    pub fn read(&self) -> Result<String, Error> {
        let (handle, reader) = pipe_with_reader_thread();
        let mut context = IoContext::new();
        context.stdout = handle;
        let status = try!(self.exec(context));
        let stdout_vec = try!(reader.join().unwrap());
        if !status.success() {
            return Err(Error::Status(Output {
                status: status,
                stdout: stdout_vec,
                stderr: Vec::new(),
            }));
        }
        let stdout_string = try!(std::str::from_utf8(&stdout_vec))
                                .trim_right_matches('\n')
                                .to_owned();
        Ok(stdout_string)
    }

    fn exec(&self, parent_context: IoContext) -> io::Result<ExitStatus> {
        self.ioargs.with_child_context(parent_context, |context| {
            match self.inner {
                ExpressionInner::ArgvCommand(ref argv) => exec_argv(argv, context),
                ExpressionInner::ShCommand(ref command) => exec_sh(command, context),
                ExpressionInner::Pipe(ref left, ref right) => exec_pipe(left, right, context),
                ExpressionInner::Then(ref left, ref right) => exec_then(left, right, context),
            }
        })
    }

    pub fn stdin(&mut self, arg: InputArg<'a>) -> &mut Self {
        self.ioargs.stdin = arg;
        self
    }

    pub fn stdout(&mut self, arg: OutputArg<'a>) -> &mut Self {
        self.ioargs.stdout = arg;
        self
    }

    pub fn stderr(&mut self, arg: OutputArg<'a>) -> &mut Self {
        self.ioargs.stderr = arg;
        self
    }
}

fn exec_argv(argv: &Vec<OsString>, context: IoContext) -> io::Result<ExitStatus> {
    let mut command = Command::new(&argv[0]);
    command.args(&argv[1..]);
    command.stdin(context.stdin.to_stdio());
    command.stdout(context.stdout.to_stdio());
    command.stderr(context.stderr.to_stdio());
    command.status()
}

fn exec_sh(command: &OsStr, context: IoContext) -> io::Result<ExitStatus> {
    // TODO: What shell should we be using here, really?
    // TODO: Figure out how cmd.exe works on Windows.
    let mut argv = Vec::new();
    argv.push("bash".into());
    argv.push("-c".into());
    argv.push(command.to_owned());
    exec_argv(&argv, context)
}

fn exec_pipe(left: &Expression, right: &Expression, context: IoContext) -> io::Result<ExitStatus> {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let left_context = IoContext {
        stdin: context.stdin,
        stdout: write_pipe,
        stderr: context.stderr.clone(),
    };
    let right_context = IoContext {
        stdin: read_pipe,
        stdout: context.stdout,
        stderr: context.stderr,
    };

    let (left_result, right_result) = crossbeam::scope(|scope| {
        let left_joiner = scope.spawn(|| left.exec(left_context));
        let right_result = right.exec(right_context);
        let left_result = left_joiner.join();
        (left_result, right_result)
    });

    let right_status = try!(right_result);
    let left_status = try!(left_result);
    if !right_status.success() {
        Ok(right_status)
    } else {
        Ok(left_status)
    }
}

fn exec_then(left: &Expression, right: &Expression, context: IoContext) -> io::Result<ExitStatus> {
    let status = try!(left.exec(context.clone()));
    if !status.success() {
        Ok(status)
    } else {
        right.exec(context)
    }
}

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Utf8(std::str::Utf8Error),
    Status(Output),
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<std::str::Utf8Error> for Error {
    fn from(err: std::str::Utf8Error) -> Error {
        Error::Utf8(err)
    }
}

// IoArgs store the redirections and other settings associated with an expression. At execution
// time, IoArgs are used to modify an IoContext, which contains the actual pipes that child process
#[derive(Debug)]
struct IoArgs<'a> {
    stdin: InputArg<'a>,
    stdout: OutputArg<'a>,
    stderr: OutputArg<'a>,
}

impl<'a> IoArgs<'a> {
    fn new() -> IoArgs<'static> {
        IoArgs {
            stdin: InputArg::Inherit,
            stdout: OutputArg::Inherit,
            stderr: OutputArg::Inherit,
        }
    }

    fn with_child_context<F, T>(&self, parent_context: IoContext, inner: F) -> io::Result<T>
        where F: FnOnce(IoContext) -> io::Result<T>
    {
        crossbeam::scope(|scope| {
            let parent_context = parent_context;  // move it into the closure
            let (stdin, maybe_stdin_thread) = try!(self.stdin
                                                       .update_handle(parent_context.stdin, scope));
            let stdout = try!(self.stdout.update_handle(parent_context.stdout));
            let stderr = try!(self.stderr.update_handle(parent_context.stderr));
            let (stdout, stderr) = apply_swaps(stdout, stderr, &self.stdout, &self.stderr);

            let child_context = IoContext {
                stdin: stdin,
                stdout: stdout,
                stderr: stderr,
            };

            let ret = try!(inner(child_context));

            if let Some(thread) = maybe_stdin_thread {
                try!(thread.join());
            }

            Ok(ret)
        })
    }
}

// returns (new_stdout, new_stderr)
fn apply_swaps(stdout_handle: pipe::Handle,
               stderr_handle: pipe::Handle,
               stdout_arg: &OutputArg,
               stderr_arg: &OutputArg)
               -> (pipe::Handle, pipe::Handle) {
    // TODO: These clones are a little excessive.
    let new_stdout = if let &OutputArg::Stderr = stdout_arg {
        stderr_handle.clone()
    } else {
        stdout_handle.clone()
    };
    let new_stderr = if let &OutputArg::Stdout = stderr_arg {
        stdout_handle.clone()
    } else {
        stderr_handle.clone()
    };
    (new_stdout, new_stderr)
}

#[derive(Debug)]
pub enum InputArg<'a> {
    Inherit,
    Null,
    Path(Cow<'a, Path>),
    // TODO: Figure out what the hell to do with these File variants. They don't work with Cow,
    // because they're not Clone/ToOwned.
    FileRef(&'a File),
    FileOwned(File),
    Bytes(Cow<'a, [u8]>),
}

impl<'a> InputArg<'a> {
    fn update_handle(&'a self,
                     parent_handle: pipe::Handle,
                     scope: &crossbeam::Scope<'a>)
                     -> io::Result<(pipe::Handle, Option<WriterThreadJoiner>)> {
        let mut maybe_thread = None;
        let handle = match self {
            &InputArg::Inherit => parent_handle,
            &InputArg::Null => pipe::Handle::from_file(try!(File::open("/dev/null"))),  // TODO: Windows
            &InputArg::Path(ref p) => pipe::Handle::from_file(try!(File::open(&p))),
            &InputArg::FileRef(ref f) => pipe::Handle::dup_file(f),
            &InputArg::FileOwned(ref f) => pipe::Handle::dup_file(f),
            &InputArg::Bytes(ref v) => {
                let (handle, thread) = pipe_with_writer_thread(v, scope);
                maybe_thread = Some(thread);
                handle
            }
        };
        Ok((handle, maybe_thread))
    }
}

// TODO: stdout/stderr swaps
#[derive(Debug)]
pub enum OutputArg<'a> {
    Inherit,
    Null,
    Stdout,
    Stderr,
    Path(Cow<'a, Path>),
    // TODO: Figure out what the hell to do with these File variants. They don't work with Cow,
    // because they're not Clone/ToOwned.
    FileRef(&'a File),
    FileOwned(File),
}

impl<'a> OutputArg<'a> {
    fn update_handle(&self, parent_handle: pipe::Handle) -> io::Result<pipe::Handle> {
        let handle = match self {
            // Stdout and Stderr are no-ops here, but with_child_context interprets them. This is
            // because they refer to the *child's* stdout/stderr, with other redirections already
            // applied, rather than to the parent's.
            &OutputArg::Inherit | &OutputArg::Stdout | &OutputArg::Stderr => parent_handle,
            &OutputArg::Null => pipe::Handle::from_file(try!(File::create("/dev/null"))),  // TODO: Windows
            &OutputArg::Path(ref p) => pipe::Handle::from_file(try!(File::create(&p))),
            &OutputArg::FileRef(ref f) => pipe::Handle::dup_file(f),
            &OutputArg::FileOwned(ref f) => pipe::Handle::dup_file(f),
        };
        Ok(handle)
    }
}

// An IoContext represents the file descriptors child processes are talking to at execution time.
// It's initialized in run(), with dups of the stdin/stdout/stderr pipes, and then passed down to
// sub-expressions. Compound expressions will clone() it, and pipes/redirections will modify it
// according to their IoArgs.
#[derive(Clone, Debug)]
pub struct IoContext {
    stdin: pipe::Handle,
    stdout: pipe::Handle,
    stderr: pipe::Handle,
}

impl IoContext {
    fn new() -> IoContext {
        IoContext {
            stdin: pipe::Handle::stdin(),
            stdout: pipe::Handle::stdout(),
            stderr: pipe::Handle::stderr(),
        }
    }
}

fn pipe_with_reader_thread() -> (pipe::Handle, JoinHandle<io::Result<Vec<u8>>>) {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let thread = std::thread::spawn(move || {
        let mut read_file = read_pipe.to_file();
        let mut output = Vec::new();
        try!(read_file.read_to_end(&mut output));
        Ok(output)
    });
    (write_pipe, thread)
}

type WriterThreadJoiner = crossbeam::ScopedJoinHandle<io::Result<()>>;

fn pipe_with_writer_thread<'a>(input: &'a [u8],
                               scope: &crossbeam::Scope<'a>)
                               -> (pipe::Handle, WriterThreadJoiner) {
    let (read_pipe, write_pipe) = pipe::open_pipe();
    let thread = scope.spawn(move || {
        let mut write_file = write_pipe.to_file();
        try!(write_file.write_all(&input));
        Ok(())
    });
    (read_pipe, thread)
}

#[cfg(test)]
mod test {
    extern crate tempfile;

    use super::*;
    use std::borrow::Cow;
    use std::io::prelude::*;
    use std::io::SeekFrom;

    #[test]
    fn test_cmd() {
        let output = cmd(&["echo", "hi"]).read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_sh() {
        let output = sh("echo hi").read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_pipe() {
        let output = pipe(sh("echo hi"), sh("sed s/i/o/")).read().unwrap();
        assert_eq!("ho", output);
    }

    #[test]
    fn test_then() {
        let output = then(sh("echo -n hi"), sh("echo lo")).read().unwrap();
        assert_eq!("hilo", output);
    }

    #[test]
    fn test_input() {
        let mut expr = sh("sed s/f/g/");
        expr.stdin(InputArg::Bytes(Cow::Borrowed(b"foo")));
        let output = expr.read().unwrap();
        assert_eq!("goo", output);
    }

    #[test]
    fn test_null() {
        let mut expr = cmd(&["cat"]);
        expr.stdin(InputArg::Null);
        expr.stdout(OutputArg::Null);
        let output = expr.read().unwrap();
        assert_eq!("", output);
    }

    #[test]
    fn test_path() {
        let mut input_file = tempfile::NamedTempFile::new().unwrap();
        let mut output_file = tempfile::NamedTempFile::new().unwrap();
        println!("Here are the paths: {:?} {:?}",
                 input_file.path(),
                 output_file.path());
        input_file.write_all(b"foo").unwrap();
        let mut expr = sh("sed s/o/a/g");
        expr.stdin(InputArg::Path(Cow::Owned(input_file.path().to_owned())));
        expr.stdout(OutputArg::Path(Cow::Owned(output_file.path().to_owned())));
        let output = expr.read().unwrap();
        assert_eq!("", output);
        let mut file_output = String::new();
        output_file.read_to_string(&mut file_output).unwrap();
        assert_eq!("faa", file_output);
    }

    #[test]
    fn test_owned_input() {
        fn set_input(expr: &mut Expression) {
            let mystr = format!("I own this: {}", "foo");
            // This would be a lifetime error if we tried to use &mystr.
            expr.stdin(InputArg::Bytes(Cow::Owned(mystr.into_bytes())));
        }

        let mut c = cmd(&["cat"]);
        set_input(&mut c);
        let output = c.read().unwrap();
        assert_eq!("I own this: foo", output);
    }

    #[test]
    fn test_stderr_to_stdout() {
        let mut command = sh("echo hi >&2");
        command.stderr(OutputArg::Stdout);
        let output = command.read().unwrap();
        assert_eq!("hi", output);
    }

    #[test]
    fn test_file() {
        let mut temp = tempfile::NamedTempFile::new().unwrap();
        temp.write_all(b"example").unwrap();
        temp.seek(SeekFrom::Start(0)).unwrap();
        let mut expr = cmd(&["cat"]);
        expr.stdin(InputArg::FileRef(&temp));
        let output = expr.read().unwrap();
        assert_eq!(output, "example");
    }
}
<|endoftext|>