<commit_before>use num::integer::Integer;
use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu<'a> {
    reg: Registers,
    mem: &'a mut Memory
}

impl<'a> Cpu<'a> {
    pub fn new(memory: &'a mut Memory) -> Cpu<'a> {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();

        match instr {
            0x00 => ops::nop(), // NOP
            0x01 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.b, &mut self.reg.c), // LD BC, nn
            0x02 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.b.read(), self.reg.c.read()), // LD (BC), A
            0x03 => ops::increment_register_pair(&mut self.reg.b, &mut self.reg.c), // INC BC
            0x04 => ops::increment_register(&mut self.reg.b, &mut self.reg.f), // INC B
            0x05 => ops::decrement_register(&mut self.reg.b, &mut self.reg.f), // DEC B
            0x06 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.b), // LD B, n
            0x07 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RLC A
            0x08 => ops::write_sp_to_address_immediate(self.mem, &mut self.reg.pc, &self.reg.sp), // LD (nn), SP
            0x09 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.b, &self.reg.c, &mut self.reg.f), // ADD HL, BC
            0x0A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.b, &mut self.reg.c), // LD A, (BC)
            0x0B => ops::decrement_register_pair(&mut self.reg.b, &mut self.reg.c), // DEC BC
            0x0C => ops::increment_register(&mut self.reg.c, &mut self.reg.f), // INC C
            0x0D => ops::decrement_register(&mut self.reg.c, &mut self.reg.f), // DEC C
            0x0E => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.c), // LD C, n
            0x0F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RRC A
            0x10 => return, // STOP
            0x11 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.d, &mut self.reg.e), // LD DE, nn
            0x12 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.d.read(), self.reg.e.read()), // LD (DE), A
            0x13 => ops::increment_register_pair(&mut self.reg.d, &mut self.reg.e), // INC DE
            0x14 => ops::increment_register(&mut self.reg.d, &mut self.reg.f), // INC D
            0x15 => ops::decrement_register(&mut self.reg.d, &mut self.reg.f), // DEC D
            0x16 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.d), // LD D, n
            0x17 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RL A
            0x18 => ops::jump_by_signed_immediate(self.mem, &mut self.reg.pc), // JR n
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<Flags>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(Flags::empty()),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0x100),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy> {
    val: T
}

impl<T: Copy> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }
}

impl<T: Copy + Unsigned>  Register<T> {
    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }

    pub fn decrement(&mut self) {
        let i = self.val;
        self.write(i - One::one());
    }
}

bitflags! {
    flags Flags: u8 {
        const ZeroFlag       = 0b10000000,
        const SubtractFlag   = 0b01000000,
        const HalfCarryFlag  = 0b00100000,
        const CarryFlag      = 0b00010000,
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

#[test]
fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);
    assert!(reg.read() == 5);
}

#[test]
fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}

#[test]
fn test_Register_increment() {
    let mut reg: Register<u16> = Register::new(483);

    reg.increment();
    assert!(reg.val == 484);

}

#[test]
fn test_Register_decrement() {
    let mut reg: Register<u16> = Register::new(401);

    reg.decrement();
    assert!(reg.val == 400);

}

#[test]
fn test_Registers_new() {
    let a = Registers::new();
    assert!(a.pc.read() == 0x100);
    assert!(a.sp.read() == 0xFFFE);
}
<commit_msg>Add opcodes 0x19-0x1F<commit_after>use num::integer::Integer;
use memory::Memory;
use memory::low_nibble;
use memory::high_nibble;
use extensions::Incrementor;
use ops::{mod};
use std::num::One;

pub struct Cpu<'a> {
    reg: Registers,
    mem: &'a mut Memory
}

impl<'a> Cpu<'a> {
    pub fn new(memory: &'a mut Memory) -> Cpu<'a> {
        return Cpu{reg: Registers::new(), mem: memory}
    }

    /// Execute a cycle on the cpu
    pub fn tick(&mut self) {
        let instr = self.fetch_instruction();

        match instr {
            0x00 => ops::nop(), // NOP
            0x01 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.b, &mut self.reg.c), // LD BC, nn
            0x02 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.b.read(), self.reg.c.read()), // LD (BC), A
            0x03 => ops::increment_register_pair(&mut self.reg.b, &mut self.reg.c), // INC BC
            0x04 => ops::increment_register(&mut self.reg.b, &mut self.reg.f), // INC B
            0x05 => ops::decrement_register(&mut self.reg.b, &mut self.reg.f), // DEC B
            0x06 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.b), // LD B, n
            0x07 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RLC A
            0x08 => ops::write_sp_to_address_immediate(self.mem, &mut self.reg.pc, &self.reg.sp), // LD (nn), SP
            0x09 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.b, &self.reg.c, &mut self.reg.f), // ADD HL, BC
            0x0A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.b, &mut self.reg.c), // LD A, (BC)
            0x0B => ops::decrement_register_pair(&mut self.reg.b, &mut self.reg.c), // DEC BC
            0x0C => ops::increment_register(&mut self.reg.c, &mut self.reg.f), // INC C
            0x0D => ops::decrement_register(&mut self.reg.c, &mut self.reg.f), // DEC C
            0x0E => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.c), // LD C, n
            0x0F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RRC A
            0x10 => return, // STOP
            0x11 => ops::ld_next_two_byte_into_reg_pair(self.mem, &mut self.reg.pc, &mut self.reg.d, &mut self.reg.e), // LD DE, nn
            0x12 => ops::write_value_to_memory_at_address(self.mem, self.reg.a.read(), self.reg.d.read(), self.reg.e.read()), // LD (DE), A
            0x13 => ops::increment_register_pair(&mut self.reg.d, &mut self.reg.e), // INC DE
            0x14 => ops::increment_register(&mut self.reg.d, &mut self.reg.f), // INC D
            0x15 => ops::decrement_register(&mut self.reg.d, &mut self.reg.f), // DEC D
            0x16 => ops::ld_immediate(self.mem, &mut self.reg.pc, &mut self.reg.d), // LD D, n
            0x17 => ops::rotate_left_with_carry(&mut self.reg.a, &mut self.reg.f), // RL A
            0x18 => ops::jump_by_signed_immediate(self.mem, &mut self.reg.pc), // JR n
            0x19 => ops::add_register_pair_to_register_pair(&mut self.reg.h, &mut self.reg.l, &self.reg.d, &self.reg.e, &mut self.reg.f), // Add HL, DE
            0x1A => ops::ld_a_from_reg_pair_as_address(self.mem, &mut self.reg.a, &mut self.reg.d, &mut self.reg.e), // LD A, (DE)
            0x1B => ops::decrement_register_pair(&mut self.reg.d, &mut self.reg.e), // DEC DE
            0x1C => ops::increment_register(&mut self.reg.e, &mut self.reg.f), // INC E
            0x1E => ops::decrement_register(&mut self.reg.e, &mut self.reg.f), // DEC E
            0x1F => ops::rotate_right_with_carry(&mut self.reg.a, &mut self.reg.f), // RR A
            _ => return
        }
    }

    /// Fetches the instruction pointed to by the program counter
    /// and increments the pc by 1
    fn fetch_instruction(&mut self) -> u8 {
        let instr = self.mem.read_byte(self.reg.pc.val);
        self.reg.pc.increment();
        return instr;
    }
}

struct Registers {
    a: Register<u8>, b: Register<u8>, c: Register<u8>,
    d: Register<u8>, e: Register<u8>, f: Register<Flags>,
    h: Register<u8>, l: Register<u8>, // 8-bit registers

    pc: Register<u16>, sp: Register<u16>, // 16-bit registers
    m: Register<u16>, t: Register<u16> // clock
}

impl Registers {
    fn new() -> Registers {
        return Registers{
            a: Register::new(0),
            b: Register::new(0),
            c: Register::new(0),
            d: Register::new(0),
            e: Register::new(0),
            f: Register::new(Flags::empty()),
            h: Register::new(0),
            l: Register::new(0),

            pc: Register::new(0x100),
            sp: Register::new(0xFFFE),

            m: Register::new(0),
            t: Register::new(0)
        }
    }
}

pub struct Register<T: Copy> {
    val: T
}

impl<T: Copy> Register<T> {
    pub fn new(i: T) -> Register<T> {
        return Register { val: i };
    }

    pub fn read(&self) -> T {
        return self.val;
    }

    pub fn write(&mut self, i: T) {
        self.val = i;
    }
}

impl<T: Copy + Unsigned>  Register<T> {
    pub fn increment(&mut self) {
        let i = self.val;
        self.write(i + One::one());
    }

    pub fn decrement(&mut self) {
        let i = self.val;
        self.write(i - One::one());
    }
}

bitflags! {
    flags Flags: u8 {
        const ZeroFlag       = 0b10000000,
        const SubtractFlag   = 0b01000000,
        const HalfCarryFlag  = 0b00100000,
        const CarryFlag      = 0b00010000,
    }
}

#[test]
fn test_Register_new() {
    let reg: Register<u8> = Register::new(10);
    assert!(reg.val == 10);
}

#[test]
fn test_Register_read() {
    let reg: Register<u32> = Register::new(5);
    assert!(reg.read() == 5);
}

#[test]
fn test_Register_write() {
    let mut reg: Register<u16> = Register::new(483);

    reg.write(5);
    assert!(reg.val == 5);
}

#[test]
fn test_Register_increment() {
    let mut reg: Register<u16> = Register::new(483);

    reg.increment();
    assert!(reg.val == 484);

}

