<commit_before>/*!
Pattern-based input scanning.

## Setup

First, place the following in your `Cargo.toml` file:

```{notrust}
[dependencies.scan]

git = "https://github.com/DanielKeep/rust-scan.git"

[dependencies.scan_util]

git = "https://github.com/DanielKeep/rust-scan-util.git"
```

If you are not using Cargo, you will need to download and compile both of these crates and make them available to `rustc`.  Note that the `scan` crate is *only* needed at compile time and does not need to be distributed.

Then, in your root module, add the following:

```{ignore}
#![feature(phase)]

#[phase(plugin)] extern crate scan;
extern crate scan_util;
```

Note that `scan_util` *must* be in the root module, and *must* not be renamed.  This is due to a limitation of macros in Rust.

## Usage

Two macros are provided for scanning text.  In general, both are concerned with taking a string and matching it against a sequence of patterns.  As soon as a matching pattern is found, the corresponding expression is evaluated and returned.

If an error occurs during an underlying IO operation *or* none of the provided patterns matched, an `Err(scan_util::ScanError)` will be returned.

The `scanln!` macro is used to scan a line of input from standard input.  For example:

```{ignore}
scanln! {
	"say hello" => println!("Hi!"),
	"exit" => return,
}
```

The `scan!` macro is used to scan a block of text.  The input value must implement the `Str` trait.  For example:

```{ignore}
let noun_to_identify = "wheelbarrow";
scan! {
	noun_to_identify,

	"banana" => "definitely a banana",
	"crowbar" => "big metal banana",
	_ => "probably not a banana at all",
}
```

## Patterns

A pattern is a space-separated sequence of one or more of the following:

### Text

These indicate a block of text which should be matched.  This matching is done by converting *both* text in the pattern *and* input text into a sequence of tokens, ignoring whitespace.  Each token is either a sequence of letters, a sequence of digits, or a single non-whitespace character.  All whitespace is ignored.

For example, the string `"Left4Dead is pretty fun..."` is tokenised into the sequence `"Left"`, `"4"`, `"Dead"`, `"is"`, `"pretty"`, `"fun"`, `"."`, `"."`, and `"."`.

This behaviour also means that `"ab cd ef"` and `"ab" "cd" "ef"` are semantically equivalent, but `"ab c" "d ef"` is different, because of the break between `c` and `d`.

Also note that text comparisons are *case insensitive*.

In future, the tokeniser, whitespace policy, and case insensitivity will be configurable.

### Capture

These are used to capture a value from the input.  They are written in the form `name: type` like a `let` binding.  The type constraint (`:type`) may be omitted *if* the usage of the captured value allows the type to be inferred.

For example, `age:int` will capture an integer and store it in a binding called `age`.  `thingy` will capture a value, the type of which is to be determined by inference.

An additional restriction is that you can only capture types which implement the `scan_util::Scanner` trait.  By default, scanners for the builtin integer and floating-point types are available as well as `bool` and `char`.  There is also a scanner for `&str` which will capture a single token.

If you wish to supply your own scanning logic for a type, you will need to wrap it in another type (such as a structure) and implement the `Scanner` trait.

### Alternate

A pattern written as `"a" | "b"` will match *either* `a` or `b`.  You can chain as many alternates as you want.

Note that alternates are tried in lexical order, and the first one to match "wins" and no others are tried.  For example, in the pattern `"a" | "a b"`, the second branch will never match.  Also note that as the scanner does not do backtracking, a failure to match later on in a pattern will not cause other alternatives to be tried.

All captures inside an alternate are wrapped in `Option`, with unmatched captures being `None`.

### Group

You can create a sub pattern using parentheses.  This is mainly used for grouping alternates.  For example: `"a" ("b" | "c") "d"`; this will match `a b d` or `a c d`.

### Repetition

Reptitions are used to match a repeating sequence, and optionally capture values from said sequence.  The general form is `[pattern] separator repeat`.

For example:

* `["pretty"]+ "please"` - matches a childish plea.

* `"rgb (" [cs:f32],{3} ")"` - matches a CSS `rgb` colour value.

* `[ns:int](ws:&str)+` - matches a sequence of at least one number separated by arbitrary words.

The `separator` is used to indicate what to match between instances of the main repeating `pattern`.  It can be omitted (in which case no seperator is scanned), or a sub pattern.  A sub pattern consisting of a single text literal can be written with the parentheses omitted.  If the text literal is `","`, `"."`, `";"`, or `":"`, then it may be written without the quotes as well.

The `repeat` indicates how many times the repeating `pattern` must appear.  It can be any one of:

- `?` - zero or one times.

- `*` - zero or more times.

- `+` - one or more times.

- `{n}` - exactly `n` times.

- `{n,}` - at least `n` times.

- `{,m}` - zero or more, but no more than `m`, times.

- `{n,m}` - at least `n`, but no more than `m`, times.

All captures inside a repetition are inserted into `Vec`s.  Nesting repetitions yield nested `Vec`s.

### Tail Capture

Patterns must match their *entire* input.  If you only wish to perform a prefix match, you must add a tail capture to the pattern.  For example, `"spake thee thus", ..the_rest` would match "spake thee thus" and then capture the remainder of the input into `the_rest`.  Note the comma to distinguish the tail capture from the pattern.  Also note that tail captures are `&str`s, limited to the lifetime of the input.

If you wish to do a prefix match, but do not actually care about the remainder of the input, you can use `_` as the capture name.

### Fallback Pattern

Finally, you can specify a fallback arm which *always* matches.  The complete pattern should be either a tail capture (such as `..tail` without any preceeding pattern), or a simple `_`.

## Example

Below is a complete example of various kinds of patterns.

```{ignore}
#![feature(phase)]

#[phase(plugin)] extern crate scan;
extern crate scan_util;

fn main() {
	loop {
		print!("input> ");

		let res = scanln! {
			// Match a single word.
			"hi" => "Hi!".into_string(),

			// Match a sequence of three tokens: "such", "input" and "!".
			"such input!" => "very syntax".into_string(),

			// Match *five* tokens: "and", "now", ".", "." and ".".
			"and now..." => "...for something completely different".into_string(),

			// Match "has" followed by an integer, captured into "value".
			"has" value:int => format!("ok has {}", value),

			// Alternative to the above.
			"has also" value => {
				let value: int = value;
				format!("ok has also {}", value)
			},

			// Match "v" followed by three numbers.
			"v" x:f64 y:f64 z:f64 => format!("({}, {}, {})", x, y, z),

			// Match "rgb" followed by three numbers.
			"rgb" r:u8 g:u8 b:u8 => format!("#{:02x}{:02x}{:02x}", r, g, b),

			// Match "rgb vec" followed by three or four numbers.
			"rgb vec" [rgb:u8],{3,4} => format!("rgba{}", rgb),

			// Match "vecs" followed by a comma-delimited vector of
			// comma-delimited vectors of floats.
			("vecs"|"vechs"|"vetches") [ "[" [vss:f64],* "]" ],* => format!("vss: {}", vss),

			// Match a vector of either an int or a word.
			"ints and words:" [is:int | ss:&str]* => format!("is: {}, ss: {}", is, ss),

			// Match a list of words separated by "and" or a comma.
			"words:" [words:&str]("and"|", and"|",")+ => format!("words: {}", words),

			// Match a list of numbers separated by words.
			"int words:" [is:int](ss:&str)* => format!("is: {}, ss: {}", is, ss),

			// Match "go" followed by a single word.
			"go" exit:&str => format!("Leaving by the {} exit.", exit),

			// Match "yes" or "no".
			"yes" | "no" => "maybe yup".into_string(),

			// Match an integer or a float.
			"i" i:int | "f" f:f64 => format!("i {} | f {}", i, f),

			// Match "identify", with the remainder of the input captured by "tail".
			"identify", ..tail => identify_banana(tail).into_string(),

			// Exit this loop.
			"exit" | "quit" => break,

			// If no other arm else matches, capture all the input into "tail".
			// You could also use `_` as the pattern, if you don't care about the
			// actual input.  Note that this will also swallow any errors caused
			// by previous arms not matching.
			//..tail => format!("wow {}", tail),
		};

		match res {
			Ok(value) => println!("Ok: {}", value),
			Err(err) => println!("Err: {}", err)
		}
	}
}

fn identify_banana<S: Str>(s: S) -> &'static str {
	(scan! {
		s,
		"banana" => "definitely a banana",
		"crowbar" => "giant metal banana",
		"human finger" => "tiny crunchy banana",
		_ => "probably not a banana"
	}).unwrap()
}
```
*/
#![feature(if_let)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(struct_variant)]

#[phase(plugin, link)] extern crate log;
extern crate rustc;
extern crate syntax;

extern crate scan_util;

pub mod parse;
pub mod plugin;