#[test]
fn test_Register_decrement() {
    let mut reg: Register<u16> = Register::new(401);

    reg.decrement();
    assert!(reg.val == 400);

}

#[test]
fn test_Registers_new() {
    let a = Registers::new();
    assert!(a.pc.read() == 0x100);
    assert!(a.sp.read() == 0xFFFE);
}
<|endoftext|>


<commit_before>#![crate_name = "irc"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![feature(if_let, slicing_syntax, globs, phase)]

#![allow(dead_code)]
#![deny(unused_must_use)]
#![deny(warnings)]

#[phase(plugin, link)] extern crate log;
extern crate time;
extern crate serialize;

pub use self::message::IrcMessage;

pub use self::connection::IrcConnection;
pub use self::connection::RawWrite;

pub use self::event::{
	IrcEvent,
	IrcEventMessage,
	IrcEventJoinBundle,
	IrcEventWhoBundle,
};

pub use self::watchers::{
    RegisterError,
    RegisterErrorType,

    JoinResult,
    JoinSuccess,
    JoinError,

    WhoResult,
    WhoRecord,
    WhoSuccess,
    WhoError,
};

pub use self::parse::{
    IrcMsgPrefix,
};

mod numerics;
mod connection;
mod message;
mod watchers;
mod core_plugins;

#[experimental = "Subject to being moved"]
/// Experimental utility code
pub mod util;

#[experimental = "Subject to being moved"]
/// Experimental parsing code
pub mod parse;

/// Event types
mod event;
<commit_msg>Sigh.. re-export things I don't want to export, for now<commit_after>#![crate_name = "irc"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![feature(if_let, slicing_syntax, globs, phase)]

#![allow(dead_code)]
#![deny(unused_must_use)]
#![deny(warnings)]

#[phase(plugin, link)] extern crate log;
extern crate time;
extern crate serialize;

pub use self::message::IrcMessage;

pub use self::connection::IrcConnection;
pub use self::connection::RawWrite;

pub use self::event::{
	IrcEvent,
	IrcEventMessage,
	IrcEventJoinBundle,
	IrcEventWhoBundle,
};

pub use self::watchers::{
    RegisterError,
    RegisterErrorType,

    JoinResult,
    JoinSuccess,
    JoinError,

    WhoResult,
    WhoRecord,
    WhoSuccess,
    WhoError,

    BundlerManager,
    JoinBundlerTrigger,
    WhoBundlerTrigger,
};

pub use self::parse::{
    IrcMsgPrefix,
};

mod numerics;
mod connection;
mod message;
mod watchers;
mod core_plugins;

#[experimental = "Subject to being moved"]
/// Experimental utility code
pub mod util;

#[experimental = "Subject to being moved"]
/// Experimental parsing code
pub mod parse;

/// Event types
mod event;
<|endoftext|>


<commit_before>//! Crate `retry` provides utilities for retrying operations that can fail.
//!
//! # Usage
//!
//! Retry an operation using the `retry` function. `retry` accepts an iterator over `Duration`s and
//! a closure that returns a `Result` (or `OperationResult`; see below). The iterator is used to
//! determine how long to wait after each unsuccessful try and how many times to try before giving
//! up and returning `Result::Err`. The closure determines either the final successful value, or
//! an error value, which can either be returned immediately or used to indicate that the
//! operation should be retried.
//!
//! Any type that implements `Iterator<Item = Duration>` can be used to determine retry behavior,
//! though a few useful implementations are provided in the `delay` module, including a fixed delay
//! and exponential back-off.
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! The `Iterator` API can be used to limit or modify the delay strategy. For example, to limit the
//! number of retries to 1:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100).take(1), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_err());
//! ```
//!
//! To apply random jitter to any delay strategy, the `jitter` function can be used in combination
//! with the `Iterator` API:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::{Exponential, jitter};
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Exponential::from_millis(10).map(jitter).take(3), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! To deal with fatal errors, return `retry::OperationResult`, which is like std's `Result`, but
//! with a third case to distinguish between errors that should cause a retry and errors that
//! should immediately return, halting retry behavior. (Internally, `OperationResult` is always
//! used, and closures passed to `retry` that return plain `Result` are converted into
//! `OperationResult`.)
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! use retry::OperationResult;
//! let mut collection = vec![1, 2].into_iter();
//! let value = retry(Fixed::from_millis(1), || {
//!     match collection.next() {
//!         Some(n) if n == 2 => OperationResult::Ok(n),
//!         Some(_) => OperationResult::Retry("not 2"),
//!         None => OperationResult::Err("not found"),
//!     }
//! }).unwrap();
//!
//! assert_eq!(value, 2);
//! ```

#![deny(missing_debug_implementations, missing_docs, warnings)]

use std::{
    error::Error as StdError,
    fmt::{Display, Error as FmtError, Formatter},
    thread::sleep,
    time::Duration,
};

pub mod delay;
mod opresult;

#[doc(inline)]
pub use opresult::OperationResult;

/// Retry the given operation synchronously until it succeeds, or until the given `Duration`
/// iterator ends.
pub fn retry<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut() -> OR,
    OR: Into<OperationResult<R, E>>,
{
    let mut iterator = iterable.into_iter();
    let mut current_try = 1;
    let mut total_delay = Duration::default();

    loop {
        match operation().into() {
            OperationResult::Ok(value) => return Ok(value),
            OperationResult::Retry(error) => {
                if let Some(delay) = iterator.next() {
                    sleep(delay);
                    current_try += 1;
                    total_delay += delay;
                } else {
                    return Err(Error::Operation {
                        error,
                        total_delay,
                        tries: current_try,
                    });
                }
            }
            OperationResult::Err(error) => {
                return Err(Error::Operation {
                    error,
                    total_delay,
                    tries: current_try,
                });
            }
        }
    }
}

/// An error with a retryable operation.
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
    /// The operation's last error, plus the number of times the operation was tried and the
    /// duration spent waiting between tries.
    Operation {
        /// The error returned by the operation on the last try.
        error: E,
        /// The duration spent waiting between retries of the operation.
        ///
        /// Note that this does not include the time spent running the operation itself.
        total_delay: Duration,
        /// The total number of times the operation was tried.
        tries: u64,
    },
    /// Something went wrong in the internal logic.
    Internal(String),
}

impl<E> Display for Error<E>
where
    E: StdError,
{
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> {
        write!(formatter, "{}", self.description())
    }
}