mod util;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut rustc::plugin::Registry) {
	reg.register_macro("scan", plugin::expand_scan);
	reg.register_macro("scanln", plugin::expand_scanln);
	reg.register_macro("scanln_from", plugin::expand_scanln_from);
	reg.register_macro("scanner", plugin::expand_scanner);
}
<commit_msg>Removed unwanted "pub" on modules.<commit_after>/*!
Pattern-based input scanning.

## Setup

First, place the following in your `Cargo.toml` file:

```{notrust}
[dependencies.scan]

git = "https://github.com/DanielKeep/rust-scan.git"

[dependencies.scan_util]

git = "https://github.com/DanielKeep/rust-scan-util.git"
```

If you are not using Cargo, you will need to download and compile both of these crates and make them available to `rustc`.  Note that the `scan` crate is *only* needed at compile time and does not need to be distributed.

Then, in your root module, add the following:

```{ignore}
#![feature(phase)]

#[phase(plugin)] extern crate scan;
extern crate scan_util;
```

Note that `scan_util` *must* be in the root module, and *must* not be renamed.  This is due to a limitation of macros in Rust.

## Usage

Two macros are provided for scanning text.  In general, both are concerned with taking a string and matching it against a sequence of patterns.  As soon as a matching pattern is found, the corresponding expression is evaluated and returned.

If an error occurs during an underlying IO operation *or* none of the provided patterns matched, an `Err(scan_util::ScanError)` will be returned.

The `scanln!` macro is used to scan a line of input from standard input.  For example:

```{ignore}
scanln! {
	"say hello" => println!("Hi!"),
	"exit" => return,
}
```

The `scan!` macro is used to scan a block of text.  The input value must implement the `Str` trait.  For example:

```{ignore}
let noun_to_identify = "wheelbarrow";
scan! {
	noun_to_identify,

	"banana" => "definitely a banana",
	"crowbar" => "big metal banana",
	_ => "probably not a banana at all",
}
```

## Patterns

A pattern is a space-separated sequence of one or more of the following:

### Text

These indicate a block of text which should be matched.  This matching is done by converting *both* text in the pattern *and* input text into a sequence of tokens, ignoring whitespace.  Each token is either a sequence of letters, a sequence of digits, or a single non-whitespace character.  All whitespace is ignored.

For example, the string `"Left4Dead is pretty fun..."` is tokenised into the sequence `"Left"`, `"4"`, `"Dead"`, `"is"`, `"pretty"`, `"fun"`, `"."`, `"."`, and `"."`.

This behaviour also means that `"ab cd ef"` and `"ab" "cd" "ef"` are semantically equivalent, but `"ab c" "d ef"` is different, because of the break between `c` and `d`.

Also note that text comparisons are *case insensitive*.

In future, the tokeniser, whitespace policy, and case insensitivity will be configurable.

### Capture

These are used to capture a value from the input.  They are written in the form `name: type` like a `let` binding.  The type constraint (`:type`) may be omitted *if* the usage of the captured value allows the type to be inferred.

For example, `age:int` will capture an integer and store it in a binding called `age`.  `thingy` will capture a value, the type of which is to be determined by inference.

An additional restriction is that you can only capture types which implement the `scan_util::Scanner` trait.  By default, scanners for the builtin integer and floating-point types are available as well as `bool` and `char`.  There is also a scanner for `&str` which will capture a single token.

If you wish to supply your own scanning logic for a type, you will need to wrap it in another type (such as a structure) and implement the `Scanner` trait.

### Alternate

A pattern written as `"a" | "b"` will match *either* `a` or `b`.  You can chain as many alternates as you want.

Note that alternates are tried in lexical order, and the first one to match "wins" and no others are tried.  For example, in the pattern `"a" | "a b"`, the second branch will never match.  Also note that as the scanner does not do backtracking, a failure to match later on in a pattern will not cause other alternatives to be tried.

All captures inside an alternate are wrapped in `Option`, with unmatched captures being `None`.

### Group

You can create a sub pattern using parentheses.  This is mainly used for grouping alternates.  For example: `"a" ("b" | "c") "d"`; this will match `a b d` or `a c d`.

### Repetition

Reptitions are used to match a repeating sequence, and optionally capture values from said sequence.  The general form is `[pattern] separator repeat`.

For example:

* `["pretty"]+ "please"` - matches a childish plea.

* `"rgb (" [cs:f32],{3} ")"` - matches a CSS `rgb` colour value.

* `[ns:int](ws:&str)+` - matches a sequence of at least one number separated by arbitrary words.

The `separator` is used to indicate what to match between instances of the main repeating `pattern`.  It can be omitted (in which case no seperator is scanned), or a sub pattern.  A sub pattern consisting of a single text literal can be written with the parentheses omitted.  If the text literal is `","`, `"."`, `";"`, or `":"`, then it may be written without the quotes as well.

The `repeat` indicates how many times the repeating `pattern` must appear.  It can be any one of:

- `?` - zero or one times.

- `*` - zero or more times.

- `+` - one or more times.

- `{n}` - exactly `n` times.

- `{n,}` - at least `n` times.

- `{,m}` - zero or more, but no more than `m`, times.

- `{n,m}` - at least `n`, but no more than `m`, times.

All captures inside a repetition are inserted into `Vec`s.  Nesting repetitions yield nested `Vec`s.

### Tail Capture

Patterns must match their *entire* input.  If you only wish to perform a prefix match, you must add a tail capture to the pattern.  For example, `"spake thee thus", ..the_rest` would match "spake thee thus" and then capture the remainder of the input into `the_rest`.  Note the comma to distinguish the tail capture from the pattern.  Also note that tail captures are `&str`s, limited to the lifetime of the input.

If you wish to do a prefix match, but do not actually care about the remainder of the input, you can use `_` as the capture name.

### Fallback Pattern

Finally, you can specify a fallback arm which *always* matches.  The complete pattern should be either a tail capture (such as `..tail` without any preceeding pattern), or a simple `_`.

## Example

Below is a complete example of various kinds of patterns.

```{ignore}
#![feature(phase)]

#[phase(plugin)] extern crate scan;
extern crate scan_util;

fn main() {
	loop {
		print!("input> ");

		let res = scanln! {
			// Match a single word.
			"hi" => "Hi!".into_string(),

			// Match a sequence of three tokens: "such", "input" and "!".
			"such input!" => "very syntax".into_string(),

			// Match *five* tokens: "and", "now", ".", "." and ".".
			"and now..." => "...for something completely different".into_string(),

			// Match "has" followed by an integer, captured into "value".
			"has" value:int => format!("ok has {}", value),

			// Alternative to the above.
			"has also" value => {
				let value: int = value;
				format!("ok has also {}", value)
			},

			// Match "v" followed by three numbers.
			"v" x:f64 y:f64 z:f64 => format!("({}, {}, {})", x, y, z),

			// Match "rgb" followed by three numbers.
			"rgb" r:u8 g:u8 b:u8 => format!("#{:02x}{:02x}{:02x}", r, g, b),

			// Match "rgb vec" followed by three or four numbers.
			"rgb vec" [rgb:u8],{3,4} => format!("rgba{}", rgb),

			// Match "vecs" followed by a comma-delimited vector of
			// comma-delimited vectors of floats.
			("vecs"|"vechs"|"vetches") [ "[" [vss:f64],* "]" ],* => format!("vss: {}", vss),

			// Match a vector of either an int or a word.
			"ints and words:" [is:int | ss:&str]* => format!("is: {}, ss: {}", is, ss),

			// Match a list of words separated by "and" or a comma.
			"words:" [words:&str]("and"|", and"|",")+ => format!("words: {}", words),

			// Match a list of numbers separated by words.
			"int words:" [is:int](ss:&str)* => format!("is: {}, ss: {}", is, ss),

			// Match "go" followed by a single word.
			"go" exit:&str => format!("Leaving by the {} exit.", exit),

			// Match "yes" or "no".
			"yes" | "no" => "maybe yup".into_string(),

			// Match an integer or a float.
			"i" i:int | "f" f:f64 => format!("i {} | f {}", i, f),

			// Match "identify", with the remainder of the input captured by "tail".
			"identify", ..tail => identify_banana(tail).into_string(),

			// Exit this loop.
			"exit" | "quit" => break,

			// If no other arm else matches, capture all the input into "tail".
			// You could also use `_` as the pattern, if you don't care about the
			// actual input.  Note that this will also swallow any errors caused
			// by previous arms not matching.
			//..tail => format!("wow {}", tail),
		};

		match res {
			Ok(value) => println!("Ok: {}", value),
			Err(err) => println!("Err: {}", err)
		}
	}
}

fn identify_banana<S: Str>(s: S) -> &'static str {
	(scan! {
		s,
		"banana" => "definitely a banana",
		"crowbar" => "giant metal banana",
		"human finger" => "tiny crunchy banana",
		_ => "probably not a banana"
	}).unwrap()
}
```
*/
#![feature(if_let)]
#![feature(phase)]
#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(struct_variant)]

#[phase(plugin, link)] extern crate log;
extern crate rustc;
extern crate syntax;

extern crate scan_util;

mod parse;
mod plugin;

mod util;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut rustc::plugin::Registry) {
	reg.register_macro("scan", plugin::expand_scan);
	reg.register_macro("scanln", plugin::expand_scanln);
	reg.register_macro("scanln_from", plugin::expand_scanln_from);
	reg.register_macro("scanner", plugin::expand_scanner);
}
<|endoftext|>


<commit_before>use std::mem;

// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4,
    TCP,
    UDP,
    DCCP,
    IP6,
    SCTP,
    UTP,
    UDT,
    IPFS,
    HTTP,
    HTTPS,
    ONION,
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        match *self {
            ProtocolTypes::IP4   => 4,
	    ProtocolTypes::TCP   => 6,
	    ProtocolTypes::UDP   => 17,
	    ProtocolTypes::DCCP  => 33,
	    ProtocolTypes::IP6   => 41,
	    ProtocolTypes::SCTP  => 132,
	    ProtocolTypes::UTP   => 301,
	    ProtocolTypes::UDT   => 302,
	    ProtocolTypes::IPFS  => 421,
	    ProtocolTypes::HTTP  => 480,
	    ProtocolTypes::HTTPS => 443,
	    ProtocolTypes::ONION => 444,
        }
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }

    // Try to convert a string to a protocol
    pub fn from_name(s: &str) -> Option<ProtocolTypes> {
        match s {
            "ip4"   => Some(ProtocolTypes::IP4),
	    "tcp"   => Some(ProtocolTypes::TCP),
	    "udp"   => Some(ProtocolTypes::UDP),
	    "dccp"  => Some(ProtocolTypes::DCCP),
	    "ip6"   => Some(ProtocolTypes::IP6),
	    "sctp"  => Some(ProtocolTypes::SCTP),
	    "utp"   => Some(ProtocolTypes::UTP),
	    "udt"   => Some(ProtocolTypes::UDT),
	    "ipfs"  => Some(ProtocolTypes::IPFS),
	    "http"  => Some(ProtocolTypes::HTTP),
	    "https" => Some(ProtocolTypes::HTTPS),
	    "onion" => Some(ProtocolTypes::ONION),
            _ => None
        }
    }
}

pub struct Multiaddr {
    bytes: Vec<u8>
}

impl Multiaddr {
    /// Create a new multiaddr based on a string representation, like
    /// `/ip4/127.0.0.1/udp/1234`.
    pub fn new(address: &str) -> Multiaddr {
        let address = address.to_string();
        let mut bytes: Vec<u8>= vec![];

        for part in address.split("/") {
            if let Some(protocol) = ProtocolTypes::from_name(part) {
                // Is there a way to do this safely?
                unsafe {
                    let code_u16 = protocol.to_code();
                    let code = mem::transmute::<u16, [u8; 2]>(code_u16);

                    if code_u16 > 255 {
                        let mut code = code.iter().cloned().collect();
                        bytes.append(&mut code);
                    } else {
                        bytes.push(code[0])
                    }
                }

                println!("Got protocol {}", protocol.to_name());
                println!("With size {}", protocol.to_size());
            } else if part.len() > 0 {
                let mut part_bytes = part.to_string().into_bytes();
                bytes.append(&mut part_bytes);
                println!("got part {}, {}", part.len(), part);
            }
        }

        Multiaddr {
            bytes: bytes,
        }
    }
}

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

    #[test]
    fn protocols_to_u16() {
        assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
    }

    #[test]
    fn protocols_from_u16() {
        assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
        assert_eq!(ProtocolTypes::from_code(455u16), None);
    }

    #[test]
    fn protocols_to_size() {
        assert_eq!(ProtocolTypes::TCP.to_size(), 16);
    }

    #[test]
    fn protocols_to_name() {
        assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
    }

    #[test]
    fn multiaddr_from_string() {
        let mut target = vec![4u8];
        target.append(&mut "127.0.0.1".to_string().into_bytes());
        target.push(17u8);
        target.append(&mut "1234".to_string().into_bytes());

        assert_eq!(
            Multiaddr::new("/ip4/127.0.0.1/udp/1234").bytes,
            target
         );
    }
}
<commit_msg>Implement equality<commit_after>use std::mem;
use std::cmp::PartialEq;

// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4,
    TCP,
    UDP,
    DCCP,
    IP6,
    SCTP,
    UTP,
    UDT,
    IPFS,
    HTTP,
    HTTPS,
    ONION,
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        match *self {
            ProtocolTypes::IP4   => 4,
	    ProtocolTypes::TCP   => 6,
	    ProtocolTypes::UDP   => 17,
	    ProtocolTypes::DCCP  => 33,
	    ProtocolTypes::IP6   => 41,
	    ProtocolTypes::SCTP  => 132,
	    ProtocolTypes::UTP   => 301,
	    ProtocolTypes::UDT   => 302,
	    ProtocolTypes::IPFS  => 421,
	    ProtocolTypes::HTTP  => 480,
	    ProtocolTypes::HTTPS => 443,
	    ProtocolTypes::ONION => 444,
        }
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }

    // Try to convert a string to a protocol
    pub fn from_name(s: &str) -> Option<ProtocolTypes> {
        match s {
            "ip4"   => Some(ProtocolTypes::IP4),
	    "tcp"   => Some(ProtocolTypes::TCP),
	    "udp"   => Some(ProtocolTypes::UDP),
	    "dccp"  => Some(ProtocolTypes::DCCP),
	    "ip6"   => Some(ProtocolTypes::IP6),
	    "sctp"  => Some(ProtocolTypes::SCTP),
	    "utp"   => Some(ProtocolTypes::UTP),
	    "udt"   => Some(ProtocolTypes::UDT),
	    "ipfs"  => Some(ProtocolTypes::IPFS),
	    "http"  => Some(ProtocolTypes::HTTP),
	    "https" => Some(ProtocolTypes::HTTPS),
	    "onion" => Some(ProtocolTypes::ONION),
            _ => None
        }
    }
}

pub struct Multiaddr {
    bytes: Vec<u8>
}

impl Multiaddr {
    /// Create a new multiaddr based on a string representation, like
    /// `/ip4/127.0.0.1/udp/1234`.
    pub fn new(address: &str) -> Multiaddr {
        let address = address.to_string();
        let mut bytes: Vec<u8>= vec![];

        for part in address.split("/") {
            if let Some(protocol) = ProtocolTypes::from_name(part) {
                // Is there a way to do this safely?
                unsafe {
                    let code_u16 = protocol.to_code();
                    let code = mem::transmute::<u16, [u8; 2]>(code_u16);

                    if code_u16 > 255 {
                        let mut code = code.iter().cloned().collect();
                        bytes.append(&mut code);
                    } else {
                        bytes.push(code[0])
                    }
                }

                println!("Got protocol {}", protocol.to_name());
                println!("With size {}", protocol.to_size());
            } else if part.len() > 0 {
                let mut part_bytes = part.to_string().into_bytes();
                bytes.append(&mut part_bytes);
                println!("got part {}, {}", part.len(), part);
            }
        }

        Multiaddr {
            bytes: bytes,
        }
    }
}