impl<E> StdError for Error<E>
where
    E: StdError,
{
    fn description(&self) -> &str {
        match *self {
            Error::Operation { ref error, .. } => error.description(),
            Error::Internal(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match *self {
            Error::Operation { ref error, .. } => Some(error),
            Error::Internal(_) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use super::delay::{Exponential, Fixed, NoDelay, Range};
    use super::opresult::OperationResult;
    use super::{retry, Error};

    #[test]
    fn succeeds_with_infinite_retries() {
        let mut collection = vec![1, 2, 3, 4, 5].into_iter();

        let value = retry(NoDelay, || match collection.next() {
            Some(n) if n == 5 => Ok(n),
            Some(_) => Err("not 5"),
            None => Err("not 5"),
        })
        .unwrap();

        assert_eq!(value, 5);
    }

    #[test]
    fn succeeds_with_maximum_retries() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fails_after_last_try() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "not 2",
                tries: 2,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn fatal_errors() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(2), || match collection.next() {
            Some(n) if n == 2 => OperationResult::Ok(n),
            Some(_) => OperationResult::Err("no retry"),
            None => OperationResult::Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "no retry",
                tries: 1,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn succeeds_with_fixed_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Fixed::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fixed_delay_from_duration() {
        assert_eq!(
            Fixed::from_millis(1_000).next(),
            Fixed::from(Duration::from_secs(1)).next(),
        );
    }

    #[test]
    fn succeeds_with_exponential_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Exponential::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn succeeds_with_ranged_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Range::from_millis_exclusive(1, 10), || {
            match collection.next() {
                Some(n) if n == 2 => Ok(n),
                Some(_) => Err("not 2"),
                None => Err("not 2"),
            }
        })
        .unwrap();

        assert_eq!(value, 2);
    }
}
<commit_msg>add retry i function<commit_after>//! Crate `retry` provides utilities for retrying operations that can fail.
//!
//! # Usage
//!
//! Retry an operation using the `retry` function. `retry` accepts an iterator over `Duration`s and
//! a closure that returns a `Result` (or `OperationResult`; see below). The iterator is used to
//! determine how long to wait after each unsuccessful try and how many times to try before giving
//! up and returning `Result::Err`. The closure determines either the final successful value, or
//! an error value, which can either be returned immediately or used to indicate that the
//! operation should be retried.
//!
//! Any type that implements `Iterator<Item = Duration>` can be used to determine retry behavior,
//! though a few useful implementations are provided in the `delay` module, including a fixed delay
//! and exponential back-off.
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! The `Iterator` API can be used to limit or modify the delay strategy. For example, to limit the
//! number of retries to 1:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100).take(1), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_err());
//! ```
//!
//! To apply random jitter to any delay strategy, the `jitter` function can be used in combination
//! with the `Iterator` API:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::{Exponential, jitter};
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Exponential::from_millis(10).map(jitter).take(3), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! To deal with fatal errors, return `retry::OperationResult`, which is like std's `Result`, but
//! with a third case to distinguish between errors that should cause a retry and errors that
//! should immediately return, halting retry behavior. (Internally, `OperationResult` is always
//! used, and closures passed to `retry` that return plain `Result` are converted into
//! `OperationResult`.)
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! use retry::OperationResult;
//! let mut collection = vec![1, 2].into_iter();
//! let value = retry(Fixed::from_millis(1), || {
//!     match collection.next() {
//!         Some(n) if n == 2 => OperationResult::Ok(n),
//!         Some(_) => OperationResult::Retry("not 2"),
//!         None => OperationResult::Err("not found"),
//!     }
//! }).unwrap();
//!
//! assert_eq!(value, 2);
//! ```

#![deny(missing_debug_implementations, missing_docs, warnings)]

use std::{
    error::Error as StdError,
    fmt::{Display, Error as FmtError, Formatter},
    thread::sleep,
    time::Duration,
};

pub mod delay;
mod opresult;

#[doc(inline)]
pub use opresult::OperationResult;

/// Retry the given operation synchronously until it succeeds, or until the given `Duration`
/// iterator ends.
pub fn retry<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut() -> OR,
    OR: Into<OperationResult<R, E>>,
{
    let mut iterator = iterable.into_iter();
    let mut current_try = 1;
    let mut total_delay = Duration::default();

    loop {
        match operation().into() {
            OperationResult::Ok(value) => return Ok(value),
            OperationResult::Retry(error) => {
                if let Some(delay) = iterator.next() {
                    sleep(delay);
                    current_try += 1;
                    total_delay += delay;
                } else {
                    return Err(Error::Operation {
                        error,
                        total_delay,
                        tries: current_try,
                    });
                }
            }
            OperationResult::Err(error) => {
                return Err(Error::Operation {
                    error,
                    total_delay,
                    tries: current_try,
                });
            }
        }
    }
}

/// Retry with iteration number. See also [retry](fn.retry.html)
pub fn retryi<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut(u64) -> OR,
    OR: Into<OperationResult<R, E>>,
{
    let mut iterator = iterable.into_iter();
    let mut current_try = 1;
    let mut total_delay = Duration::default();

    loop {
        match operation(current_try).into() {
            OperationResult::Ok(value) => return Ok(value),
            OperationResult::Retry(error) => {
                if let Some(delay) = iterator.next() {
                    sleep(delay);
                    current_try += 1;
                    total_delay += delay;
                } else {
                    return Err(Error::Operation {
                        error,
                        total_delay,
                        tries: current_try,
                    });
                }
            }
            OperationResult::Err(error) => {
                return Err(Error::Operation {
                    error,
                    total_delay,
                    tries: current_try,
                });
            }
        }
    }
}

/// An error with a retryable operation.
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
    /// The operation's last error, plus the number of times the operation was tried and the
    /// duration spent waiting between tries.
    Operation {
        /// The error returned by the operation on the last try.
        error: E,
        /// The duration spent waiting between retries of the operation.
        ///
        /// Note that this does not include the time spent running the operation itself.
        total_delay: Duration,
        /// The total number of times the operation was tried.
        tries: u64,
    },
    /// Something went wrong in the internal logic.
    Internal(String),
}

impl<E> Display for Error<E>
where
    E: StdError,
{
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> {
        write!(formatter, "{}", self.description())
    }
}

impl<E> StdError for Error<E>
where
    E: StdError,
{
    fn description(&self) -> &str {
        match *self {
            Error::Operation { ref error, .. } => error.description(),
            Error::Internal(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match *self {
            Error::Operation { ref error, .. } => Some(error),
            Error::Internal(_) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use super::delay::{Exponential, Fixed, NoDelay, Range};
    use super::opresult::OperationResult;
    use super::{retry, Error};

    #[test]
    fn succeeds_with_infinite_retries() {
        let mut collection = vec![1, 2, 3, 4, 5].into_iter();

        let value = retry(NoDelay, || match collection.next() {
            Some(n) if n == 5 => Ok(n),
            Some(_) => Err("not 5"),
            None => Err("not 5"),
        })
        .unwrap();

        assert_eq!(value, 5);
    }

    #[test]
    fn succeeds_with_maximum_retries() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fails_after_last_try() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "not 2",
                tries: 2,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn fatal_errors() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(2), || match collection.next() {
            Some(n) if n == 2 => OperationResult::Ok(n),
            Some(_) => OperationResult::Err("no retry"),
            None => OperationResult::Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "no retry",
                tries: 1,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn succeeds_with_fixed_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Fixed::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fixed_delay_from_duration() {
        assert_eq!(
            Fixed::from_millis(1_000).next(),
            Fixed::from(Duration::from_secs(1)).next(),
        );
    }

    #[test]
    fn succeeds_with_exponential_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Exponential::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn succeeds_with_ranged_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Range::from_millis_exclusive(1, 10), || {
            match collection.next() {
                Some(n) if n == 2 => Ok(n),
                Some(_) => Err("not 2"),
                None => Err("not 2"),
            }
        })
        .unwrap();

        assert_eq!(value, 2);
    }
}
<|endoftext|>


<commit_before>#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/7853871?s=128", html_favicon_url = "https://avatars0.githubusercontent.com/u/7853871?s=256", html_root_url = "http://ironframework.io/core/iron")]

#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, feature(test, box_syntax))]

#![deny(missing_docs)]
#![feature(unboxed_closures, core, os, io, net, fs, path)]

//! The main crate for Iron.
//!
//! ## Overview
//!
//! Iron is a high level web framework built in and for Rust, built on
//! [hyper](https://github.com/hyperium/hyper). Iron is designed to take advantage
//! of Rust's greatest features - it's excellent type system and its principled
//! approach to ownership in both single threaded and multi threaded contexts.
//!
//! Iron is highly concurrent and can scale horizontally on more machines behind a
//! load balancer or by running more threads on a more powerful machine. Iron
//! avoids the bottlenecks encountered in highly concurrent code by avoiding shared
//! writes and locking in the core framework.
//!
//! Iron is 100% safe code:
//!
//! ```sh
//! $ ack unsafe src | wc
//!        0       0       0
//! ```
//!
//! ## Hello World
//!
//! ```no_run
//! # use iron::prelude::*;
//! # use iron::status;
//! Iron::new(|req: &mut Request| {
//!     Ok(Response::with((status::Ok, "Hello World!")))
//! }).http("localhost:3000").unwrap();
//! ```
//!
//! ## Design Philosophy
//!
//! Iron is meant to be as extensible and pluggable as possible; Iron's core is
//! concentrated and avoids unnecessary features by leaving them to middleware,
//! plugins, and modifiers.
//!
//! Middleware, Plugins, and Modifiers are the main ways to extend Iron with new
//! functionality. Most extensions that would be provided by middleware in other
//! web frameworks are instead addressed by the much simpler Modifier and Plugin
//! systems.
//!
//! Modifiers allow external code to manipulate Requests and Response in an ergonomic
//! fashion, allowing third-party extensions to get the same treatment as modifiers
//! defined in Iron itself. Plugins allow for lazily-evaluated, automically cached
//! extensions to Requests and Responses, perfect for parsing, accessing, and
//! otherwise lazily manipulating an http connection.
//!
//! Middleware are only used when it is necessary to modify the control flow of a
//! Request flow, hijack the entire handling of a Request, check an incoming
//! Request, or to do final post-processing. This covers areas such as routing,
//! mounting, static asset serving, final template rendering, authentication, and
//! logging.
//!
//! Iron comes with only basic modifiers for setting the status, body, and various
//! headers, and the infrastructure for creating modifiers, plugins, and
//! middleware. No plugins or middleware are bundled with Iron.
//!

// Stdlib dependencies
#[macro_use] extern crate log;
#[cfg(test)] extern crate test;

// Third party packages
extern crate hyper;
extern crate "typemap" as tmap;
extern crate plugin;
extern crate "error" as err;
extern crate url;

// Request + Response
pub use request::{Request, Url};
pub use response::Response;

// Middleware system
pub use middleware::{BeforeMiddleware, AfterMiddleware, AroundMiddleware,
                     Handler, Chain};

// Server
pub use iron::{Iron, Protocol};

// Extensions
pub use typemap::TypeMap;

// Headers
pub use hyper::header as headers;
pub use hyper::header::Headers;

// Expose `Pluggable` as `Plugin` so users can do `use iron::Plugin`.
pub use plugin::Pluggable as Plugin;

// Expose modifiers.
pub use modifier::Set;

// Errors
pub use error::Error;
pub use error::IronError;

// Mime types
pub use hyper::mime;

/// Iron's error type and associated utilities.
pub mod error;

/// The Result alias used throughout Iron and in clients of Iron.
pub type IronResult<T> = Result<T, IronError>;

/// A module meant to be glob imported when using Iron, for instance:
///
/// ```
/// use iron::prelude::*;
/// ```
///
/// This module contains several important traits that provide many
/// of the convenience methods in Iron, as well as `Request`, `Response`
/// `IronResult`, `IronError` and `Iron`.
pub mod prelude {
    pub use {Set, Plugin, Chain, Request, Response,
             IronResult, IronError, Iron};
}

/// Re-exports from the TypeMap crate.
pub mod typemap {
    pub use tmap::{TypeMap, Key};
}

/// Re-exports from the Modifier crate.
pub mod modifier {
    extern crate "modifier" as modfier;
    pub use self::modfier::*;
}

/// Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
}

/// HTTP Methods
pub mod method {
    pub use hyper::method::Method;
    pub use hyper::method::Method::*;
}

// Publicized to show the documentation
pub mod middleware;

// Response utilities
pub mod response;

// Request utilities
pub mod request;

// Request and Response Modifiers
pub mod modifiers;

mod iron;

<commit_msg>Remove the fs feature gate.<commit_after>#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/7853871?s=128", html_favicon_url = "https://avatars0.githubusercontent.com/u/7853871?s=256", html_root_url = "http://ironframework.io/core/iron")]

#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, feature(test, box_syntax))]

#![deny(missing_docs)]
#![feature(unboxed_closures, core, os, io, net, path)]

//! The main crate for Iron.
//!
//! ## Overview
//!
//! Iron is a high level web framework built in and for Rust, built on
//! [hyper](https://github.com/hyperium/hyper). Iron is designed to take advantage
//! of Rust's greatest features - it's excellent type system and its principled
//! approach to ownership in both single threaded and multi threaded contexts.
//!
//! Iron is highly concurrent and can scale horizontally on more machines behind a
//! load balancer or by running more threads on a more powerful machine. Iron
//! avoids the bottlenecks encountered in highly concurrent code by avoiding shared
//! writes and locking in the core framework.
//!
//! Iron is 100% safe code:
//!
//! ```sh
//! $ ack unsafe src | wc
//!        0       0       0
//! ```
//!
//! ## Hello World
//!
//! ```no_run
//! # use iron::prelude::*;
//! # use iron::status;
//! Iron::new(|req: &mut Request| {
//!     Ok(Response::with((status::Ok, "Hello World!")))
//! }).http("localhost:3000").unwrap();
//! ```
//!
//! ## Design Philosophy
//!
//! Iron is meant to be as extensible and pluggable as possible; Iron's core is
//! concentrated and avoids unnecessary features by leaving them to middleware,
//! plugins, and modifiers.
//!
//! Middleware, Plugins, and Modifiers are the main ways to extend Iron with new
//! functionality. Most extensions that would be provided by middleware in other
//! web frameworks are instead addressed by the much simpler Modifier and Plugin
//! systems.
//!
//! Modifiers allow external code to manipulate Requests and Response in an ergonomic
//! fashion, allowing third-party extensions to get the same treatment as modifiers
//! defined in Iron itself. Plugins allow for lazily-evaluated, automically cached
//! extensions to Requests and Responses, perfect for parsing, accessing, and
//! otherwise lazily manipulating an http connection.
//!
//! Middleware are only used when it is necessary to modify the control flow of a
//! Request flow, hijack the entire handling of a Request, check an incoming
//! Request, or to do final post-processing. This covers areas such as routing,
//! mounting, static asset serving, final template rendering, authentication, and
//! logging.
//!
//! Iron comes with only basic modifiers for setting the status, body, and various
//! headers, and the infrastructure for creating modifiers, plugins, and
//! middleware. No plugins or middleware are bundled with Iron.
//!

// Stdlib dependencies
#[macro_use] extern crate log;
#[cfg(test)] extern crate test;

// Third party packages
extern crate hyper;
extern crate "typemap" as tmap;
extern crate plugin;
extern crate "error" as err;
extern crate url;

// Request + Response
pub use request::{Request, Url};
pub use response::Response;

// Middleware system
pub use middleware::{BeforeMiddleware, AfterMiddleware, AroundMiddleware,
                     Handler, Chain};

// Server
pub use iron::{Iron, Protocol};

// Extensions
pub use typemap::TypeMap;

// Headers
pub use hyper::header as headers;
pub use hyper::header::Headers;

// Expose `Pluggable` as `Plugin` so users can do `use iron::Plugin`.
pub use plugin::Pluggable as Plugin;

// Expose modifiers.
pub use modifier::Set;

// Errors
pub use error::Error;
pub use error::IronError;

// Mime types
pub use hyper::mime;

/// Iron's error type and associated utilities.
pub mod error;

/// The Result alias used throughout Iron and in clients of Iron.
pub type IronResult<T> = Result<T, IronError>;

/// A module meant to be glob imported when using Iron, for instance:
///
/// ```
/// use iron::prelude::*;
/// ```
///
/// This module contains several important traits that provide many
/// of the convenience methods in Iron, as well as `Request`, `Response`
/// `IronResult`, `IronError` and `Iron`.
pub mod prelude {
    pub use {Set, Plugin, Chain, Request, Response,
             IronResult, IronError, Iron};
}

/// Re-exports from the TypeMap crate.
pub mod typemap {
    pub use tmap::{TypeMap, Key};
}

/// Re-exports from the Modifier crate.
pub mod modifier {
    extern crate "modifier" as modfier;
    pub use self::modfier::*;
}

/// Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
}

/// HTTP Methods
pub mod method {
    pub use hyper::method::Method;
    pub use hyper::method::Method::*;
}

// Publicized to show the documentation
pub mod middleware;

// Response utilities
pub mod response;

// Request utilities
pub mod request;

// Request and Response Modifiers
pub mod modifiers;

mod iron;

<|endoftext|>


<commit_before>#![cfg(feature = "full")]

#[macro_use]
extern crate quote;
extern crate syn;
extern crate syntex_pos;
extern crate syntex_syntax;
extern crate time;
extern crate walkdir;

use syntex_pos::Span;
use syntex_syntax::ast;
use syntex_syntax::parse::{self, ParseSess, PResult};
use time::PreciseTime;

use std::fs::File;
use std::io::{self, Read, Write};

macro_rules! errorf {
    ($($tt:tt)*) => {
        write!(io::stderr(), $($tt)*).unwrap();
    };
}

#[test]
fn test_round_trip() {
    let mut failed = 0;

    let walk = walkdir::WalkDir::new("tests/cases").sort_by(|a, b| a.cmp(b));
    for entry in walk.into_iter() {
        let entry = entry.unwrap();

        let path = entry.path();
        if path.extension().map(|e| e != "rs").unwrap_or(true) {
            continue;
        }
        errorf!("=== {}: ", path.display());

        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        let start = PreciseTime::now();
        let (krate, elapsed) = match syn::parse_crate(&content) {
            Ok(krate) => (krate, start.to(PreciseTime::now())),
            Err(msg) => {
                errorf!("syn failed to parse\n{}\n", msg);
                failed += 1;
                continue;
            }
        };
        let back = quote!(#krate).to_string();

        let sess = ParseSess::new();
        let before = match syntex_parse(content, &sess) {
            Ok(before) => before,
            Err(mut diagnostic) => {
                diagnostic.cancel();
                if diagnostic.message.starts_with("file not found for module") {
                    errorf!("ignore\n");
                    continue;
                } else {
                    panic!(diagnostic.message.clone());
                }
            }
        };
        let after = match syntex_parse(back, &sess) {
            Ok(after) => after,
            Err(mut diagnostic) => {
                errorf!("syntex failed to parse");
                diagnostic.emit();
                failed += 1;
                continue;
            }
        };

        if before == after {
            errorf!("pass in {}ms\n", elapsed.num_milliseconds());
        } else {
            errorf!("FAIL\nbefore: {:?}\nafter: {:?}\n", before, after);
            failed += 1;
        }
    }

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn syntex_parse<'a>(content: String, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
    let name = "test_round_trip".to_string();
    let cfg = Vec::new();
    parse::parse_crate_from_source_str(name, content, cfg, sess).map(respan_crate)
}

fn respan_crate(krate: ast::Crate) -> ast::Crate {
    use std::rc::Rc;
    use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
                             ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
                             NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind,
                             TyParam};
    use syntex_syntax::codemap::{self, Spanned};
    use syntex_syntax::fold::{self, Folder};
    use syntex_syntax::parse::token::{Lit, Token, intern};
    use syntex_syntax::ptr::P;
    use syntex_syntax::tokenstream::{Delimited, SequenceRepetition, TokenTree};
    use syntex_syntax::util::move_map::MoveMap;
    use syntex_syntax::util::small_vector::SmallVector;

    struct Respanner;

    impl Respanner {
        fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
            codemap::respan(self.new_span(spanned.span), spanned.node)
        }

        fn fold_lit(&mut self, l: Lit) -> Lit {
            match l {
                Lit::Integer(repr) => {
                    Lit::Integer(intern(&repr.to_string().replace("_", "")))
                }
                Lit::Str_(_) => {
                    // Give up on comparing strings because there are so many
                    // equivalent representations of the same string; they are
                    // tested elsewhere
                    Lit::Str_(intern(""))
                }
                Lit::Float(repr) => {
                    Lit::Float(intern(&repr.to_string().replace("_", "")))
                }
                _ => l,
            }
        }
    }

    impl Folder for Respanner {
        fn new_span(&mut self, _: Span) -> Span {
            syntex_pos::DUMMY_SP
        }

        fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
            match i {
                ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
                    let generics = self.fold_generics(generics);
                    let decl = self.fold_fn_decl(decl);
                    let body = self.fold_block(body);
                    // default fold_item_kind does not fold this span
                    let constness = self.fold_spanned(constness);
                    ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
                }
                _ => fold::noop_fold_item_kind(i, self),
            }
        }

        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
            e.map(|e| {
                let folded = fold::noop_fold_expr(e, self);
                Expr {
                    node: match folded.node {
                        ExprKind::Lit(l) => {
                            // default fold_expr does not fold lits
                            ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
                        }
                        ExprKind::Binary(op, lhs, rhs) => {
                            // default fold_expr does not fold the op span
                            ExprKind::Binary(self.fold_spanned(op),
                                             self.fold_expr(lhs),
                                             self.fold_expr(rhs))
                        }
                        ExprKind::AssignOp(op, lhs, rhs) => {
                            // default fold_expr does not fold the op span
                            ExprKind::AssignOp(self.fold_spanned(op),
                                               self.fold_expr(lhs),
                                               self.fold_expr(rhs))
                        }
                        other => other,
                    },
                    ..folded
                }
            })
        }

        fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
            TyParam {
                // default fold_ty_param does not fold the span
                span: self.new_span(tp.span),
                ..fold::noop_fold_ty_param(tp, self)
            }
        }

        fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
            decl.map(|FnDecl { inputs, output, variadic }| {
                FnDecl {
                    inputs: inputs.move_map(|x| self.fold_arg(x)),
                    output: match output {
                        FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
                        // default fold_fn_decl does not fold this span
                        FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
                    },
                    variadic: variadic,
                }
            })
        }

        fn fold_field(&mut self, field: Field) -> Field {
            Field {
                ident: codemap::respan(// default fold_field does not fold this span
                                       self.new_span(field.ident.span),
                                       self.fold_ident(field.ident.node)),
                expr: self.fold_expr(field.expr),
                span: self.new_span(field.span),
            }
        }

        fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
            let noop = fold::noop_fold_trait_item(i, self).expect_one("");
            SmallVector::one(TraitItem {
                node: match noop.node {
                    TraitItemKind::Method(sig, body) => {
                        TraitItemKind::Method(MethodSig {
                                                  constness: self.fold_spanned(sig.constness),
                                                  ..sig
                                              },
                                              body)
                    }
                    node => node,
                },
                ..noop
            })
        }

        fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
            let noop = fold::noop_fold_impl_item(i, self).expect_one("");
            SmallVector::one(ImplItem {
                node: match noop.node {
                    ImplItemKind::Method(sig, body) => {
                        ImplItemKind::Method(MethodSig {
                                                 constness: self.fold_spanned(sig.constness),
                                                 ..sig
                                             },
                                             body)
                    }
                    node => node,
                },
                ..noop
            })
        }

        fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
            at.node.id.0 = 0;
            fold::noop_fold_attribute(at, self)
        }

        fn fold_meta_item(&mut self, meta_item: P<MetaItem>) -> P<MetaItem> {
            meta_item.map(|Spanned {node, span}| Spanned {
                node: match node {
                    MetaItemKind::Word(id) => MetaItemKind::Word(id),
                    MetaItemKind::List(id, mis) => {
                        MetaItemKind::List(id, mis.move_map(|e| self.fold_meta_list_item(e)))
                    }
                    // default fold_meta_item does not fold the value span
                    MetaItemKind::NameValue(id, lit) => MetaItemKind::NameValue(id, self.fold_spanned(lit))
                },
                span: self.new_span(span)
            })
        }

        fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
            Spanned {
                node: match list_item.node {
                    NestedMetaItemKind::MetaItem(mi) =>  {
                        NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
                    },
                    // default fold_meta_list_item does not fold the span
                    NestedMetaItemKind::Literal(lit) => NestedMetaItemKind::Literal(self.fold_spanned(lit))
                },
                span: self.new_span(list_item.span)
            }
        }

        fn fold_mac(&mut self, mac: Mac) -> Mac {
            fold::noop_fold_mac(mac, self)
        }

        fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
            match *tt {
                TokenTree::Token(span, ref tok) => {
                    TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
                }
                TokenTree::Delimited(span, ref delimed) => {
                    TokenTree::Delimited(self.new_span(span),
                                         Rc::new(Delimited {
                                             delim: delimed.delim,
                                             open_span: self.new_span(delimed.open_span),
                                             tts: self.fold_tts(&delimed.tts),
                                             close_span: self.new_span(delimed.close_span),
                                         }))
                }
                TokenTree::Sequence(span, ref seq) => {
                    TokenTree::Sequence(self.new_span(span),
                                        Rc::new(SequenceRepetition {
                                            tts: self.fold_tts(&seq.tts),
                                            separator: seq.separator
                                                .clone()
                                                .map(|tok| self.fold_token(tok)),
                                            ..**seq
                                        }))
                }
            }
        }

        fn fold_token(&mut self, t: Token) -> Token {
            match t {
                // default fold_token does not fold literals
                Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
                Token::Ident(id) => Token::Ident(self.fold_ident(id)),
                Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
                Token::Interpolated(nt) => Token::Interpolated(self.fold_interpolated(nt)),
                Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
                Token::MatchNt(name, kind) => {
                    Token::MatchNt(self.fold_ident(name), self.fold_ident(kind))
                }
                _ => t,
            }
        }
    }

    Respanner.fold_crate(krate)
}
<commit_msg>Ignore test cases containing explicit paths to modules<commit_after>#![cfg(feature = "full")]

#[macro_use]
extern crate quote;
extern crate syn;
extern crate syntex_pos;
extern crate syntex_syntax;
extern crate time;
extern crate walkdir;

use syntex_pos::Span;
use syntex_syntax::ast;
use syntex_syntax::parse::{self, ParseSess, PResult};
use time::PreciseTime;

use std::fs::File;
use std::io::{self, Read, Write};

macro_rules! errorf {
    ($($tt:tt)*) => {
        write!(io::stderr(), $($tt)*).unwrap();
    };
}

#[test]
fn test_round_trip() {
    let mut failed = 0;

    let walk = walkdir::WalkDir::new("tests/cases").sort_by(|a, b| a.cmp(b));
    for entry in walk.into_iter() {
        let entry = entry.unwrap();

        let path = entry.path();
        if path.extension().map(|e| e != "rs").unwrap_or(true) {
            continue;
        }
        errorf!("=== {}: ", path.display());

        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        let start = PreciseTime::now();
        let (krate, elapsed) = match syn::parse_crate(&content) {
            Ok(krate) => (krate, start.to(PreciseTime::now())),
            Err(msg) => {
                errorf!("syn failed to parse\n{}\n", msg);
                failed += 1;
                continue;
            }
        };
        let back = quote!(#krate).to_string();

        let sess = ParseSess::new();
        let before = match syntex_parse(content, &sess) {
            Ok(before) => before,
            Err(mut diagnostic) => {
                diagnostic.cancel();
                if diagnostic.message.starts_with("file not found for module")
                        || diagnostic.message.starts_with("couldn't read") {
                    errorf!("ignore\n");
                    continue;
                } else {
                    panic!(diagnostic.message.clone());
                }
            }
        };
        let after = match syntex_parse(back, &sess) {
            Ok(after) => after,
            Err(mut diagnostic) => {
                errorf!("syntex failed to parse");
                diagnostic.emit();
                failed += 1;
                continue;
            }
        };

        if before == after {
            errorf!("pass in {}ms\n", elapsed.num_milliseconds());
        } else {
            errorf!("FAIL\nbefore: {:?}\nafter: {:?}\n", before, after);
            failed += 1;
        }
    }

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn syntex_parse<'a>(content: String, sess: &'a ParseSess) -> PResult<'a, ast::Crate> {
    let name = "test_round_trip".to_string();
    let cfg = Vec::new();
    parse::parse_crate_from_source_str(name, content, cfg, sess).map(respan_crate)
}