impl PartialEq for Multiaddr {
    fn eq(&self, other: &Multiaddr) -> bool {
        self.bytes == other.bytes
    }
}

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

    #[test]
    fn protocols_to_u16() {
        assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
    }

    #[test]
    fn protocols_from_u16() {
        assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
        assert_eq!(ProtocolTypes::from_code(455u16), None);
    }

    #[test]
    fn protocols_to_size() {
        assert_eq!(ProtocolTypes::TCP.to_size(), 16);
    }

    #[test]
    fn protocols_to_name() {
        assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
    }

    #[test]
    fn multiaddr_from_string() {
        let mut target = vec![4u8];
        target.append(&mut "127.0.0.1".to_string().into_bytes());
        target.push(17u8);
        target.append(&mut "1234".to_string().into_bytes());

        assert_eq!(
            Multiaddr::new("/ip4/127.0.0.1/udp/1234").bytes,
            target
         );
    }

    #[test]
    fn multiaddr_eq() {
        assert!(Multiaddr::new("/ip4/127.0.0.1") == Multiaddr::new("/ip4/127.0.0.1"));
        assert!(Multiaddr::new("/ip4/127.0.0.1") != Multiaddr::new("/ip4/128.0.0.1"))
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>trait AtomicCounter, impl for AtomicSize, tests.<commit_after>
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;

trait AtomicCounter {
    type PrimitiveType;

    fn inc(&self);
    fn add(&self, amount: Self::PrimitiveType);
    fn reset(&self) -> Self::PrimitiveType;
}

impl AtomicCounter for AtomicUsize {
    type PrimitiveType = usize;

    fn inc(&self) {
        self.add(1)
    }

    fn add(&self, amount: usize) {
        self.fetch_add(amount, Relaxed);
    }

    fn reset(&self) -> usize {
        let mut last_value = 0;
        loop {
            let ret = self.compare_and_swap(last_value, 0, Relaxed);
            if ret == last_value {
                break;
            }
            last_value = ret;
        }
        last_value
    }
}

#[cfg(test)]
mod tests {

    use std::thread;
    use std::sync::Arc;
    use std::ops::Deref;

    use super::*;

    const NUM_THREADS: usize = 29;
    const NUM_ITERATIONS: usize = 7_000_000;

    #[test]
    fn test_inc() {
        let mut join_handles = Vec::new();
        let counter = Arc::new(AtomicUsize::new(0));
        println!("test_inc: Spawning {} threads, each with {} iterations...", NUM_THREADS, NUM_ITERATIONS);
        for _ in 0..NUM_THREADS {
            let counter_ref = counter.clone();
            join_handles.push(thread::spawn(move || {
                //make sure we're not going though Arc on each iteration
                let counter: &AtomicUsize = counter_ref.deref();
                for _ in 0..NUM_ITERATIONS {
                    counter.inc();
                }
            }));
        }
        for handle in join_handles {
            handle.join().unwrap();
        }
        let count = counter.reset();
        println!("test_inc: Got count: {}", count);
        assert_eq!(NUM_THREADS * NUM_ITERATIONS, count);
    }

    #[test]
    fn test_add() {
        let mut join_handles = Vec::new();
        let counter = Arc::new(AtomicUsize::new(0));
        println!("test_add: Spawning {} threads, each with {} iterations...", NUM_THREADS, NUM_ITERATIONS);
        let mut expected_count = 0;
        for to_add in 0..NUM_THREADS {
            let counter_ref = counter.clone();
            expected_count += to_add * NUM_ITERATIONS;
            join_handles.push(thread::spawn(move || {
                //make sure we're not going though Arc on each iteration
                let counter: &AtomicUsize = counter_ref.deref();
                for _ in 0..NUM_ITERATIONS {
                    counter.add(to_add);
                }
            }));
        }
        for handle in join_handles {
            handle.join().unwrap();
        }
        let count = counter.reset();
        println!("test_add: Expected count: {}, got count: {}", expected_count, count);
        assert_eq!(expected_count, count);
    }

    #[test]
    fn test_add_reset() {
        let mut join_handles = Vec::new();
        let counter = Arc::new(AtomicUsize::new(0));
        println!("test_add_reset: Spawning {} threads, each with {} iterations...", NUM_THREADS, NUM_ITERATIONS);
        let mut expected_count = 0;
        for to_add in 0..NUM_THREADS {
            expected_count += to_add * NUM_ITERATIONS;
        }

        // setup thread that `reset()`s all the time
        let counter_ref = counter.clone();
        let reset_handle = thread::spawn(move || {
            // Usually, you would check for some better termination condition.
            // I don't want to pollute my test with thread synchronization
            // operations outside of AtomicCounter, hence this approach.
            let mut total_count = 0;
            let counter: &AtomicUsize = counter_ref.deref();
            while total_count < expected_count {
                total_count += counter.reset();
            }
            // Ok, now we got the total_count but this could just be lucky.
            // Better do some more resets to be sure... ;)
            for _ in 0..NUM_ITERATIONS {
                total_count += counter.reset();
            }
            total_count
        });

        for to_add in 0..NUM_THREADS {
            let counter_ref = counter.clone();

            join_handles.push(thread::spawn(move || {
                //make sure we're not going though Arc on each iteration
                let counter: &AtomicUsize = counter_ref.deref();
                for _ in 0..NUM_ITERATIONS {
                    counter.add(to_add);
                }
            }));
        }
        for handle in join_handles {
            handle.join().unwrap();
        }
        let actual_count = reset_handle.join().unwrap();
        println!("test_add_reset: Expected count: {}, got count: {}", expected_count, actual_count);
        assert_eq!(expected_count, actual_count);
    }

}
<|endoftext|>


<commit_before>extern crate expression;
#[cfg(test)]
extern crate expression_num;

pub mod parametric;

use std::fmt::Debug;

/// Used to name symbols and variables.
pub trait Alphabet: Debug + PartialEq + Eq + Clone {}

impl Alphabet for &'static str {}
impl Alphabet for char {}
impl Alphabet for u8 {}
impl Alphabet for u16 {}
impl Alphabet for u32 {}
impl Alphabet for u64 {}
impl Alphabet for usize {}
<commit_msg>Add DualAlphabet trait<commit_after>extern crate expression;
#[cfg(test)]
extern crate expression_num;

pub mod parametric;

use std::fmt::Debug;

/// Used to name symbols and variables.
pub trait Alphabet: Debug + PartialEq + Eq + Clone {}

impl Alphabet for &'static str {}
impl Alphabet for char {}
impl Alphabet for u8 {}
impl Alphabet for u16 {}
impl Alphabet for u32 {}
impl Alphabet for u64 {}
impl Alphabet for usize {}

/// An alphabet that distinguishes between terminal
/// and non-terminal symbols.
pub trait DualAlphabet: Alphabet {
    type Terminal: Alphabet;
    type NonTerminal: Alphabet;

    fn nonterminal(&self) -> Option<Self::NonTerminal>;
    fn terminal(&self) -> Option<Self::Terminal>;
}
<|endoftext|>


<commit_before>pub fn distance(input: &str) -> u32 {
    let mut chars = input.chars();
    if let (Some(turn_dir), Some(num_blocks)) = (chars.next(), chars.next()) {
        let num_blocks: u32 = num_blocks.to_string()
                                        .parse()
                                        .expect("Cannot parse");
        return num_blocks
    }
    panic!("oh no!");
}

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

    #[test]
    fn right_one_is_one() {
        assert_eq!(distance("R1"), 1);
    }

    #[test]
    fn r1_r1_is_two() {
        assert_eq!(distance("R1 R1"), 2);
    }

}<commit_msg>Handle turning<commit_after>#[derive(PartialEq, Debug)]
pub enum Direction {
    North,
    East,
    South,
    West,
}

pub fn distance(input: &str) -> u32 {
    let mut chars = input.chars();
    if let (Some(_turn_dir), Some(num_blocks)) = (chars.next(), chars.next()) {
        let num_blocks: u32 = num_blocks.to_string()
                                        .parse()
                                        .expect("Cannot parse");
        return num_blocks
    }
    panic!("oh no!");
}

pub fn turn(initial_direction: Direction, turn_command: char) -> Direction {
    match (turn_command, initial_direction) {
        ('L', Direction::North) => Direction::West,
        ('L', Direction::West) => Direction::South,
        ('L', Direction::South) => Direction::East,
        ('L', Direction::East) => Direction::North,

        ('R', Direction::North) => Direction::East,
        ('R', Direction::East) => Direction::South,
        ('R', Direction::South) => Direction::West,
        ('R', Direction::West) => Direction::North,

        (unknown_turn_command, dir) => panic!("\
            I don't know how to turn {} from {:?}!",
            unknown_turn_command,
            dir
        ),
    }
}

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

    #[test]
    fn right_one_is_one() {
        assert_eq!(distance("R1"), 1);
    }

    #[test]
    fn turn_left() {
        let mut dir = Direction::North;
        dir = turn(dir, 'L');
        assert_eq!(dir, Direction::West);
        dir = turn(dir, 'L');
        assert_eq!(dir, Direction::South);
        dir = turn(dir, 'L');
        assert_eq!(dir, Direction::East);
        dir = turn(dir, 'L');
        assert_eq!(dir, Direction::North);
    }

    #[test]
    fn turn_right() {
        let mut dir = Direction::North;
        dir = turn(dir, 'R');
        assert_eq!(dir, Direction::East);
        dir = turn(dir, 'R');
        assert_eq!(dir, Direction::South);
        dir = turn(dir, 'R');
        assert_eq!(dir, Direction::West);
        dir = turn(dir, 'R');
        assert_eq!(dir, Direction::North);
    }
}<|endoftext|>


<commit_before>// Inspired by http://python-gitlab.readthedocs.io/en/stable/

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;
extern crate serde_urlencoded;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

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

pub mod gitlab;
pub mod groups;
pub mod projects;
pub mod issues;
pub mod merge_requests;

// Re-export those structs
pub use gitlab::Pagination;
pub use gitlab::GitLab;
// pub use projects::Project;

trait BuildQuery {
    fn build_query(&self) -> String;
}

#[cfg(test)]
mod tests {
    // use gitlab::GitLab;
    // use hyper;

    // #[test]
    // fn unauthorized() {
    //     let gl = GitLab::new("http", "gitlab.com", 80, "XXXXXXXXXXXXX");
    //     println!("gl: {:?}", gl);
    //     assert_eq!(gl.attempt_connection().unwrap().status,
    //                hyper::status::StatusCode::Unauthorized);
    // }
}
<commit_msg>Use crate<commit_after>// Inspired by http://python-gitlab.readthedocs.io/en/stable/

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;
extern crate serde_urlencoded;

#[macro_use]
extern crate error_chain;

#[cfg(feature = "serde_derive")]
include!("serde_types.in.rs");

#[cfg(feature = "serde_codegen")]
include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

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

pub mod gitlab;
pub mod groups;
pub mod projects;
pub mod issues;
pub mod merge_requests;

// Re-export those structs
pub use gitlab::Pagination;
pub use gitlab::GitLab;
// pub use projects::Project;

trait BuildQuery {
    fn build_query(&self) -> String;
}

#[cfg(test)]
mod tests {
    // use gitlab::GitLab;
    // use hyper;