fn respan_crate(krate: ast::Crate) -> ast::Crate {
    use std::rc::Rc;
    use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
                             ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
                             NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind,
                             TyParam};
    use syntex_syntax::codemap::{self, Spanned};
    use syntex_syntax::fold::{self, Folder};
    use syntex_syntax::parse::token::{Lit, Token, intern};
    use syntex_syntax::ptr::P;
    use syntex_syntax::tokenstream::{Delimited, SequenceRepetition, TokenTree};
    use syntex_syntax::util::move_map::MoveMap;
    use syntex_syntax::util::small_vector::SmallVector;

    struct Respanner;

    impl Respanner {
        fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
            codemap::respan(self.new_span(spanned.span), spanned.node)
        }

        fn fold_lit(&mut self, l: Lit) -> Lit {
            match l {
                Lit::Integer(repr) => {
                    Lit::Integer(intern(&repr.to_string().replace("_", "")))
                }
                Lit::Str_(_) => {
                    // Give up on comparing strings because there are so many
                    // equivalent representations of the same string; they are
                    // tested elsewhere
                    Lit::Str_(intern(""))
                }
                Lit::Float(repr) => {
                    Lit::Float(intern(&repr.to_string().replace("_", "")))
                }
                _ => l,
            }
        }
    }

    impl Folder for Respanner {
        fn new_span(&mut self, _: Span) -> Span {
            syntex_pos::DUMMY_SP
        }

        fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
            match i {
                ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
                    let generics = self.fold_generics(generics);
                    let decl = self.fold_fn_decl(decl);
                    let body = self.fold_block(body);
                    // default fold_item_kind does not fold this span
                    let constness = self.fold_spanned(constness);
                    ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
                }
                _ => fold::noop_fold_item_kind(i, self),
            }
        }

        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
            e.map(|e| {
                let folded = fold::noop_fold_expr(e, self);
                Expr {
                    node: match folded.node {
                        ExprKind::Lit(l) => {
                            // default fold_expr does not fold lits
                            ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
                        }
                        ExprKind::Binary(op, lhs, rhs) => {
                            // default fold_expr does not fold the op span
                            ExprKind::Binary(self.fold_spanned(op),
                                             self.fold_expr(lhs),
                                             self.fold_expr(rhs))
                        }
                        ExprKind::AssignOp(op, lhs, rhs) => {
                            // default fold_expr does not fold the op span
                            ExprKind::AssignOp(self.fold_spanned(op),
                                               self.fold_expr(lhs),
                                               self.fold_expr(rhs))
                        }
                        other => other,
                    },
                    ..folded
                }
            })
        }

        fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
            TyParam {
                // default fold_ty_param does not fold the span
                span: self.new_span(tp.span),
                ..fold::noop_fold_ty_param(tp, self)
            }
        }

        fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
            decl.map(|FnDecl { inputs, output, variadic }| {
                FnDecl {
                    inputs: inputs.move_map(|x| self.fold_arg(x)),
                    output: match output {
                        FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
                        // default fold_fn_decl does not fold this span
                        FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
                    },
                    variadic: variadic,
                }
            })
        }

        fn fold_field(&mut self, field: Field) -> Field {
            Field {
                ident: codemap::respan(// default fold_field does not fold this span
                                       self.new_span(field.ident.span),
                                       self.fold_ident(field.ident.node)),
                expr: self.fold_expr(field.expr),
                span: self.new_span(field.span),
            }
        }

        fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
            let noop = fold::noop_fold_trait_item(i, self).expect_one("");
            SmallVector::one(TraitItem {
                node: match noop.node {
                    TraitItemKind::Method(sig, body) => {
                        TraitItemKind::Method(MethodSig {
                                                  constness: self.fold_spanned(sig.constness),
                                                  ..sig
                                              },
                                              body)
                    }
                    node => node,
                },
                ..noop
            })
        }

        fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
            let noop = fold::noop_fold_impl_item(i, self).expect_one("");
            SmallVector::one(ImplItem {
                node: match noop.node {
                    ImplItemKind::Method(sig, body) => {
                        ImplItemKind::Method(MethodSig {
                                                 constness: self.fold_spanned(sig.constness),
                                                 ..sig
                                             },
                                             body)
                    }
                    node => node,
                },
                ..noop
            })
        }

        fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
            at.node.id.0 = 0;
            fold::noop_fold_attribute(at, self)
        }

        fn fold_meta_item(&mut self, meta_item: P<MetaItem>) -> P<MetaItem> {
            meta_item.map(|Spanned {node, span}| Spanned {
                node: match node {
                    MetaItemKind::Word(id) => MetaItemKind::Word(id),
                    MetaItemKind::List(id, mis) => {
                        MetaItemKind::List(id, mis.move_map(|e| self.fold_meta_list_item(e)))
                    }
                    // default fold_meta_item does not fold the value span
                    MetaItemKind::NameValue(id, lit) => MetaItemKind::NameValue(id, self.fold_spanned(lit))
                },
                span: self.new_span(span)
            })
        }

        fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
            Spanned {
                node: match list_item.node {
                    NestedMetaItemKind::MetaItem(mi) =>  {
                        NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
                    },
                    // default fold_meta_list_item does not fold the span
                    NestedMetaItemKind::Literal(lit) => NestedMetaItemKind::Literal(self.fold_spanned(lit))
                },
                span: self.new_span(list_item.span)
            }
        }

        fn fold_mac(&mut self, mac: Mac) -> Mac {
            fold::noop_fold_mac(mac, self)
        }

        fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
            match *tt {
                TokenTree::Token(span, ref tok) => {
                    TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
                }
                TokenTree::Delimited(span, ref delimed) => {
                    TokenTree::Delimited(self.new_span(span),
                                         Rc::new(Delimited {
                                             delim: delimed.delim,
                                             open_span: self.new_span(delimed.open_span),
                                             tts: self.fold_tts(&delimed.tts),
                                             close_span: self.new_span(delimed.close_span),
                                         }))
                }
                TokenTree::Sequence(span, ref seq) => {
                    TokenTree::Sequence(self.new_span(span),
                                        Rc::new(SequenceRepetition {
                                            tts: self.fold_tts(&seq.tts),
                                            separator: seq.separator
                                                .clone()
                                                .map(|tok| self.fold_token(tok)),
                                            ..**seq
                                        }))
                }
            }
        }

        fn fold_token(&mut self, t: Token) -> Token {
            match t {
                // default fold_token does not fold literals
                Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
                Token::Ident(id) => Token::Ident(self.fold_ident(id)),
                Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
                Token::Interpolated(nt) => Token::Interpolated(self.fold_interpolated(nt)),
                Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
                Token::MatchNt(name, kind) => {
                    Token::MatchNt(self.fold_ident(name), self.fold_ident(kind))
                }
                _ => t,
            }
        }
    }

    Respanner.fold_crate(krate)
}
<|endoftext|>


<commit_before>#![cfg(unix)]

extern crate rexpect;

use rexpect::spawn;
use rexpect::session::PtySession;
use rexpect::errors::*;

macro_rules! test {
    ($b: block) => {
        if ::std::env::var("GLUON_PATH").is_err() {
            ::std::env::set_var("GLUON_PATH", "..");
        }

        || -> Result<()> {
            $b
            Ok(())
        }().unwrap_or_else(|err| panic!("{}", err));
    };
}

struct REPL {
    session: PtySession,
    prompt: &'static str,
}

impl REPL {
    /// Defines the command, timeout, and prompt settings.
    /// Wraps a rexpect::session::PtySession. expecting the prompt after launch.
    fn new() -> Result<REPL> {
        let command = "../target/debug/gluon -i";
        let timeout: u64 = 10_000;
        let prompt: &'static str = "> ";

        let mut session = spawn(command, Some(timeout))?;
        session.exp_string(prompt)?;

        Ok(REPL { session, prompt })
    }

    /// Ensures certain lines are expected to reduce race conditions.
    /// If no ouput is expected or desired to be tested, pass it an Option::None,
    /// causing rexpect to wait for the next prompt.
    fn test(&mut self, send: &str, expect: Option<&str>) -> Result<()> {
        self.session.send_line(send)?;
        self.session.exp_string(send)?;

        if let Some(string) = expect {
            self.session.exp_string(string)?;
        }

        self.session.exp_string(self.prompt)?;
        Ok(())
    }

    fn quit(&mut self) -> Result<()> {
        let line: &'static str = ":q";
        self.session.send_line(line)?;
        self.session.exp_string(line)?;
        self.session.exp_eof()?;
        Ok(())
    }
}

#[test]
fn prompt() {
    test!({
        let mut repl = REPL::new()?;
    });
}

#[test]
fn exit() {
    test!({
        let mut repl = REPL::new()?;
        repl.quit()?;
    });
}

#[test]
fn hello_world() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let io = import! std.io", None)?;
        repl.test("io.println \"Hello world\"", Some("Hello world"))?;
    });
}

#[test]
fn expression_types() {
    test!({
        let mut repl = REPL::new()?;

        repl.test(":t 5", Some("Int"))?;
        // repl.test(":t 5 + 5", Some("Int -> Int"))?;
        repl.test(":t \"gluon\"", Some("String"))?;
    });
}

#[test]
fn names() {
    test!({
        let mut repl = REPL::new()?;

        repl.test(
            ":i std.prelude.show",
            Some("std.prelude.show: forall a . [std.prelude.Show a] -> a -> String"),
        )?;
    });
}

#[test]
fn comments() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("1 + 2 // Calls the + function on 1 and 2", Some("3"))?;
        repl.test("1 + 2 /* Calls the + function on 1 and 2 */", Some("3"))?;
    });
}

#[test]
fn if_expressions() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("if True then 1 else 0", Some("1"))?;
        repl.test("if False then 1 else 0", Some("0"))?;
    });
}

#[test]
fn records() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let record = { pi = 3.14, add1 = (+) 1.0 }", None)?;
        repl.test("record.pi", Some("3.14"))?;

        repl.test("let record_2 = {x = 1 .. record }", None)?;
        repl.test("record_2.x", Some("1"))?;
        repl.test("record_2.pi", Some("3.14"))?;
    });
}

#[test]
fn arrays() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let array = import! std.array", None)?;
        repl.test("array.len [1, 2, 3]", Some("3"))?;
    });
}
<commit_msg>Rename exit test to quit.<commit_after>#![cfg(unix)]

extern crate rexpect;

use rexpect::spawn;
use rexpect::session::PtySession;
use rexpect::errors::*;

macro_rules! test {
    ($b: block) => {
        if ::std::env::var("GLUON_PATH").is_err() {
            ::std::env::set_var("GLUON_PATH", "..");
        }

        || -> Result<()> {
            $b
            Ok(())
        }().unwrap_or_else(|err| panic!("{}", err));
    };
}

struct REPL {
    session: PtySession,
    prompt: &'static str,
}

impl REPL {
    /// Defines the command, timeout, and prompt settings.
    /// Wraps a rexpect::session::PtySession. expecting the prompt after launch.
    fn new() -> Result<REPL> {
        let command = "../target/debug/gluon -i";
        let timeout: u64 = 10_000;
        let prompt: &'static str = "> ";

        let mut session = spawn(command, Some(timeout))?;
        session.exp_string(prompt)?;

        Ok(REPL { session, prompt })
    }

    /// Ensures certain lines are expected to reduce race conditions.
    /// If no ouput is expected or desired to be tested, pass it an Option::None,
    /// causing rexpect to wait for the next prompt.
    fn test(&mut self, send: &str, expect: Option<&str>) -> Result<()> {
        self.session.send_line(send)?;
        self.session.exp_string(send)?;

        if let Some(string) = expect {
            self.session.exp_string(string)?;
        }

        self.session.exp_string(self.prompt)?;
        Ok(())
    }

    fn quit(&mut self) -> Result<()> {
        let line: &'static str = ":q";
        self.session.send_line(line)?;
        self.session.exp_string(line)?;
        self.session.exp_eof()?;
        Ok(())
    }
}

#[test]
fn prompt() {
    test!({
        let mut repl = REPL::new()?;
    });
}

#[test]
fn quit() {
    test!({
        let mut repl = REPL::new()?;
        repl.quit()?;
    });
}