    // #[test]
    // fn unauthorized() {
    //     let gl = GitLab::new("http", "gitlab.com", 80, "XXXXXXXXXXXXX");
    //     println!("gl: {:?}", gl);
    //     assert_eq!(gl.attempt_connection().unwrap().status,
    //                hyper::status::StatusCode::Unauthorized);
    // }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_char, c_int, c_void};

#[repr(C)]
#[derive(Copy)]
pub struct PamHandle;

#[repr(C)]
#[derive(Copy)]
pub struct PamConv {
    pub conv: *const c_void,
    pub data_ptr: *mut c_void
}

extern "C" {
    /* --------------             pam_appl.h            ------------- */
    /* -------------- The Linux-PAM Framework layer API ------------- */

    pub fn pam_start(service_name: *const c_char, user: *const c_char,
        pam_conversation: *const PamConv, pamh: *mut *const PamHandle) -> c_int;

    pub fn pam_end(pamh: *const PamHandle, pam_status: c_int) -> c_int;

    /* Authentication API's */
    pub fn pam_authenticate(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_setcred(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Account Management API's */
    pub fn pam_acct_mgmt(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Session Management API's */
    pub fn pam_open_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_close_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Password Management API's */
    pub fn pam_chauthtok(pamh: *mut PamHandle, flags: c_int) -> c_int;
}
<commit_msg>Test signature change<commit_after>extern crate libc;

use libc::{c_char, c_int, c_void};

#[repr(C)]
#[derive(Copy)]
pub struct PamHandle;

#[repr(C)]
#[derive(Copy)]
pub struct PamConv {
    pub conv: *const c_void,
    pub data_ptr: *mut c_void
}

extern "C" {
    /* --------------             pam_appl.h            ------------- */
    /* -------------- The Linux-PAM Framework layer API ------------- */

    pub fn pam_start(service_name: *const c_char, user: *const c_char,
        pam_conversation: *const PamConv, pamh: *const *mut PamHandle) -> c_int;

    pub fn pam_end(pamh: *mut PamHandle, pam_status: c_int) -> c_int;

    /* Authentication API's */
    pub fn pam_authenticate(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_setcred(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Account Management API's */
    pub fn pam_acct_mgmt(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Session Management API's */
    pub fn pam_open_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_close_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Password Management API's */
    pub fn pam_chauthtok(pamh: *mut PamHandle, flags: c_int) -> c_int;
}
<|endoftext|>


<commit_before>#![feature(advanced_slice_patterns, slice_patterns)]
#![recursion_limit = "1024"]
#[macro_use]

extern crate error_chain;
extern crate git2;

pub mod author;
pub mod config;
pub mod errors;

use std::collections::HashMap;
use std::env;
use std::process::Command;

use author::{Author, AuthorParser};
use config::{Config, GitConfig, NamespacedConfig};
use errors::*;

pub struct GitTogether<C> {
  config: C,
  author_parser: AuthorParser,
}

impl GitTogether<NamespacedConfig<GitConfig>> {
  pub fn new(namespace: &str) -> Result<Self> {
    let mut config = Self::open_config()?;
    Self::auto_include(&mut config, namespace);

    let config = GitConfig { config: config };
    let config = NamespacedConfig::new(namespace, config);
    let domain = config.get("domain")?;
    let author_parser = AuthorParser { domain: domain };

    Ok(GitTogether {
      config: config,
      author_parser: author_parser,
    })
  }

  fn open_config() -> Result<git2::Config> {
    Self::open_repo()
      .and_then(|repo| repo.config().chain_err(|| ""))
      .or_else(|_| git2::Config::open_default())
      .chain_err(|| "")
  }

  fn auto_include(config: &mut git2::Config, namespace: &str) {
    let filename = format!(".{}", namespace);
    let include_path = format!("../{}", filename);

    let repo = match Self::open_repo() {
      Ok(repo) => repo,
      Err(_) => {
        return;
      }
    };

    let file_exists = repo.workdir().map(|path| {
      let mut path_buf = path.to_path_buf();
      path_buf.push(&filename);
      path_buf.exists()
    });

    // Make sure .git-together exists
    if !file_exists.unwrap_or(false) {
      return;
    }

    if Self::already_included(config, &include_path).unwrap_or(true) {
      return;
    }

    let _ = config.set_multivar("include.path", "^$", &include_path);
  }

  fn already_included(config: &git2::Config,
                      include_path: &str)
                      -> Result<bool> {
    let local_config = config.open_level(git2::ConfigLevel::Local)
      .chain_err(|| "error opening local git config")?;
    let entries = local_config.entries(None)
      .chain_err(|| "error getting git config entries")?;
    Ok(entries.into_iter().any(|entry| {
      entry.map(|entry| entry.value() == Some(include_path)).unwrap_or(true)
    }))
  }

  fn open_repo() -> Result<git2::Repository> {
    let path = env::current_dir().chain_err(|| "")?;
    git2::Repository::discover(path).chain_err(|| "")
  }
}

impl<C: Config> GitTogether<C> {
  pub fn set_active(&mut self, inits: &[&str]) -> Result<Vec<Author>> {
    let authors = self.get_authors(inits)?;
    self.config.set("active", &inits.join("+"))?;
    Ok(authors)
  }

  pub fn all_authors(&self) -> Result<HashMap<String, Author>> {
    let mut authors = HashMap::new();
    let raw = self.config.get_all("authors.")?;
    for (name, value) in raw {
      let initials = name.split('.').last().ok_or("")?;
      let author = self.parse_author(initials, &value)?;
      authors.insert(initials.into(), author);
    }
    Ok(authors)
  }

  pub fn signoff<'a>(&self, cmd: &'a mut Command) -> Result<&'a mut Command> {
    let active = self.config.get("active")?;
    let inits: Vec<_> = active.split('+').collect();
    let authors = self.get_authors(&inits)?;

    let (author, committer) = match *authors.as_slice() {
      [] => {
        return Err("".into());
      }
      [ref solo] => (solo, solo),
      [ref author, ref committer, _..] => (author, committer),
    };

    let cmd = cmd.env("GIT_AUTHOR_NAME", author.name.clone())
      .env("GIT_AUTHOR_EMAIL", author.email.clone())
      .env("GIT_COMMITTER_NAME", committer.name.clone())
      .env("GIT_COMMITTER_EMAIL", committer.email.clone());

    let no_signoff = env::var("GIT_TOGETHER_NO_SIGNOFF").is_ok();
    let cmd = if !no_signoff && author != committer {
      cmd.arg("--signoff")
    } else {
      cmd
    };

    Ok(cmd)
  }

  fn get_active(&self) -> Result<Vec<String>> {
    self.config
      .get("active")
      .map(|active| active.split('+').map(|s| s.into()).collect())
  }

  pub fn rotate_active(&mut self) -> Result<()> {
    self.get_active().and_then(|active| {
      let mut inits: Vec<_> = active.iter().map(String::as_ref).collect();
      if !inits.is_empty() {
        let author = inits.remove(0);
        inits.push(author);
      }
      self.set_active(&inits[..]).map(|_| ())
    })
  }

  fn get_authors(&self, inits: &[&str]) -> Result<Vec<Author>> {
    inits.iter()
      .map(|&initials| self.get_author(initials))
      .collect()
  }

  fn get_author(&self, initials: &str) -> Result<Author> {
    self.config
      .get(&format!("authors.{}", initials))
      .chain_err(|| format!("author not found for '{}'", initials))
      .and_then(|raw| self.parse_author(initials, &raw))
  }

  fn parse_author(&self, initials: &str, raw: &str) -> Result<Author> {
    self.author_parser
      .parse(raw)
      .ok_or(format!("invalid author for '{}': '{}'", initials, raw).into())
  }
}

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

  use std::collections::HashMap;

  use author::{Author, AuthorParser};
  use config::Config;
  use errors::*;

  #[test]
  fn get_authors() {
    let config =
      MockConfig::new(&[("authors.jh", ""),
                        ("authors.nn", "Naomi Nagata"),
                        ("authors.ab", "Amos Burton; aburton"),
                        ("authors.ak", "Alex Kamal; akamal"),
                        ("authors.ca", "Chrisjen Avasarala;"),
                        ("authors.bd", "Bobbie Draper; bdraper@mars.mil"),
                        ("authors.jm", "Joe Miller; jmiller@starhelix.com")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    assert!(gt.get_authors(&["jh"]).is_err());
    assert!(gt.get_authors(&["nn"]).is_err());
    assert!(gt.get_authors(&["ca"]).is_err());
    assert!(gt.get_authors(&["jh", "bd"]).is_err());

    assert_eq!(gt.get_authors(&["ab", "ak"]).unwrap(),
               vec![Author {
                      name: "Amos Burton".into(),
                      email: "aburton@rocinante.com".into(),
                    },
                    Author {
                      name: "Alex Kamal".into(),
                      email: "akamal@rocinante.com".into(),
                    }]);
    assert_eq!(gt.get_authors(&["ab", "bd", "jm"]).unwrap(),
               vec![Author {
                      name: "Amos Burton".into(),
                      email: "aburton@rocinante.com".into(),
                    },
                    Author {
                      name: "Bobbie Draper".into(),
                      email: "bdraper@mars.mil".into(),
                    },
                    Author {
                      name: "Joe Miller".into(),
                      email: "jmiller@starhelix.com".into(),
                    }]);
  }

  #[test]
  fn set_active() {
    let config = MockConfig::new(&[("authors.jh", "James Holden; jholden"),
                                   ("authors.nn", "Naomi Nagata; nnagata")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let mut gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    gt.set_active(&["jh"]).unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["jh"]);

    gt.set_active(&["jh", "nn"]).unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["jh", "nn"]);
  }

  #[test]
  fn rotate_active() {
    let config = MockConfig::new(&[("active", "jh+nn"),
                                   ("authors.jh", "James Holden; jholden"),
                                   ("authors.nn", "Naomi Nagata; nnagata")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let mut gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    gt.rotate_active().unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["nn", "jh"]);
  }

  #[test]
  fn all_authors() {
    let config =
      MockConfig::new(&[("active", "jh+nn"),
                        ("authors.ab", "Amos Burton; aburton"),
                        ("authors.bd", "Bobbie Draper; bdraper@mars.mil"),
                        ("authors.jm", "Joe Miller; jmiller@starhelix.com")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    let all_authors = gt.all_authors().unwrap();
    assert_eq!(all_authors.len(), 3);
    assert_eq!(all_authors["ab"],
               Author {
                 name: "Amos Burton".into(),
                 email: "aburton@rocinante.com".into(),
               });
    assert_eq!(all_authors["bd"],
               Author {
                 name: "Bobbie Draper".into(),
                 email: "bdraper@mars.mil".into(),
               });
    assert_eq!(all_authors["jm"],
               Author {
                 name: "Joe Miller".into(),
                 email: "jmiller@starhelix.com".into(),
               });
  }

  struct MockConfig {
    data: HashMap<String, String>,
  }

  impl MockConfig {
    fn new(data: &[(&str, &str)]) -> MockConfig {
      MockConfig {
        data: data.iter().map(|&(k, v)| (k.into(), v.into())).collect(),
      }
    }
  }

  impl Config for MockConfig {
    fn get(&self, name: &str) -> Result<String> {
      self.data
        .get(name.into())
        .cloned()
        .ok_or(format!("name not found: '{}'", name).into())
    }

    fn get_all(&self, glob: &str) -> Result<HashMap<String, String>> {
      Ok(self.data
        .iter()
        .filter(|&(name, _)| name.contains(glob))
        .map(|(name, value)| (name.clone(), value.clone()))
        .collect())
    }

    fn add(&mut self, _: &str, _: &str) -> Result<()> {
      unimplemented!();
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
      self.data.insert(name.into(), value.into());
      Ok(())
    }
  }
}
<commit_msg>better error messages<commit_after>#![feature(advanced_slice_patterns, slice_patterns)]
#![recursion_limit = "1024"]
#[macro_use]

extern crate error_chain;
extern crate git2;

pub mod author;
pub mod config;
pub mod errors;

use std::collections::HashMap;
use std::env;
use std::process::Command;

use author::{Author, AuthorParser};
use config::{Config, GitConfig, NamespacedConfig};
use errors::*;

pub struct GitTogether<C> {
  config: C,
  author_parser: AuthorParser,
}

impl GitTogether<NamespacedConfig<GitConfig>> {
  pub fn new(namespace: &str) -> Result<Self> {
    let mut config = Self::open_config()?;
    Self::auto_include(&mut config, namespace);

    let config = GitConfig { config: config };
    let config = NamespacedConfig::new(namespace, config);
    let domain = config.get("domain")?;
    let author_parser = AuthorParser { domain: domain };

    Ok(GitTogether {
      config: config,
      author_parser: author_parser,
    })
  }

  fn open_config() -> Result<git2::Config> {
    Self::open_repo()
      .and_then(|repo| repo.config().chain_err(|| "error getting git config from repo"))
      .or_else(|_| git2::Config::open_default())
      .chain_err(|| "error opening git config")
  }

  fn auto_include(config: &mut git2::Config, namespace: &str) {
    let filename = format!(".{}", namespace);
    let include_path = format!("../{}", filename);

    let repo = match Self::open_repo() {
      Ok(repo) => repo,
      Err(_) => {
        return;
      }
    };

    let file_exists = repo.workdir().map(|path| {
      let mut path_buf = path.to_path_buf();
      path_buf.push(&filename);
      path_buf.exists()
    });

    // Make sure .git-together exists
    if !file_exists.unwrap_or(false) {
      return;
    }

    if Self::already_included(config, &include_path).unwrap_or(true) {
      return;
    }

    let _ = config.set_multivar("include.path", "^$", &include_path);
  }

  fn already_included(config: &git2::Config,
                      include_path: &str)
                      -> Result<bool> {
    let local_config = config.open_level(git2::ConfigLevel::Local)
      .chain_err(|| "error opening local git config")?;
    let entries = local_config.entries(None)
      .chain_err(|| "error getting git config entries")?;
    Ok(entries.into_iter().any(|entry| {
      entry.map(|entry| entry.value() == Some(include_path)).unwrap_or(true)
    }))
  }

  fn open_repo() -> Result<git2::Repository> {
    let path = env::current_dir().chain_err(|| "error getting current directory")?;
    git2::Repository::discover(path).chain_err(|| "error discovering git repo")
  }
}

impl<C: Config> GitTogether<C> {
  pub fn set_active(&mut self, inits: &[&str]) -> Result<Vec<Author>> {
    let authors = self.get_authors(inits)?;
    self.config.set("active", &inits.join("+"))?;
    Ok(authors)
  }

  pub fn all_authors(&self) -> Result<HashMap<String, Author>> {
    let mut authors = HashMap::new();
    let raw = self.config.get_all("authors.")?;
    for (name, value) in raw {
      let initials = name.split('.').last().ok_or("")?;
      let author = self.parse_author(initials, &value)?;
      authors.insert(initials.into(), author);
    }
    Ok(authors)
  }

  pub fn signoff<'a>(&self, cmd: &'a mut Command) -> Result<&'a mut Command> {
    let active = self.config.get("active")?;
    let inits: Vec<_> = active.split('+').collect();
    let authors = self.get_authors(&inits)?;

    let (author, committer) = match *authors.as_slice() {
      [] => {
        return Err("".into());
      }
      [ref solo] => (solo, solo),
      [ref author, ref committer, _..] => (author, committer),
    };

    let cmd = cmd.env("GIT_AUTHOR_NAME", author.name.clone())
      .env("GIT_AUTHOR_EMAIL", author.email.clone())
      .env("GIT_COMMITTER_NAME", committer.name.clone())
      .env("GIT_COMMITTER_EMAIL", committer.email.clone());

    let no_signoff = env::var("GIT_TOGETHER_NO_SIGNOFF").is_ok();
    let cmd = if !no_signoff && author != committer {
      cmd.arg("--signoff")
    } else {
      cmd
    };

    Ok(cmd)
  }

  fn get_active(&self) -> Result<Vec<String>> {
    self.config
      .get("active")
      .map(|active| active.split('+').map(|s| s.into()).collect())
  }

  pub fn rotate_active(&mut self) -> Result<()> {
    self.get_active().and_then(|active| {
      let mut inits: Vec<_> = active.iter().map(String::as_ref).collect();
      if !inits.is_empty() {
        let author = inits.remove(0);
        inits.push(author);
      }
      self.set_active(&inits[..]).map(|_| ())
    })
  }

  fn get_authors(&self, inits: &[&str]) -> Result<Vec<Author>> {
    inits.iter()
      .map(|&initials| self.get_author(initials))
      .collect()
  }

  fn get_author(&self, initials: &str) -> Result<Author> {
    self.config
      .get(&format!("authors.{}", initials))
      .chain_err(|| format!("author not found for '{}'", initials))
      .and_then(|raw| self.parse_author(initials, &raw))
  }

  fn parse_author(&self, initials: &str, raw: &str) -> Result<Author> {
    self.author_parser
      .parse(raw)
      .ok_or(format!("invalid author for '{}': '{}'", initials, raw).into())
  }
}

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

  use std::collections::HashMap;

  use author::{Author, AuthorParser};
  use config::Config;
  use errors::*;

  #[test]
  fn get_authors() {
    let config =
      MockConfig::new(&[("authors.jh", ""),
                        ("authors.nn", "Naomi Nagata"),
                        ("authors.ab", "Amos Burton; aburton"),
                        ("authors.ak", "Alex Kamal; akamal"),
                        ("authors.ca", "Chrisjen Avasarala;"),
                        ("authors.bd", "Bobbie Draper; bdraper@mars.mil"),
                        ("authors.jm", "Joe Miller; jmiller@starhelix.com")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    assert!(gt.get_authors(&["jh"]).is_err());
    assert!(gt.get_authors(&["nn"]).is_err());
    assert!(gt.get_authors(&["ca"]).is_err());
    assert!(gt.get_authors(&["jh", "bd"]).is_err());

    assert_eq!(gt.get_authors(&["ab", "ak"]).unwrap(),
               vec![Author {
                      name: "Amos Burton".into(),
                      email: "aburton@rocinante.com".into(),
                    },
                    Author {
                      name: "Alex Kamal".into(),
                      email: "akamal@rocinante.com".into(),
                    }]);
    assert_eq!(gt.get_authors(&["ab", "bd", "jm"]).unwrap(),
               vec![Author {
                      name: "Amos Burton".into(),
                      email: "aburton@rocinante.com".into(),
                    },
                    Author {
                      name: "Bobbie Draper".into(),
                      email: "bdraper@mars.mil".into(),
                    },
                    Author {
                      name: "Joe Miller".into(),
                      email: "jmiller@starhelix.com".into(),
                    }]);
  }

  #[test]
  fn set_active() {
    let config = MockConfig::new(&[("authors.jh", "James Holden; jholden"),
                                   ("authors.nn", "Naomi Nagata; nnagata")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let mut gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    gt.set_active(&["jh"]).unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["jh"]);

    gt.set_active(&["jh", "nn"]).unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["jh", "nn"]);
  }

  #[test]
  fn rotate_active() {
    let config = MockConfig::new(&[("active", "jh+nn"),
                                   ("authors.jh", "James Holden; jholden"),
                                   ("authors.nn", "Naomi Nagata; nnagata")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let mut gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    gt.rotate_active().unwrap();
    assert_eq!(gt.get_active().unwrap(), vec!["nn", "jh"]);
  }

  #[test]
  fn all_authors() {
    let config =
      MockConfig::new(&[("active", "jh+nn"),
                        ("authors.ab", "Amos Burton; aburton"),
                        ("authors.bd", "Bobbie Draper; bdraper@mars.mil"),
                        ("authors.jm", "Joe Miller; jmiller@starhelix.com")]);
    let author_parser = AuthorParser { domain: "rocinante.com".into() };
    let gt = GitTogether {
      config: config,
      author_parser: author_parser,
    };

    let all_authors = gt.all_authors().unwrap();
    assert_eq!(all_authors.len(), 3);
    assert_eq!(all_authors["ab"],
               Author {
                 name: "Amos Burton".into(),
                 email: "aburton@rocinante.com".into(),
               });
    assert_eq!(all_authors["bd"],
               Author {
                 name: "Bobbie Draper".into(),
                 email: "bdraper@mars.mil".into(),
               });
    assert_eq!(all_authors["jm"],
               Author {
                 name: "Joe Miller".into(),
                 email: "jmiller@starhelix.com".into(),
               });
  }

  struct MockConfig {
    data: HashMap<String, String>,
  }

  impl MockConfig {
    fn new(data: &[(&str, &str)]) -> MockConfig {
      MockConfig {
        data: data.iter().map(|&(k, v)| (k.into(), v.into())).collect(),
      }
    }
  }

  impl Config for MockConfig {
    fn get(&self, name: &str) -> Result<String> {
      self.data
        .get(name.into())
        .cloned()
        .ok_or(format!("name not found: '{}'", name).into())
    }

    fn get_all(&self, glob: &str) -> Result<HashMap<String, String>> {
      Ok(self.data
        .iter()
        .filter(|&(name, _)| name.contains(glob))
        .map(|(name, value)| (name.clone(), value.clone()))
        .collect())
    }

    fn add(&mut self, _: &str, _: &str) -> Result<()> {
      unimplemented!();
    }

    fn set(&mut self, name: &str, value: &str) -> Result<()> {
      self.data.insert(name.into(), value.into());
      Ok(())
    }
  }
}
<|endoftext|>


<commit_before>use time;
use chrono::prelude::Local;
use ansi_term::Colour;
use ansi_term::Colour::{Green, Red, Yellow};
use logger::Logger;

#[derive(PartialEq, Clone)]
pub enum TestCaseStatus {
    PASSED,
    FAILED,
    SKIPPED,
    UNKNOWN,
}

pub struct TestCase {
    pub title: &'static str,
    pub criteria: &'static str,
    pub exec: Box<Fn(&mut Logger) -> TestCaseStatus>,
}
impl TestCase {
    pub fn new(title: &'static str,
               criteria: &'static str,
               exec: Box<Fn(&mut Logger) -> TestCaseStatus>)
               -> TestCase {
        TestCase {
            title: title,
            criteria: criteria,
            exec: exec,
        }
    }
}

pub struct TestCaseResults {
    title: &'static str,
    criteria: &'static str,
    duration: i32,
    status: TestCaseStatus,
}

pub trait Testable {
    fn tests(self) -> Vec<TestCase>;
}

#[allow(dead_code)]
pub struct TestRunnerAttributes {
    pub bail_out_after_first_failure: i64,
    pub disable_final_stats:          i64,
    pub minimize_output:              i64,
    // more attributes can be added here
}

// attribute values need to be assigned here if new attributes are added to TestRunnerAttributes
pub static TEST_RUNNER_ATTRIBUTES: TestRunnerAttributes = TestRunnerAttributes {
    bail_out_after_first_failure: 0x1,
    disable_final_stats:          0x2,
    minimize_output:              0x4,
};

#[allow(dead_code)]
pub struct TestRunner {
    attributes: i64,
    results: Vec<TestCaseResults>,
    module_path: &'static str,
}
impl TestRunner {
    pub fn new() -> TestRunner {
        TestRunner {
            attributes: 0,
            results: vec![],
            module_path: "",
        }
    }
    pub fn set_module_path(&mut self, path: &'static str) -> &mut Self {
        self.module_path = path;
        self
    }
    pub fn set_attribute(&mut self, attribute: i64) -> &mut Self {
        self.attributes |= attribute;
        self
    }
    pub fn set_attributes(&mut self, attributes: i64) -> &mut Self {
        self.attributes = attributes;
        self
    }
    pub fn has_attribute(&self, attribute: i64) -> bool {
        self.attributes & attribute == attribute
    }
    pub fn run_test(&mut self, test: TestCase) -> bool {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Starting {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
        }
        let mut logger: Logger = Logger::new();
        let starting_time: i32 = time::now().tm_nsec;
        let mut status: TestCaseStatus = (test.exec)(&mut logger);
        let ending_time: i32 = time::now().tm_nsec;
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Ended {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
            logger.drop();
        }
        if status == TestCaseStatus::UNKNOWN {
            if logger.get_num_fail() > 0 {
                status = TestCaseStatus::FAILED;
            } else {
                status = TestCaseStatus::PASSED;
            }
        }
        let mark = match status {
            TestCaseStatus::PASSED => "✅",
            TestCaseStatus::FAILED => "❌",
            TestCaseStatus::SKIPPED => "❗",
            TestCaseStatus::UNKNOWN => "",
        };
        let formatted_criteria = match status {
            TestCaseStatus::PASSED => Green.paint(test.criteria),
            TestCaseStatus::FAILED => Red.paint(test.criteria),
            TestCaseStatus::SKIPPED => Yellow.paint(test.criteria),
            TestCaseStatus::UNKNOWN => Yellow.paint(test.criteria),
        };
        let test_info = TestCaseResults {
            title: test.title,
            criteria: test.criteria,
            duration: (ending_time - starting_time) / 1000,
            status: status.clone(),
        };
        if self.module_path.len() > 0 {
            println!("{} {}::{}: {} ({}ns)", mark, self.module_path, test.title, formatted_criteria, test_info.duration);
        }
        else {
            println!("{} {}: {} ({}ns)", mark, test.title, formatted_criteria, test_info.duration);
        }
        self.results.push(test_info);
        return status == TestCaseStatus::PASSED;
    }
    pub fn run_tests(&mut self, tests: Vec<TestCase>) -> bool {
        for test in tests {
            if !self.run_test(test) && self.has_attribute(TEST_RUNNER_ATTRIBUTES.bail_out_after_first_failure) {
                return false;
            }
        }
        return true;
    }
    pub fn run_tests_from_class<T: Testable>(&mut self, test_class: T) -> bool {
        return self.run_tests(test_class.tests());
    }
}
impl Drop for TestRunner {
    fn drop(&mut self) {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.disable_final_stats) {
            let (mut total_count, mut total_duration): (i32, i32) = (0, 0);
            let (mut pass, mut fail, mut skip): (i32, i32, i32) = (0, 0, 0);
            print!("\n");
            for stat in self.results.iter() {
                let color: Colour;
                match stat.status {
                    TestCaseStatus::PASSED => {
                        pass += 1;
                        color = Green;
                    },
                    TestCaseStatus::FAILED => {
                        fail += 1;
                        color = Red;
                    },
                    TestCaseStatus::SKIPPED => {
                        skip += 1;
                        color = Yellow;
                    },
                    _ => {
                        color = Yellow;
                    }
                };
                total_count += 1;
                total_duration += stat.duration;
                let formatted_text = color.paint(format!("{} ({}) ... {}ns",
                                                         stat.title,
                                                         stat.criteria,
                                                         stat.duration));
                println!("{}", formatted_text);
            }
            println!("\nRan {} test(s) in {}ns", total_count, total_duration);
            let formatted_pass = Green.paint(format!("{} Passed", pass));
            let formatted_failed = Red.paint(format!("{} Failed", fail));
            let formatted_skipped = Yellow.paint(format!("{} Skipped", skip));
            println!("{}  {}  {}",
                     formatted_pass,
                     formatted_failed,
                     formatted_skipped);
        }
    }
}
<commit_msg>fixes a bug reported in #27<commit_after>use time;
use chrono::prelude::Local;
use ansi_term::Colour;
use ansi_term::Colour::{Green, Red, Yellow};
use logger::Logger;

#[derive(PartialEq, Clone)]
pub enum TestCaseStatus {
    PASSED,
    FAILED,
    SKIPPED,
    UNKNOWN,
}

pub struct TestCase {
    pub title: &'static str,
    pub criteria: &'static str,
    pub exec: Box<Fn(&mut Logger) -> TestCaseStatus>,
}
impl TestCase {
    pub fn new(title: &'static str,
               criteria: &'static str,
               exec: Box<Fn(&mut Logger) -> TestCaseStatus>)
               -> TestCase {
        TestCase {
            title: title,
            criteria: criteria,
            exec: exec,
        }
    }
}

pub struct TestCaseResults {
    title: &'static str,
    criteria: &'static str,
    duration: i32,
    status: TestCaseStatus,
}

pub trait Testable {
    fn tests(self) -> Vec<TestCase>;
}

#[allow(dead_code)]
pub struct TestRunnerAttributes {
    pub bail_out_after_first_failure: i64,
    pub disable_final_stats:          i64,
    pub minimize_output:              i64,
    // more attributes can be added here
}

// attribute values need to be assigned here if new attributes are added to TestRunnerAttributes
pub static TEST_RUNNER_ATTRIBUTES: TestRunnerAttributes = TestRunnerAttributes {
    bail_out_after_first_failure: 0x1,
    disable_final_stats:          0x2,
    minimize_output:              0x4,
};

#[allow(dead_code)]
pub struct TestRunner {
    attributes: i64,
    results: Vec<TestCaseResults>,
    module_path: &'static str,
}
impl TestRunner {
    pub fn new() -> TestRunner {
        TestRunner {
            attributes: 0,
            results: vec![],
            module_path: "",
        }
    }
    pub fn set_module_path(&mut self, path: &'static str) -> &mut Self {
        self.module_path = path;
        self
    }
    pub fn set_attribute(&mut self, attribute: i64) -> &mut Self {
        self.attributes |= attribute;
        self
    }
    pub fn set_attributes(&mut self, attributes: i64) -> &mut Self {
        self.attributes = attributes;
        self
    }
    pub fn has_attribute(&self, attribute: i64) -> bool {
        self.attributes & attribute == attribute
    }
    pub fn run_test(&mut self, test: TestCase) -> bool {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Starting {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
        }
        let mut logger: Logger = Logger::new();
        let starting_time: i32 = time::now().tm_nsec;
        let mut status: TestCaseStatus = (test.exec)(&mut logger);
        let ending_time: i32 = time::now().tm_nsec;
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Ended {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
            logger.drop();
        }
        if status == TestCaseStatus::UNKNOWN {
            if logger.get_num_fail() > 0 {
                status = TestCaseStatus::FAILED;
            } else {
                status = TestCaseStatus::PASSED;
            }
        }
        let mark = match status {
            TestCaseStatus::PASSED => "✅",
            TestCaseStatus::FAILED => "❌",
            TestCaseStatus::SKIPPED => "❗",
            TestCaseStatus::UNKNOWN => "",
        };
        let formatted_criteria = match status {
            TestCaseStatus::PASSED => Green.paint(test.criteria),
            TestCaseStatus::FAILED => Red.paint(test.criteria),
            TestCaseStatus::SKIPPED => Yellow.paint(test.criteria),
            TestCaseStatus::UNKNOWN => Yellow.paint(test.criteria),
        };
        let test_info = TestCaseResults {
            title: test.title,
            criteria: test.criteria,
            duration: ending_time - starting_time,
            status: status.clone(),
        };
        if self.module_path.len() > 0 {
            println!("{} {}::{}: {} ({}ns)", mark, self.module_path, test.title, formatted_criteria, test_info.duration);
        }
        else {
            println!("{} {}: {} ({}ns)", mark, test.title, formatted_criteria, test_info.duration);
        }
        self.results.push(test_info);
        return status == TestCaseStatus::PASSED;
    }
    pub fn run_tests(&mut self, tests: Vec<TestCase>) -> bool {
        for test in tests {
            if !self.run_test(test) && self.has_attribute(TEST_RUNNER_ATTRIBUTES.bail_out_after_first_failure) {
                return false;
            }
        }
        return true;
    }
    pub fn run_tests_from_class<T: Testable>(&mut self, test_class: T) -> bool {
        return self.run_tests(test_class.tests());
    }
}
impl Drop for TestRunner {
    fn drop(&mut self) {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.disable_final_stats) {
            let (mut total_count, mut total_duration): (i32, i32) = (0, 0);
            let (mut pass, mut fail, mut skip): (i32, i32, i32) = (0, 0, 0);
            print!("\n");
            for stat in self.results.iter() {
                let color: Colour;
                match stat.status {
                    TestCaseStatus::PASSED => {
                        pass += 1;
                        color = Green;
                    },
                    TestCaseStatus::FAILED => {
                        fail += 1;
                        color = Red;
                    },
                    TestCaseStatus::SKIPPED => {
                        skip += 1;
                        color = Yellow;
                    },
                    _ => {
                        color = Yellow;
                    }
                };
                total_count += 1;
                total_duration += stat.duration;
                let formatted_text = color.paint(format!("{} ({}) ... {}ns",
                                                         stat.title,
                                                         stat.criteria,
                                                         stat.duration));
                println!("{}", formatted_text);
            }
            println!("\nRan {} test(s) in {}ns", total_count, total_duration);
            let formatted_pass = Green.paint(format!("{} Passed", pass));
            let formatted_failed = Red.paint(format!("{} Failed", fail));
            let formatted_skipped = Yellow.paint(format!("{} Skipped", skip));
            println!("{}  {}  {}",
                     formatted_pass,
                     formatted_failed,
                     formatted_skipped);
        }
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2015 Joshua R. Rodgers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#[macro_export]
macro_rules! query {
    ($($tokens:tt)*) => {
        query_from!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_from {
    (from $context:pat => $source:expr, $($remainder:tt)+) => {
        query_from!($source, $context => $($remainder)+)
    };

    ($source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            let $context = value;
            $newSource
        });
        query_from!(source, $newContext => $($remainder)+)
    }};

    ($($tokens:tt)*) => {
        query_operator!($($tokens)*)
    };
}

//This is a total hack and may need to be further tested with other patterns (i.e. does not work with nested patterns)
#[macro_export]
macro_rules! query_where_let {
    ($source:expr, $context:pat => $t:ident($($newContext:ident),*) = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter_map(|&value| {
                let $context = value;
                if let $t($($newContext),*) = $letValue {
                    Some((value, ($($newContext),*, ())))
                }
                else {
                    None
                }
            });

            query!(source, ($context, ($($newContext),*, ())) => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => $t:ident = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter_map(|&value| {
                let $context = value;
                if let $t = $letValue {
                    Some(value)
                }
                else {
                    None
                }
            });

            query!(source, $context => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_operator {
    ($source:expr, $context:pat => where let $($remainder:tt)+) => {
        query_where_let!($source, $context => $($remainder)+)
    };

    ($source:expr, $context:pat => where $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&$context| { $filter });
            query!(source, $context => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => let $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                let $context = value;
                (value, $letValue)
            });

            query!(source, ($context, $newContext) => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => do $action:stmt, $($remainder:tt)+) => {
        {
            let source = $source.inspect(|$context| { $action; });
            query!(source, $context => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => from $($remainder:tt)+) => {
        {
            query_from!($source, $context => from $($remainder)+)
        }
    };

    ($($tokens:tt)*) => {
        query_end!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_end {
    ($source:expr, $context:pat => select $selector:expr) => {
        $source.map(|$context| { $selector })
    };
}<commit_msg>Improved the functionality of "where let" statements to handle complex pattern contexts.  Will need to add the ability to handle clauses besides "select" that may follow a where let.<commit_after>//
// Copyright 2015 Joshua R. Rodgers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#[macro_export]
macro_rules! context_match {
    ($value:expr, $context:pat => $result:expr) => {
        match $value {
            $context => $result,
            _ => panic!("Unexpected pattern context.")
        }
    }
}

#[macro_export]
macro_rules! query {
    ($($tokens:tt)*) => {
        query_from!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_from {
    (from $context:pat => $source:expr, $($remainder:tt)+) => {
        query_from!($source, $context => $($remainder)+)
    };

    ($source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            let $context = value;
            $newSource
        });

        query_from!(source, $newContext => $($remainder)+)
    }};

    ($($tokens:tt)*) => {
        query_operator!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_where {
    ($source:expr, $context:pat => $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&$context| { $filter });
            query!(source, $context => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_let {
    ($source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                let $context = value;
                (value, $letValue)
            });

            query!(source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_where_let {
    ($source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter_map(|&value| {
                let $context = value;
                if let $newContext = $letValue {
                    Some((value, $letValue))
                }
                else {
                    None
                }
            });

            query!(match_context, source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_operator {
    ($source:expr, $context:pat => where let $($remainder:tt)+) => {
        query_where_let!($source, $context => $($remainder)+)
    };

    ($source:expr, $context:pat => where $($remainder:tt)+) => {
        {
            query_where!($source, $context => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => let $($remainder:tt)+) => {
        {
            query_let!($source, $context => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => do $action:stmt, $($remainder:tt)+) => {
        {
            let source = $source.inspect(|$context| { $action; });
            query!(source, $context => $($remainder)+)
        }
    };

    ($source:expr, $context:pat => from $($remainder:tt)+) => {
        {
            query_from!($source, $context => from $($remainder)+)
        }
    };

    ($($tokens:tt)*) => {
        query_end!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_end {
    (match_context, $source:expr, $context:pat => select $selector:expr) => {
        $source.map(|value| { context_match!(value, $context => $selector) })
    };

    ($source:expr, $context:pat => select $selector:expr) => {
        $source.map(|$context| { $selector })
    };
}<|endoftext|>