#[test]
fn hello_world() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let io = import! std.io", None)?;
        repl.test("io.println \"Hello world\"", Some("Hello world"))?;
    });
}

#[test]
fn expression_types() {
    test!({
        let mut repl = REPL::new()?;

        repl.test(":t 5", Some("Int"))?;
        // repl.test(":t 5 + 5", Some("Int -> Int"))?;
        repl.test(":t \"gluon\"", Some("String"))?;
    });
}

#[test]
fn names() {
    test!({
        let mut repl = REPL::new()?;

        repl.test(
            ":i std.prelude.show",
            Some("std.prelude.show: forall a . [std.prelude.Show a] -> a -> String"),
        )?;
    });
}

#[test]
fn comments() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("1 + 2 // Calls the + function on 1 and 2", Some("3"))?;
        repl.test("1 + 2 /* Calls the + function on 1 and 2 */", Some("3"))?;
    });
}

#[test]
fn if_expressions() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("if True then 1 else 0", Some("1"))?;
        repl.test("if False then 1 else 0", Some("0"))?;
    });
}

#[test]
fn records() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let record = { pi = 3.14, add1 = (+) 1.0 }", None)?;
        repl.test("record.pi", Some("3.14"))?;

        repl.test("let record_2 = {x = 1 .. record }", None)?;
        repl.test("record_2.x", Some("1"))?;
        repl.test("record_2.pi", Some("3.14"))?;
    });
}

#[test]
fn arrays() {
    test!({
        let mut repl = REPL::new()?;

        repl.test("let array = import! std.array", None)?;
        repl.test("array.len [1, 2, 3]", Some("3"))?;
    });
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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.

use std::mem;
use std::str::FromStr;

use bevy::ecs::component::Component;
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use serde::{Deserialize, Serialize};

use crate::config::scoring::ScoringConfig;
use crate::model::{Scenario, World};
use crate::storage::Storage;
use crate::storage::sqlite::SqliteStorage;
use crate::world::Planet;
use crate::SaverState;

use self::scoring_function::Expression;

mod scoring_function;

pub struct ScoringPlugin;

impl Plugin for ScoringPlugin {
    fn build(&self, app: &mut AppBuilder) {
        app.init_resource::<ActiveWorld>()
            .add_startup_system(setup.system())
            .add_system_set(
                SystemSet::on_update(SaverState::Run)
                    .with_system(score.system().label("compute-score"))
                    .with_system(score_text.system().after("compute-score")),
            )
            .add_system_set(
                SystemSet::on_exit(SaverState::Run)
                    .with_system(store_result::<SqliteStorage>.system()));
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(transparent)]
pub struct ScoringFunction(Expression);

impl ScoringFunction {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, elapsed_fract: f64, total_mass: f64, mass_count: f64) -> f64 {
        self.0.eval(elapsed_fract, total_mass, mass_count)
    }
}

impl FromStr for ScoringFunction {
    type Err = String;

    fn from_str(source: &str) -> Result<ScoringFunction, String> {
        source.parse().map(ScoringFunction)
    }
}

/// Resource for tracking the status of the currently active scene.
pub struct ActiveWorld {
    /// The world being scored.
    pub world: World,
    /// The parent of the world being scored.
    pub parent: Option<Scenario>,
    /// The score the world has received so far.
    pub cumulative_score: f64,
    /// The number of physics ticks that the world has been scored on so far.
    pub timer: Timer,
}

impl ActiveWorld {
    /// Reset the active world for a new scenario.
    pub fn start(&mut self, world: World, parent: Option<Scenario>) {
        self.world = world;
        self.parent = parent;
        self.cumulative_score = 0.0;
        self.timer.reset();
    }
}

impl FromWorld for ActiveWorld {
    fn from_world(world: &mut bevy::ecs::world::World) -> Self {
        let config = world.get_resource::<ScoringConfig>().unwrap();
        ActiveWorld {
            world: World { planets: vec![] },
            parent: None,
            cumulative_score: 0.,
            timer: Timer::new(config.scored_time, false),
        }
    }
}

/// Marker component for the score text entity.
struct ScoreText;

/// Adds a ui camera and score keeper text.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn_bundle(UiCameraBundle::default());
    commands
        .spawn_bundle(TextBundle {
            style: Style {
                align_self: AlignSelf::FlexEnd,
                ..Default::default()
            },
            text: Text {
                sections: vec![
                    TextSection {
                        value: "Score: ".to_string(),
                        style: TextStyle {
                            font: asset_server.load("fonts/FiraSans-Book.ttf"),
                            font_size: 60.0,
                            color: Color::WHITE,
                        },
                    },
                    TextSection {
                        value: "".to_string(),
                        style: TextStyle {
                            font: asset_server.load("fonts/FiraMono-Regular.ttf"),
                            font_size: 60.0,
                            color: Color::GOLD,
                        },
                    },
                ],
                ..Default::default()
            },
            ..Default::default()
        })
        .insert(ScoreText);
}

/// Compute the scenario score for each frame.
fn score(
    time: Res<Time>,
    mut world: ResMut<ActiveWorld>,
    config: Res<ScoringConfig>,
    query: Query<&RigidBodyMassProps, With<Planet>>,
    mut state: ResMut<State<SaverState>>,
) {
    world.timer.tick(time.delta());

    let scenario_time = world.timer.percent() as f64;
    let mut mass_count = 0.0;
    let mut total_mass = 0.0;

    let maxx = config.scored_area.width / 2.0;
    let maxy = config.scored_area.height / 2.0;
    let maxz = config.scored_area.depth / 2.0;

    for rb in query.iter() {
        if rb.world_com.x.abs() > maxx || rb.world_com.y.abs() > maxy || rb.world_com.z.abs() > maxz
        {
            continue;
        }
        mass_count += 1.0;
        total_mass += rb.mass() as f64;
    }

    world.cumulative_score += config
        .score_per_second
        .eval(scenario_time, total_mass, mass_count)
        * time.delta_seconds_f64();

    if world.timer.just_finished() {
        state
            .set(SaverState::Generate)
            .expect("Unable to switch to scenario generation");
    }
}

/// Put the score in the score text.
fn score_text(world: Res<ActiveWorld>, mut query: Query<&mut Text, With<ScoreText>>) {
    for mut text in query.iter_mut() {
        text.sections[1].value = format!("{:.2}", world.cumulative_score);
    }
}

/// Store scenario results.
fn store_result<S: Storage + Component>(mut tracker: ResMut<ActiveWorld>, mut storage: ResMut<S>) {
    info!("Storing scored world");
    let world = mem::replace(&mut tracker.world, World::default());
    let parent = mem::replace(&mut tracker.parent, None);
    let score = if tracker.cumulative_score.is_nan() {
        warn!("Score was NaN, replacing with -inf");
        f64::NEG_INFINITY
    } else {
        tracker.cumulative_score
    };
    let store_result = match parent {
        Some(parent) => storage.add_child_scenario(world, score, &parent),
        None => storage.add_root_scenario(world, score),
    };
    match store_result {
        Err(error) => error!("Error while storing finished scenario: {}", error),
        Ok(scenario) => info!(
            "Saved scenario {} (parent: {:?}, family: {}, generation: {}) with score {}",
            scenario.id,
            scenario.parent,
            scenario.family,
            scenario.generation,
            scenario.score,
        ),
    }
}

// #[derive(Default)]
// pub struct ScoreKeeper<T>(PhantomData<T>);
//
// impl<'a, T> System<'a> for ScoreKeeper<T> where T: Storage + Default + Send + Sync + 'static {
//     type SystemData = (
//         Read<'a, ScoringConfig>,
//         Write<'a, ActiveWorld>,
//         Write<'a, T>,
//         Write<'a, SceneChange>,
//         ReadStorage<'a, Position>,
//         ReadStorage<'a, Mass>,
//         ReadStorage<'a, Deleted>,
//         area_scaling::AreaScalingData<'a>,
//     );
//
//     fn run(
//         &mut self,
//         (
//             scoring,
//             mut world_track,
//             mut storage,
//             mut scene_change,
//             positions,
//             masses,
//             deleted,
//             scaling_data,
//         ): Self::SystemData,
//     ) {
//         if world_track.ticks_completed < scoring.scored_ticks {
//             let vertical_half_extent = scoring.scored_area.height / 2.;
//             let horizontal_half_extent = if scoring.scored_area.scale_width_by_aspect {
//                 let aspect = area_scaling::get_aspect(&scaling_data);
//                 scoring.scored_area.width * aspect
//             } else {
//                 scoring.scored_area.width
//             } / 2.;
//
//             let mut mass_count = 0f64;
//             let mut total_mass = 0f64;
//             for (position, mass, _) in (&positions, &masses, !&deleted).join() {
//                 let pos = position.pos();
//                 if pos.x.abs() <= horizontal_half_extent && pos.y.abs() <= vertical_half_extent {
//                     mass_count += 1.;
//                     total_mass += mass.linear as f64
//                 }
//             }
//             world_track.cumulative_score += scoring.per_frame_scoring
//                 .eval(world_track.ticks_completed as f64, total_mass, mass_count);
//             world_track.ticks_completed += 1;
//         } else {
//             info!("Storing scored world");
//             let world = mem::replace(&mut world_track.world, World::default());
//             let parent = mem::replace(&mut world_track.parent, None);
//             let score = match mem::replace(&mut world_track.cumulative_score, 0.) {
//                 score if score.is_nan() => {
//                     warn!("Score was NaN, replacing with -inf");
//                     ::std::f64::NEG_INFINITY
//                 },
//                 score => score,
//             };
//             world_track.ticks_completed = 0;
//             let store_result = match parent {
//                 Some(parent) => storage.add_child_scenario(world, score, &parent),
//                 None => storage.add_root_scenario(world, score),
//             };
//             match store_result {
//                 Err(error) => error!("Error while storing finished scenario: {}", error),
//                 Ok(scenario) => info!(
//                     "Saved scenario {} (parent: {:?}, family: {}, generation: {}) with score {}",
//                     scenario.id,
//                     scenario.parent,
//                     scenario.family,
//                     scenario.generation,
//                     scenario.score,
//                 ),
//             }
//             scene_change.change_scene(WorldGenerator::<T>::default());
//         }
//     }
// }
<commit_msg>cargo fmt<commit_after>// Copyright 2018 Google LLC
//
// 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.

use std::mem;
use std::str::FromStr;

use bevy::ecs::component::Component;
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use serde::{Deserialize, Serialize};

use crate::config::scoring::ScoringConfig;
use crate::model::{Scenario, World};
use crate::storage::sqlite::SqliteStorage;
use crate::storage::Storage;
use crate::world::Planet;
use crate::SaverState;

use self::scoring_function::Expression;

mod scoring_function;

pub struct ScoringPlugin;

impl Plugin for ScoringPlugin {
    fn build(&self, app: &mut AppBuilder) {
        app.init_resource::<ActiveWorld>()
            .add_startup_system(setup.system())
            .add_system_set(
                SystemSet::on_update(SaverState::Run)
                    .with_system(score.system().label("compute-score"))
                    .with_system(score_text.system().after("compute-score")),
            )
            .add_system_set(
                SystemSet::on_exit(SaverState::Run)
                    .with_system(store_result::<SqliteStorage>.system()),
            );
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(transparent)]
pub struct ScoringFunction(Expression);

impl ScoringFunction {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, elapsed_fract: f64, total_mass: f64, mass_count: f64) -> f64 {
        self.0.eval(elapsed_fract, total_mass, mass_count)
    }
}

impl FromStr for ScoringFunction {
    type Err = String;

    fn from_str(source: &str) -> Result<ScoringFunction, String> {
        source.parse().map(ScoringFunction)
    }
}

/// Resource for tracking the status of the currently active scene.
pub struct ActiveWorld {
    /// The world being scored.
    pub world: World,
    /// The parent of the world being scored.
    pub parent: Option<Scenario>,
    /// The score the world has received so far.
    pub cumulative_score: f64,
    /// The number of physics ticks that the world has been scored on so far.
    pub timer: Timer,
}

impl ActiveWorld {
    /// Reset the active world for a new scenario.
    pub fn start(&mut self, world: World, parent: Option<Scenario>) {
        self.world = world;
        self.parent = parent;
        self.cumulative_score = 0.0;
        self.timer.reset();
    }
}

impl FromWorld for ActiveWorld {
    fn from_world(world: &mut bevy::ecs::world::World) -> Self {
        let config = world.get_resource::<ScoringConfig>().unwrap();
        ActiveWorld {
            world: World { planets: vec![] },
            parent: None,
            cumulative_score: 0.,
            timer: Timer::new(config.scored_time, false),
        }
    }
}

/// Marker component for the score text entity.
struct ScoreText;

/// Adds a ui camera and score keeper text.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn_bundle(UiCameraBundle::default());
    commands
        .spawn_bundle(TextBundle {
            style: Style {
                align_self: AlignSelf::FlexEnd,
                ..Default::default()
            },
            text: Text {
                sections: vec![
                    TextSection {
                        value: "Score: ".to_string(),
                        style: TextStyle {
                            font: asset_server.load("fonts/FiraSans-Book.ttf"),
                            font_size: 60.0,
                            color: Color::WHITE,
                        },
                    },
                    TextSection {
                        value: "".to_string(),
                        style: TextStyle {
                            font: asset_server.load("fonts/FiraMono-Regular.ttf"),
                            font_size: 60.0,
                            color: Color::GOLD,
                        },
                    },
                ],
                ..Default::default()
            },
            ..Default::default()
        })
        .insert(ScoreText);
}

/// Compute the scenario score for each frame.
fn score(
    time: Res<Time>,
    mut world: ResMut<ActiveWorld>,
    config: Res<ScoringConfig>,
    query: Query<&RigidBodyMassProps, With<Planet>>,
    mut state: ResMut<State<SaverState>>,
) {
    world.timer.tick(time.delta());

    let scenario_time = world.timer.percent() as f64;
    let mut mass_count = 0.0;
    let mut total_mass = 0.0;

    let maxx = config.scored_area.width / 2.0;
    let maxy = config.scored_area.height / 2.0;
    let maxz = config.scored_area.depth / 2.0;

    for rb in query.iter() {
        if rb.world_com.x.abs() > maxx || rb.world_com.y.abs() > maxy || rb.world_com.z.abs() > maxz
        {
            continue;
        }
        mass_count += 1.0;
        total_mass += rb.mass() as f64;
    }

    world.cumulative_score += config
        .score_per_second
        .eval(scenario_time, total_mass, mass_count)
        * time.delta_seconds_f64();

    if world.timer.just_finished() {
        state
            .set(SaverState::Generate)
            .expect("Unable to switch to scenario generation");
    }
}

/// Put the score in the score text.
fn score_text(world: Res<ActiveWorld>, mut query: Query<&mut Text, With<ScoreText>>) {
    for mut text in query.iter_mut() {
        text.sections[1].value = format!("{:.2}", world.cumulative_score);
    }
}

/// Store scenario results.
fn store_result<S: Storage + Component>(mut tracker: ResMut<ActiveWorld>, mut storage: ResMut<S>) {
    info!("Storing scored world");
    let world = mem::replace(&mut tracker.world, World::default());
    let parent = mem::replace(&mut tracker.parent, None);
    let score = if tracker.cumulative_score.is_nan() {
        warn!("Score was NaN, replacing with -inf");
        f64::NEG_INFINITY
    } else {
        tracker.cumulative_score
    };
    let store_result = match parent {
        Some(parent) => storage.add_child_scenario(world, score, &parent),
        None => storage.add_root_scenario(world, score),
    };
    match store_result {
        Err(error) => error!("Error while storing finished scenario: {}", error),
        Ok(scenario) => info!(
            "Saved scenario {} (parent: {:?}, family: {}, generation: {}) with score {}",
            scenario.id, scenario.parent, scenario.family, scenario.generation, scenario.score,
        ),
    }
}

// #[derive(Default)]
// pub struct ScoreKeeper<T>(PhantomData<T>);
//
// impl<'a, T> System<'a> for ScoreKeeper<T> where T: Storage + Default + Send + Sync + 'static {
//     type SystemData = (
//         Read<'a, ScoringConfig>,
//         Write<'a, ActiveWorld>,
//         Write<'a, T>,
//         Write<'a, SceneChange>,
//         ReadStorage<'a, Position>,
//         ReadStorage<'a, Mass>,
//         ReadStorage<'a, Deleted>,
//         area_scaling::AreaScalingData<'a>,
//     );
//
//     fn run(
//         &mut self,
//         (
//             scoring,
//             mut world_track,
//             mut storage,
//             mut scene_change,
//             positions,
//             masses,
//             deleted,
//             scaling_data,
//         ): Self::SystemData,
//     ) {
//         if world_track.ticks_completed < scoring.scored_ticks {
//             let vertical_half_extent = scoring.scored_area.height / 2.;
//             let horizontal_half_extent = if scoring.scored_area.scale_width_by_aspect {
//                 let aspect = area_scaling::get_aspect(&scaling_data);
//                 scoring.scored_area.width * aspect
//             } else {
//                 scoring.scored_area.width
//             } / 2.;
//
//             let mut mass_count = 0f64;
//             let mut total_mass = 0f64;
//             for (position, mass, _) in (&positions, &masses, !&deleted).join() {
//                 let pos = position.pos();
//                 if pos.x.abs() <= horizontal_half_extent && pos.y.abs() <= vertical_half_extent {
//                     mass_count += 1.;
//                     total_mass += mass.linear as f64
//                 }
//             }
//             world_track.cumulative_score += scoring.per_frame_scoring
//                 .eval(world_track.ticks_completed as f64, total_mass, mass_count);
//             world_track.ticks_completed += 1;
//         } else {
//             info!("Storing scored world");
//             let world = mem::replace(&mut world_track.world, World::default());
//             let parent = mem::replace(&mut world_track.parent, None);
//             let score = match mem::replace(&mut world_track.cumulative_score, 0.) {
//                 score if score.is_nan() => {
//                     warn!("Score was NaN, replacing with -inf");
//                     ::std::f64::NEG_INFINITY
//                 },
//                 score => score,
//             };
//             world_track.ticks_completed = 0;
//             let store_result = match parent {
//                 Some(parent) => storage.add_child_scenario(world, score, &parent),
//                 None => storage.add_root_scenario(world, score),
//             };
//             match store_result {
//                 Err(error) => error!("Error while storing finished scenario: {}", error),
//                 Ok(scenario) => info!(
//                     "Saved scenario {} (parent: {:?}, family: {}, generation: {}) with score {}",
//                     scenario.id,
//                     scenario.parent,
//                     scenario.family,
//                     scenario.generation,
//                     scenario.score,
//                 ),
//             }
//             scene_change.change_scene(WorldGenerator::<T>::default());
//         }
//     }
// }
<|endoftext|>