<commit_before>//! Contains the `Snes` struct, which wields the combined power of this project.

use apu::Apu;
use cpu::Cpu;
use dma::{do_dma, do_hdma, DmaChannel};
use frontend::Renderer;
use input::Input;
use log_util::LogOnPanic;
use ppu::Ppu;
use rom::Rom;

const WRAM_SIZE: usize = 128 * 1024;
byte_array!(Wram[WRAM_SIZE]);

/// Contains everything connected to the CPU via one of the two address buses. All memory accesses
/// will be directed through this (the CPU already takes access time into account).
pub struct Peripherals {
    apu: Apu,
    ppu: Ppu,
    rom: Rom,
    /// The 128 KB of working RAM of the SNES (separate from cartridge RAM)
    wram: Wram,
    input: Input,

    pub dma: [DmaChannel; 8],
    /// `$420c` - HDMAEN: HDMA enable flags
    /// (Note that general DMA doesn't have a register here, since all transactions are started
    /// immediately and the register can't be read)
    hdmaen: u8,
    /// `$4200` - NMITIMEN: Interrupt enable flags
    /// `n-xy---a`
    /// * `n`: Enable NMI on V-Blank
    /// * `x`: Enable IRQ on H-Counter match
    /// * `y`: Enable IRQ on V-Counter match
    /// * `a`: Enable auto-joypad read
    nmien: u8,
    /// `$4204`/`$4205` - WRDIVH/WRDIVL: Dividend C
    wrdiv: u16,
    /// `$4206` - WRDIVB: Divisor B
    wrdivb: u8,
    /// `$4207`/`$4208` - HTIMEL/HTIMEH: H Timer (9-bit value)
    htime: u16,
    /// `$4209`/`$420a` - VTIMEL/VTIMEH: V Timer (9-bit value)
    vtime: u16,
    /// `$4210` NMI flag and 5A22 Version
    /// `n---vvvv`
    /// * `n`: `self.nmi`
    /// * `v`: Version
    nmi: bool,
    /// `$4211` TIMEUP - IRQ flag
    /// `i-------`
    /// * `i`: IRQ flag (cleared on read)
    irq: bool,

    /// Additional cycles spent doing IO (in master clock cycles). This is reset before each CPU
    /// instruction and added to the cycle count returned by the CPU.
    cy: u32,
}

impl Peripherals {
    pub fn new(rom: Rom) -> Peripherals {
        Peripherals {
            rom: rom,
            apu: Apu::new(),
            ppu: Ppu::new(),
            wram: Wram::default(),
            input: Input::default(),
            dma: [DmaChannel::new(); 8],
            hdmaen: 0x00,
            nmien: 0x00,
            wrdiv: 0xffff,
            wrdivb: 0,
            htime: 0x1ff,
            vtime: 0x1ff,
            nmi: false,
            irq: false,
            cy: 0,
        }
    }

    pub fn load(&mut self, bank: u8, addr: u16) -> u8 {
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf => match addr {
                // Mirror of first 8k of WRAM
                0x0000 ... 0x1fff => self.wram[addr as usize],
                // PPU
                0x2100 ... 0x2133 => panic!("read from write-only PPU register ${:04X}", addr),
                0x2138 ... 0x213f => self.ppu.load(addr),
                // APU IO registers
                0x2140 ... 0x217f => self.apu.read_port((addr & 0b11) as u8),
                0x4016 | 0x4017 => self.input.load(addr),
                0x4210 => {
                    const CPU_VERSION: u8 = 2;  // FIXME Is 2 okay in all cases? Does anyone care?
                    let nmi = if self.nmi { 0x80 } else { 0 };
                    self.nmi = false;   // Cleared on read
                    nmi | CPU_VERSION
                }
                0x4211 => {
                    let val = if self.irq { 0x80 } else { 0 };
                    self.irq = false;
                    val
                }
                0x4218 ... 0x421f => self.input.load(addr),
                // DMA channels (0x43xr, where x is the channel and r is the channel register)
                0x4300 ... 0x43ff => self.dma[(addr as usize & 0x00f0) >> 4].load(addr as u8 & 0xf),
                0x8000 ... 0xffff => self.rom.loadb(bank, addr),
                _ => panic!("invalid/unimplemented load from ${:02X}:{:04X}", bank, addr)
            },
            // WRAM banks. The first 8k are mapped into the start of all banks.
            0x7e | 0x7f => self.wram[(bank as usize - 0x7e) * 65536 + addr as usize],
            0x40 ... 0x7d | 0xc0 ... 0xff => self.rom.loadb(bank, addr),
            _ => unreachable!(),    // Rust should know this!
        }
    }

    pub fn store(&mut self, bank: u8, addr: u16, value: u8) {
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf => match addr {
                0x0000 ... 0x1fff => self.wram[addr as usize] = value,
                // PPU registers. Let it deal with the access.
                0x2100 ... 0x2133 => self.ppu.store(addr, value),
                0x2138 ... 0x213f => panic!("store to read-only PPU register ${:04X}", addr),
                // APU IO registers.
                0x2140 ... 0x217f => self.apu.store_port((addr & 0b11) as u8, value),
                0x2180 ... 0x2183 => panic!("NYI: WRAM registers"),
                0x4016 => self.input.store(addr, value),
                0x4200 => {
                    // NMITIMEN - NMI/IRQ enable
                    // E-HV---J
                    // E: Enable NMI
                    // H: Enable IRQ on H-Counter
                    // V: Enable IRQ on V-Counter
                    // J: Enable Auto-Joypad-Read

                    // Check useless bits
                    if value & 0x4e != 0 { panic!("Invalid value for NMIEN: ${:02X}", value) }
                    self.nmien = value;
                }
                0x4204 => self.wrdiv = (self.wrdiv & 0xff00) | value as u16,
                0x4205 => self.wrdiv = ((value as u16) << 8) | (self.wrdiv & 0xff),
                0x4206 => self.wrdivb = value,
                0x4207 => self.htime = (self.htime & 0xff00) | value as u16,
                0x4208 => {
                    assert!(value & 0x01 == value, "invalid value for $4207: ${:02X}", value);
                    self.htime = ((value as u16) << 8) | (self.htime & 0xff);
                }
                0x4209 => self.vtime = (self.vtime & 0xff00) | value as u16,
                0x420a => {
                    assert!(value & 0x01 == value, "invalid value for $4209: ${:02X}", value);
                    self.vtime = ((value as u16) << 8) | (self.vtime & 0xff);
                }
                // MDMAEN - Party enable
                0x420b => self.cy += do_dma(self, value),
                // HDMAEN - HDMA enable
                0x420c => self.hdmaen = value,
                // DMA channels (0x43xr, where x is the channel and r is the channel register)
                0x4300 ... 0x43ff => {
                    self.dma[(addr as usize & 0x00f0) >> 4].store(addr as u8 & 0xf, value);
                }
                0x8000 ... 0xffff => self.rom.storeb(bank, addr, value),
                _ => panic!("invalid store: ${:02X} to ${:02X}:{:04X}", value, bank, addr)
            },
            // WRAM main banks
            0x7e | 0x7f => self.wram[(bank as usize - 0x7e) * 65536 + addr as usize] = value,
            0x40 ... 0x7d | 0xc0 ... 0xff => self.rom.storeb(bank, addr, value),
            _ => unreachable!(),    // Rust should know this!
        }
    }

    fn nmi_enabled(&self) -> bool { self.nmien & 0x80 != 0 }
    fn v_irq_enabled(&self) -> bool { self.nmien & 0x10 != 0 }
    fn h_irq_enabled(&self) -> bool { self.nmien & 0x20 != 0 }
}

pub struct Snes {
    cpu: Cpu,
    renderer: Box<Renderer>,
}

impl Snes {
    pub fn new(rom: Rom, renderer: Box<Renderer>) -> Snes {
        Snes {
            cpu: Cpu::new(Peripherals::new(rom)),
            renderer: renderer,
        }
    }

    pub fn run(&mut self) {
        /// Exit after this number of master clock cycles
        const CY_LIMIT: u64 = 150_000_000;
        /// Start tracing at this master cycle (0 to trace everything)
        const TRACE_START: u64 = CY_LIMIT - 10_000;

        /// Approximated APU clock divider. It's actually somewhere around 20.9..., which is why we
        /// can't directly use `MASTER_CLOCK_FREQ / APU_CLOCK_FREQ` (it would round down, which
        /// might not be critical, but better safe than sorry).
        const APU_DIVIDER: i32 = 21;

        // Master cycle counter, used only for debugging atm
        let mut master_cy: u64 = 0;
        let mut total_apu_cy: u64 = 0;
        let mut total_ppu_cy: u64 = 0;
        // Master clock cycles for the APU not yet accounted for (can be negative)
        let mut apu_master_cy_debt = 0;
        let mut ppu_master_cy_debt = 0;
        let working_cy = LogOnPanic::new(0);

        while master_cy < CY_LIMIT {
            if master_cy >= TRACE_START {
                self.cpu.trace = true;
                self.cpu.mem.apu.trace = true;
            }

            // Run a CPU instruction and calculate the master cycles elapsed
            self.cpu.mem.cy = 0;
            let cpu_master_cy = self.cpu.dispatch() as i32 + self.cpu.mem.cy as i32;
            master_cy += cpu_master_cy as u64;

            // Now we "owe" the other components a few cycles:
            apu_master_cy_debt += cpu_master_cy;
            ppu_master_cy_debt += cpu_master_cy;

            // Run all components until we no longer owe them:
            while apu_master_cy_debt > APU_DIVIDER {
                // (Since the APU uses lots of cycles to do stuff - lower clock rate and such - we
                // only run it if we owe it `APU_DIVIDER` master cycles - or one SPC700 cycle)
                let apu_master_cy = self.cpu.mem.apu.dispatch() as i32 * APU_DIVIDER;
                apu_master_cy_debt -= apu_master_cy;
                total_apu_cy += apu_master_cy as u64;
            }
            while ppu_master_cy_debt > 0 {
                let (cy, result) = self.cpu.mem.ppu.update();
                ppu_master_cy_debt -= cy as i32;
                total_ppu_cy += cy as u64;

                if result.hblank {
                    // FIXME Cycles
                    do_hdma(self.cpu.mem.hdmaen);
                }
                if result.last_pixel {
                    self.renderer.render(&*self.cpu.mem.ppu.framebuf);
                }
                if result.vblank {
                    self.cpu.mem.input.new_frame();
                    if self.cpu.mem.nmi_enabled() {
                        //trace!("V-Blank NMI triggered! Trace started!");
                        //self.cpu.trace = true;
                        self.cpu.mem.nmi = true;
                        self.cpu.trigger_nmi();
                        // XXX Break to handle the NMI immediately. Let's hope we don't owe the PPU
                        // too many cycles.
                        break;
                    }
                }
                if result.new_frame {
                    self.cpu.mem.nmi = false;   // Flag cleared on read or end of VBlank
                }

                let cpu = &mut self.cpu;
                if cpu.mem.ppu.v_counter() == cpu.mem.vtime && cpu.mem.v_irq_enabled() {
                    cpu.mem.irq = true;
                    cpu.trigger_irq();
                    break;
                }
                if cpu.mem.ppu.h_counter() == cpu.mem.htime && cpu.mem.h_irq_enabled() {
                    cpu.mem.irq = true;
                    cpu.trigger_irq();
                    break;
                }
            }

            working_cy.set(master_cy);
        }

        info!("EXITING. Master cycle count: {}, APU: {}, PPU: {}",
            master_cy, total_apu_cy, total_ppu_cy);
    }
}
<commit_msg>Remove the cycle limit<commit_after>//! Contains the `Snes` struct, which wields the combined power of this project.

use apu::Apu;
use cpu::Cpu;
use dma::{do_dma, do_hdma, DmaChannel};
use frontend::Renderer;
use input::Input;
use log_util::LogOnPanic;
use ppu::Ppu;
use rom::Rom;

const WRAM_SIZE: usize = 128 * 1024;
byte_array!(Wram[WRAM_SIZE]);

/// Contains everything connected to the CPU via one of the two address buses. All memory accesses
/// will be directed through this (the CPU already takes access time into account).
pub struct Peripherals {
    apu: Apu,
    ppu: Ppu,
    rom: Rom,
    /// The 128 KB of working RAM of the SNES (separate from cartridge RAM)
    wram: Wram,
    input: Input,

    pub dma: [DmaChannel; 8],
    /// `$420c` - HDMAEN: HDMA enable flags
    /// (Note that general DMA doesn't have a register here, since all transactions are started
    /// immediately and the register can't be read)
    hdmaen: u8,
    /// `$4200` - NMITIMEN: Interrupt enable flags
    /// `n-xy---a`
    /// * `n`: Enable NMI on V-Blank
    /// * `x`: Enable IRQ on H-Counter match
    /// * `y`: Enable IRQ on V-Counter match
    /// * `a`: Enable auto-joypad read
    nmien: u8,
    /// `$4204`/`$4205` - WRDIVH/WRDIVL: Dividend C
    wrdiv: u16,
    /// `$4206` - WRDIVB: Divisor B
    wrdivb: u8,
    /// `$4207`/`$4208` - HTIMEL/HTIMEH: H Timer (9-bit value)
    htime: u16,
    /// `$4209`/`$420a` - VTIMEL/VTIMEH: V Timer (9-bit value)
    vtime: u16,
    /// `$4210` NMI flag and 5A22 Version
    /// `n---vvvv`
    /// * `n`: `self.nmi`
    /// * `v`: Version
    nmi: bool,
    /// `$4211` TIMEUP - IRQ flag
    /// `i-------`
    /// * `i`: IRQ flag (cleared on read)
    irq: bool,

    /// Additional cycles spent doing IO (in master clock cycles). This is reset before each CPU
    /// instruction and added to the cycle count returned by the CPU.
    cy: u32,
}

impl Peripherals {
    pub fn new(rom: Rom) -> Peripherals {
        Peripherals {
            rom: rom,
            apu: Apu::new(),
            ppu: Ppu::new(),
            wram: Wram::default(),
            input: Input::default(),
            dma: [DmaChannel::new(); 8],
            hdmaen: 0x00,
            nmien: 0x00,
            wrdiv: 0xffff,
            wrdivb: 0,
            htime: 0x1ff,
            vtime: 0x1ff,
            nmi: false,
            irq: false,
            cy: 0,
        }
    }

    pub fn load(&mut self, bank: u8, addr: u16) -> u8 {
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf => match addr {
                // Mirror of first 8k of WRAM
                0x0000 ... 0x1fff => self.wram[addr as usize],
                // PPU
                0x2100 ... 0x2133 => panic!("read from write-only PPU register ${:04X}", addr),
                0x2138 ... 0x213f => self.ppu.load(addr),
                // APU IO registers
                0x2140 ... 0x217f => self.apu.read_port((addr & 0b11) as u8),
                0x4016 | 0x4017 => self.input.load(addr),
                0x4210 => {
                    const CPU_VERSION: u8 = 2;  // FIXME Is 2 okay in all cases? Does anyone care?
                    let nmi = if self.nmi { 0x80 } else { 0 };
                    self.nmi = false;   // Cleared on read
                    nmi | CPU_VERSION
                }
                0x4211 => {
                    let val = if self.irq { 0x80 } else { 0 };
                    self.irq = false;
                    val
                }
                0x4218 ... 0x421f => self.input.load(addr),
                // DMA channels (0x43xr, where x is the channel and r is the channel register)
                0x4300 ... 0x43ff => self.dma[(addr as usize & 0x00f0) >> 4].load(addr as u8 & 0xf),
                0x8000 ... 0xffff => self.rom.loadb(bank, addr),
                _ => panic!("invalid/unimplemented load from ${:02X}:{:04X}", bank, addr)
            },
            // WRAM banks. The first 8k are mapped into the start of all banks.
            0x7e | 0x7f => self.wram[(bank as usize - 0x7e) * 65536 + addr as usize],
            0x40 ... 0x7d | 0xc0 ... 0xff => self.rom.loadb(bank, addr),
            _ => unreachable!(),    // Rust should know this!
        }
    }

    pub fn store(&mut self, bank: u8, addr: u16, value: u8) {
        match bank {
            0x00 ... 0x3f | 0x80 ... 0xbf => match addr {
                0x0000 ... 0x1fff => self.wram[addr as usize] = value,
                // PPU registers. Let it deal with the access.
                0x2100 ... 0x2133 => self.ppu.store(addr, value),
                0x2138 ... 0x213f => panic!("store to read-only PPU register ${:04X}", addr),
                // APU IO registers.
                0x2140 ... 0x217f => self.apu.store_port((addr & 0b11) as u8, value),
                0x2180 ... 0x2183 => panic!("NYI: WRAM registers"),
                0x4016 => self.input.store(addr, value),
                0x4200 => {
                    // NMITIMEN - NMI/IRQ enable
                    // E-HV---J
                    // E: Enable NMI
                    // H: Enable IRQ on H-Counter
                    // V: Enable IRQ on V-Counter
                    // J: Enable Auto-Joypad-Read

                    // Check useless bits
                    if value & 0x4e != 0 { panic!("Invalid value for NMIEN: ${:02X}", value) }
                    self.nmien = value;
                }
                0x4204 => self.wrdiv = (self.wrdiv & 0xff00) | value as u16,
                0x4205 => self.wrdiv = ((value as u16) << 8) | (self.wrdiv & 0xff),
                0x4206 => self.wrdivb = value,
                0x4207 => self.htime = (self.htime & 0xff00) | value as u16,
                0x4208 => {
                    assert!(value & 0x01 == value, "invalid value for $4207: ${:02X}", value);
                    self.htime = ((value as u16) << 8) | (self.htime & 0xff);
                }
                0x4209 => self.vtime = (self.vtime & 0xff00) | value as u16,
                0x420a => {
                    assert!(value & 0x01 == value, "invalid value for $4209: ${:02X}", value);
                    self.vtime = ((value as u16) << 8) | (self.vtime & 0xff);
                }
                // MDMAEN - Party enable
                0x420b => self.cy += do_dma(self, value),
                // HDMAEN - HDMA enable
                0x420c => self.hdmaen = value,
                // DMA channels (0x43xr, where x is the channel and r is the channel register)
                0x4300 ... 0x43ff => {
                    self.dma[(addr as usize & 0x00f0) >> 4].store(addr as u8 & 0xf, value);
                }
                0x8000 ... 0xffff => self.rom.storeb(bank, addr, value),
                _ => panic!("invalid store: ${:02X} to ${:02X}:{:04X}", value, bank, addr)
            },
            // WRAM main banks
            0x7e | 0x7f => self.wram[(bank as usize - 0x7e) * 65536 + addr as usize] = value,
            0x40 ... 0x7d | 0xc0 ... 0xff => self.rom.storeb(bank, addr, value),
            _ => unreachable!(),    // Rust should know this!
        }
    }

    fn nmi_enabled(&self) -> bool { self.nmien & 0x80 != 0 }
    fn v_irq_enabled(&self) -> bool { self.nmien & 0x10 != 0 }
    fn h_irq_enabled(&self) -> bool { self.nmien & 0x20 != 0 }
}

pub struct Snes {
    cpu: Cpu,
    renderer: Box<Renderer>,
}

impl Snes {
    pub fn new(rom: Rom, renderer: Box<Renderer>) -> Snes {
        Snes {
            cpu: Cpu::new(Peripherals::new(rom)),
            renderer: renderer,
        }
    }

    pub fn run(&mut self) {
        /// Start tracing at this master cycle (0 to trace everything)
        const TRACE_START: u64 = !0;

        /// Approximated APU clock divider. It's actually somewhere around 20.9..., which is why we
        /// can't directly use `MASTER_CLOCK_FREQ / APU_CLOCK_FREQ` (it would round down, which
        /// might not be critical, but better safe than sorry).
        const APU_DIVIDER: i32 = 21;

        // Master cycle counter, used only for debugging atm
        let mut master_cy: u64 = 0;
        // Master clock cycles for the APU not yet accounted for (can be negative)
        let mut apu_master_cy_debt = 0;
        let mut ppu_master_cy_debt = 0;
        let working_cy = LogOnPanic::new(0);

        loop {
            if master_cy >= TRACE_START {
                self.cpu.trace = true;
                self.cpu.mem.apu.trace = true;
            }

            // Run a CPU instruction and calculate the master cycles elapsed
            self.cpu.mem.cy = 0;
            let cpu_master_cy = self.cpu.dispatch() as i32 + self.cpu.mem.cy as i32;
            master_cy += cpu_master_cy as u64;

            // Now we "owe" the other components a few cycles:
            apu_master_cy_debt += cpu_master_cy;
            ppu_master_cy_debt += cpu_master_cy;

            // Run all components until we no longer owe them:
            while apu_master_cy_debt > APU_DIVIDER {
                // (Since the APU uses lots of cycles to do stuff - lower clock rate and such - we
                // only run it if we owe it `APU_DIVIDER` master cycles - or one SPC700 cycle)
                let apu_master_cy = self.cpu.mem.apu.dispatch() as i32 * APU_DIVIDER;
                apu_master_cy_debt -= apu_master_cy;
            }
            while ppu_master_cy_debt > 0 {
                let (cy, result) = self.cpu.mem.ppu.update();
                ppu_master_cy_debt -= cy as i32;

                if result.hblank {
                    // FIXME Cycles
                    do_hdma(self.cpu.mem.hdmaen);
                }
                if result.last_pixel {
                    self.renderer.render(&*self.cpu.mem.ppu.framebuf);
                }
                if result.vblank {
                    self.cpu.mem.input.new_frame();
                    if self.cpu.mem.nmi_enabled() {
                        //trace!("V-Blank NMI triggered! Trace started!");
                        //self.cpu.trace = true;
                        self.cpu.mem.nmi = true;
                        self.cpu.trigger_nmi();
                        // XXX Break to handle the NMI immediately. Let's hope we don't owe the PPU
                        // too many cycles.
                        break;
                    }
                }
                if result.new_frame {
                    self.cpu.mem.nmi = false;   // Flag cleared on read or end of VBlank
                }

                let cpu = &mut self.cpu;
                if cpu.mem.ppu.v_counter() == cpu.mem.vtime && cpu.mem.v_irq_enabled() {
                    cpu.mem.irq = true;
                    cpu.trigger_irq();
                    break;
                }
                if cpu.mem.ppu.h_counter() == cpu.mem.htime && cpu.mem.h_irq_enabled() {
                    cpu.mem.irq = true;
                    cpu.trigger_irq();
                    break;
                }
            }

            working_cy.set(master_cy);
        }
    }
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::cmp::{min, max};
use core::mem::{size_of, zeroed, replace, swap, transmute};
use core::ptr;

/// The smallest number of elements that may be quicksorted.
/// Must be at least 9.
const MIN_QUICKSORT_ELEMS: uint = 10;

/// The maximum number of elements to be insertion sorted.
const MAX_INSERTION_SORT_ELEMS: uint = 42;

/// Controls the number of elements to be insertion sorted.
/// Higher values give more insertion sorted elements.
const INSERTION_SORT_FACTOR: uint = 450;

pub fn sort_by<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    if maybe_insertion_sort(v, compare) { return; }
    let heapsort_depth = (3 * log2(v.len())) / 2;
    do_introsort(v, compare, 0, heapsort_depth);
}

#[inline]
pub fn sort<T: Ord>(v: &mut [T]) {
    sort_by(v, &|&: a: &T, b| a.cmp(b));
}

fn introsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C, rec: u32, heapsort_depth: u32) {
    if maybe_insertion_sort(v, compare) { return; }
    do_introsort(v, compare, rec, heapsort_depth);
}

fn do_introsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C, rec: u32, heapsort_depth: u32) {
    // This works around some bugs with unboxed closures
    macro_rules! maybe_swap(
        ($v: expr, $a: expr, $b: expr, $compare: expr) => {
            if compare_idxs($v, *$a, *$b, $compare) == Greater {
                swap($a, $b);
            }
        }
    )

    if rec > heapsort_depth {
        heapsort(v, compare);
        return;
    }

    let n = v.len();

    // Pivot selection algorithm based on Java's DualPivotQuicksort.

    // Fast approximation of n / 7
    let seventh = (n / 8) + (n / 64) + 1;

    // Pick five element evenly spaced around the middle (inclusive) of the slice.
    let mut e3 = n / 2;
    let mut e2 = e3 - seventh;
    let mut e1 = e3 - 2*seventh;
    let mut e4 = e3 + seventh;
    let mut e5 = e3 + 2*seventh;

    // Sort them with a sorting network.
    unsafe {
        maybe_swap!(v, &mut e1, &mut e2, compare);
        maybe_swap!(v, &mut e4, &mut e5, compare);
        maybe_swap!(v, &mut e3, &mut e5, compare);
        maybe_swap!(v, &mut e3, &mut e4, compare);
        maybe_swap!(v, &mut e2, &mut e5, compare);
        maybe_swap!(v, &mut e1, &mut e4, compare);
        maybe_swap!(v, &mut e1, &mut e3, compare);
        maybe_swap!(v, &mut e2, &mut e4, compare);
        maybe_swap!(v, &mut e2, &mut e3, compare);
    }

    if unsafe { compare_idxs(v, e1, e2, compare) != Equal &&
                compare_idxs(v, e2, e3, compare) != Equal &&
                compare_idxs(v, e3, e4, compare) != Equal &&
                compare_idxs(v, e4, e5, compare) != Equal } {
        // No consecutive pivot candidates are the same, meaning there is some variaton.
        dual_pivot_sort(v, (e1, e2, e3, e4, e5), compare, rec, heapsort_depth);
    } else {
        // Two consecutive pivots candidates where the same.
        // There are probably many similar elements.
        single_pivot_sort(v, e3, compare, rec, heapsort_depth);
    }
}

fn maybe_insertion_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) -> bool {
    let n = v.len();
    if n <= 1 {
        return true;
    }

    let threshold = min(MAX_INSERTION_SORT_ELEMS,
                        max(MIN_QUICKSORT_ELEMS, INSERTION_SORT_FACTOR / size_of::<T>()));
    if n <= threshold {
        insertion_sort(v, compare);
        return true;
    }
    return false;
}

pub fn insertion_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    let mut i = 1;
    let n = v.len();
    while i < n {
        let mut j = i;
        while j > 0 && unsafe { compare_idxs(v, j-1, j, compare) } == Greater {
            unsafe { unsafe_swap(v, j, j-1); }
            j -= 1;
        }
        i += 1;
    }
}

fn dual_pivot_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivots: (uint, uint, uint, uint, uint),
                                                               compare: &C, rec: u32, heapsort_depth: u32) {
    let (pmin, p1, _pmid, p2, pmax) = pivots;
    let n = v.len();

    let lp = 0;
    let rp = n - 1;

    v.swap(p1, lp);
    v.swap(p2, rp);

    let mut lesser = 1;
    let mut greater = n - 2;

    unsafe {
        // Skip elements that are already in the correct position
        while compare_idxs(v, lesser, lp, compare) == Less { lesser += 1; }
        while compare_idxs(v, greater, rp, compare) == Greater { greater -= 1; }

        let mut k = lesser;
        // XXX We make some unecessary swaps since we can't leave uninitialized values
        // in `v` in case `compare` unwinds.
        while k <= greater {
            if compare_idxs(v, k, lp, compare) == Less {
                unsafe_swap(v, k, lesser);
                lesser += 1;
            } else {
                let cmp = compare_idxs(v, k, rp, compare);
                if cmp == Greater || cmp == Equal {
                    while k < greater && compare_idxs(v, greater, rp, compare) == Greater {
                        greater -= 1;
                    }
                    unsafe_swap(v, k, greater);
                    greater -= 1;
                    if compare_idxs(v, k, lp, compare) == Less {
                        unsafe_swap(v, k, lesser);
                        lesser += 1;
                    }
                }
            }
            k += 1;
        }
    }

    lesser -= 1;
    greater += 1;

    // Swap back pivots
    unsafe {
        unsafe_swap(v, lp, lesser);
        unsafe_swap(v, rp, greater);
    }

    // Sort left and right partition
    introsort(v[mut ..lesser], compare, rec + 1, heapsort_depth);
    introsort(v[mut greater+1..], compare, rec + 1, heapsort_depth);

    // Sort center partition
    let left = lesser+1;
    let right = greater;
    if lesser >= pmin || greater <= pmax {
        // Center partition is small
        let mid = (right - left) / 2;
        if maybe_insertion_sort(v[mut left..right], compare) { return; }
        single_pivot_sort(v[mut left..right], mid, compare, rec + 1, heapsort_depth);
    } else {
        // Center partition is big
        introsort(v[mut left..right], compare, rec + 1, heapsort_depth);
    }
}

fn single_pivot_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivot: uint, compare: &C, rec: u32, heapsort_depth: u32) {
    let (l, r) = fat_partition(v, pivot, compare);
    let n = v.len();
    if l > 1 {
        introsort(v[mut ..l], compare, rec + 1, heapsort_depth);
    }
    if r > 1 {
        introsort(v[mut n - r..], compare, rec + 1, heapsort_depth);
    }
}

/// Partitions elements, using the element at `pivot` as pivot.
/// After partitioning, the array looks as following:
/// <<<<<==>>>
/// Return (number of < elements, number of > elements)
fn fat_partition<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivot: uint, compare: &C) -> (uint, uint)  {
    let mut a = 0;
    let mut b = a;
    let mut c = v.len() - 1;
    let mut d = c;
    unsafe { unsafe_swap(v, 0, pivot); }
    loop {
        while b <= c {
            let r = unsafe { compare_idxs(v, b, 0, compare) };
            if r == Greater { break; }
            if r == Equal {
                unsafe { unsafe_swap(v, a, b); }
                a += 1;
            }
            b += 1;
        }
        while c >= b {
            let r = unsafe { compare_idxs(v, c, 0, compare) };
            if r == Less { break; }
            if r == Equal {
                unsafe { unsafe_swap(v, c, d); }
                d -= 1;
            }
            c -= 1;
        }
        if b > c { break; }
        unsafe { unsafe_swap(v, b, c); }
        b += 1;
        c -= 1;
    }

    let n = v.len();
    let l = min(a, b - a);
    unsafe { swap_many(v, 0, b - l, l); }
    let r = min(d - c, n - 1 - d);
    unsafe { swap_many(v, b, n - r, r); }

    return (b - a, d - c);
}

unsafe fn swap_many<T>(v: &mut [T], a: uint, b: uint, n: uint) {
    let mut i = 0;
    while i < n {
        unsafe_swap(v, a + i, b + i);
        i += 1;
    }
}

#[cold]
#[inline(never)]
pub fn heapsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    heapify(v, compare);
    let mut end = v.len();
    while end > 0 {
        end -= 1;
        v.swap(0, end);
        siftdown_range(v, 0, end, compare);
    }
}

#[inline]
fn heapify<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    let mut n = v.len() / 2;
    while n > 0 {
        n -= 1;
        siftdown(v, n, compare)
    }
}

#[inline]
fn siftup<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], start: uint, mut pos: uint, compare: &C) {
    unsafe {
        let new = replace(&mut v[pos], zeroed());

        while pos > start {
            let parent = (pos - 1) >> 1;
            // TODO: Get rid of transmute when high-rank lifetimes work
            if (*compare)(transmute(&new), transmute(v.unsafe_get(parent))) == Greater {
                let x = replace(&mut v[parent], zeroed());
                ptr::write(&mut v[pos], x);
                pos = parent;
                continue
            }
            break
        }
        ptr::write(&mut v[pos], new);
    }
}

#[inline]
fn siftdown_range<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], mut pos: uint, end: uint, compare: &C) {
    unsafe {
        let start = pos;
        let new = replace(&mut v[pos], zeroed());

        let mut child = 2 * pos + 1;
        while child < end {
            let right = child + 1;
            if right < end && compare_idxs(v, child, right, compare) != Greater {
                child = right;
            }
            let x = replace(&mut v[child], zeroed());
            ptr::write(&mut v[pos], x);
            pos = child;
            child = 2 * pos + 1;
        }

        ptr::write(&mut v[pos], new);
        siftup(v, start, pos, compare);
    }
}

#[inline]
fn siftdown<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pos: uint, compare: &C) {
    let len = v.len();
    siftdown_range(v, pos, len, compare);
}

fn log2(x: uint) -> u32 {
    if x <= 1 { return 0; }
    let n = if size_of::<uint>() == 8 {
        (unsafe { ::core::intrinsics::ctlz64(x as u64) }) as u32
    } else {
        unsafe { ::core::intrinsics::ctlz32(x as u32) }
    };
    size_of::<uint>() as u32 * 8 - n
}

// TODO Replace this function when unboxed closures work properly
// Blocked on https://github.com/rust-lang/rust/issues/17661
#[inline(always)]
unsafe fn compare_idxs<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &[T], a: uint, b: uint, compare: &C) -> Ordering {
    let x = v.unsafe_get(a);
    let y = v.unsafe_get(b);
    (*compare)(transmute(x), transmute(y))
}

#[inline(always)]
fn compare_idxs_safe<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &[T], a: uint, b: uint, compare: &C) -> Ordering {
    unsafe { (*compare)(transmute(&v[a]), transmute(&v[b])) }
}

#[inline(always)]
unsafe fn unsafe_swap<T>(v: &mut[T], a: uint, b: uint) {
    ptr::swap(v.unsafe_mut(a) as *mut T, v.unsafe_mut(b) as *mut T);
}
<commit_msg>Use safe comparisons in single-pivot partitioning. Fixes #1<commit_after>use core::prelude::*;
use core::cmp::{min, max};
use core::mem::{size_of, zeroed, replace, swap, transmute};
use core::ptr;

/// The smallest number of elements that may be quicksorted.
/// Must be at least 9.
const MIN_QUICKSORT_ELEMS: uint = 10;

/// The maximum number of elements to be insertion sorted.
const MAX_INSERTION_SORT_ELEMS: uint = 42;

/// Controls the number of elements to be insertion sorted.
/// Higher values give more insertion sorted elements.
const INSERTION_SORT_FACTOR: uint = 450;

pub fn sort_by<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    if maybe_insertion_sort(v, compare) { return; }
    let heapsort_depth = (3 * log2(v.len())) / 2;
    do_introsort(v, compare, 0, heapsort_depth);
}

#[inline]
pub fn sort<T: Ord>(v: &mut [T]) {
    sort_by(v, &|&: a: &T, b| a.cmp(b));
}

fn introsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C, rec: u32, heapsort_depth: u32) {
    if maybe_insertion_sort(v, compare) { return; }
    do_introsort(v, compare, rec, heapsort_depth);
}

fn do_introsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C, rec: u32, heapsort_depth: u32) {
    // This works around some bugs with unboxed closures
    macro_rules! maybe_swap(
        ($v: expr, $a: expr, $b: expr, $compare: expr) => {
            if compare_idxs($v, *$a, *$b, $compare) == Greater {
                swap($a, $b);
            }
        }
    )

    if rec > heapsort_depth {
        heapsort(v, compare);
        return;
    }

    let n = v.len();

    // Pivot selection algorithm based on Java's DualPivotQuicksort.

    // Fast approximation of n / 7
    let seventh = (n / 8) + (n / 64) + 1;

    // Pick five element evenly spaced around the middle (inclusive) of the slice.
    let mut e3 = n / 2;
    let mut e2 = e3 - seventh;
    let mut e1 = e3 - 2*seventh;
    let mut e4 = e3 + seventh;
    let mut e5 = e3 + 2*seventh;

    // Sort them with a sorting network.
    unsafe {
        maybe_swap!(v, &mut e1, &mut e2, compare);
        maybe_swap!(v, &mut e4, &mut e5, compare);
        maybe_swap!(v, &mut e3, &mut e5, compare);
        maybe_swap!(v, &mut e3, &mut e4, compare);
        maybe_swap!(v, &mut e2, &mut e5, compare);
        maybe_swap!(v, &mut e1, &mut e4, compare);
        maybe_swap!(v, &mut e1, &mut e3, compare);
        maybe_swap!(v, &mut e2, &mut e4, compare);
        maybe_swap!(v, &mut e2, &mut e3, compare);
    }

    if unsafe { compare_idxs(v, e1, e2, compare) != Equal &&
                compare_idxs(v, e2, e3, compare) != Equal &&
                compare_idxs(v, e3, e4, compare) != Equal &&
                compare_idxs(v, e4, e5, compare) != Equal } {
        // No consecutive pivot candidates are the same, meaning there is some variaton.
        dual_pivot_sort(v, (e1, e2, e3, e4, e5), compare, rec, heapsort_depth);
    } else {
        // Two consecutive pivots candidates where the same.
        // There are probably many similar elements.
        single_pivot_sort(v, e3, compare, rec, heapsort_depth);
    }
}

fn maybe_insertion_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) -> bool {
    let n = v.len();
    if n <= 1 {
        return true;
    }

    let threshold = min(MAX_INSERTION_SORT_ELEMS,
                        max(MIN_QUICKSORT_ELEMS, INSERTION_SORT_FACTOR / size_of::<T>()));
    if n <= threshold {
        insertion_sort(v, compare);
        return true;
    }
    return false;
}

pub fn insertion_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    let mut i = 1;
    let n = v.len();
    while i < n {
        let mut j = i;
        while j > 0 && unsafe { compare_idxs(v, j-1, j, compare) } == Greater {
            unsafe { unsafe_swap(v, j, j-1); }
            j -= 1;
        }
        i += 1;
    }
}

fn dual_pivot_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivots: (uint, uint, uint, uint, uint),
                                                               compare: &C, rec: u32, heapsort_depth: u32) {
    let (pmin, p1, _pmid, p2, pmax) = pivots;
    let n = v.len();

    let lp = 0;
    let rp = n - 1;

    v.swap(p1, lp);
    v.swap(p2, rp);

    let mut lesser = 1;
    let mut greater = n - 2;

    unsafe {
        // Skip elements that are already in the correct position
        while compare_idxs(v, lesser, lp, compare) == Less { lesser += 1; }
        while compare_idxs(v, greater, rp, compare) == Greater { greater -= 1; }

        let mut k = lesser;
        // XXX We make some unecessary swaps since we can't leave uninitialized values
        // in `v` in case `compare` unwinds.
        while k <= greater {
            if compare_idxs(v, k, lp, compare) == Less {
                unsafe_swap(v, k, lesser);
                lesser += 1;
            } else {
                let cmp = compare_idxs(v, k, rp, compare);
                if cmp == Greater || cmp == Equal {
                    while k < greater && compare_idxs(v, greater, rp, compare) == Greater {
                        greater -= 1;
                    }
                    unsafe_swap(v, k, greater);
                    greater -= 1;
                    if compare_idxs(v, k, lp, compare) == Less {
                        unsafe_swap(v, k, lesser);
                        lesser += 1;
                    }
                }
            }
            k += 1;
        }
    }

    lesser -= 1;
    greater += 1;

    // Swap back pivots
    unsafe {
        unsafe_swap(v, lp, lesser);
        unsafe_swap(v, rp, greater);
    }

    // Sort left and right partition
    introsort(v[mut ..lesser], compare, rec + 1, heapsort_depth);
    introsort(v[mut greater+1..], compare, rec + 1, heapsort_depth);

    // Sort center partition
    let left = lesser+1;
    let right = greater;
    if lesser >= pmin || greater <= pmax {
        // Center partition is small
        let mid = (right - left) / 2;
        if maybe_insertion_sort(v[mut left..right], compare) { return; }
        single_pivot_sort(v[mut left..right], mid, compare, rec + 1, heapsort_depth);
    } else {
        // Center partition is big
        introsort(v[mut left..right], compare, rec + 1, heapsort_depth);
    }
}

fn single_pivot_sort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivot: uint, compare: &C, rec: u32, heapsort_depth: u32) {
    let (l, r) = fat_partition(v, pivot, compare);
    let n = v.len();
    if l > 1 {
        introsort(v[mut ..l], compare, rec + 1, heapsort_depth);
    }
    if r > 1 {
        introsort(v[mut n - r..], compare, rec + 1, heapsort_depth);
    }
}

/// Partitions elements, using the element at `pivot` as pivot.
/// After partitioning, the array looks as following:
/// <<<<<==>>>
/// Return (number of < elements, number of > elements)
fn fat_partition<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pivot: uint, compare: &C) -> (uint, uint)  {
    let mut a = 0;
    let mut b = a;
    let mut c = v.len() - 1;
    let mut d = c;
    v.swap(0, pivot);
    loop {
        while b <= c {
            let r = compare_idxs_safe(v, b, 0, compare);
            if r == Greater { break; }
            if r == Equal {
                unsafe { unsafe_swap(v, a, b); }
                a += 1;
            }
            b += 1;
        }
        while c >= b {
            let r = compare_idxs_safe(v, c, 0, compare);
            if r == Less { break; }
            if r == Equal {
                unsafe { unsafe_swap(v, c, d); }
                d -= 1;
            }
            c -= 1;
        }
        if b > c { break; }
        unsafe { unsafe_swap(v, b, c); }
        b += 1;
        c -= 1;
    }

    let n = v.len();
    let l = min(a, b - a);
    unsafe { swap_many(v, 0, b - l, l); }
    let r = min(d - c, n - 1 - d);
    unsafe { swap_many(v, b, n - r, r); }

    return (b - a, d - c);
}

unsafe fn swap_many<T>(v: &mut [T], a: uint, b: uint, n: uint) {
    let mut i = 0;
    while i < n {
        unsafe_swap(v, a + i, b + i);
        i += 1;
    }
}

#[cold]
#[inline(never)]
pub fn heapsort<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    heapify(v, compare);
    let mut end = v.len();
    while end > 0 {
        end -= 1;
        v.swap(0, end);
        siftdown_range(v, 0, end, compare);
    }
}

#[inline]
fn heapify<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], compare: &C) {
    let mut n = v.len() / 2;
    while n > 0 {
        n -= 1;
        siftdown(v, n, compare)
    }
}

#[inline]
fn siftup<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], start: uint, mut pos: uint, compare: &C) {
    unsafe {
        let new = replace(&mut v[pos], zeroed());

        while pos > start {
            let parent = (pos - 1) >> 1;
            // TODO: Get rid of transmute when high-rank lifetimes work
            if (*compare)(transmute(&new), transmute(v.unsafe_get(parent))) == Greater {
                let x = replace(&mut v[parent], zeroed());
                ptr::write(&mut v[pos], x);
                pos = parent;
                continue
            }
            break
        }
        ptr::write(&mut v[pos], new);
    }
}

#[inline]
fn siftdown_range<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], mut pos: uint, end: uint, compare: &C) {
    unsafe {
        let start = pos;
        let new = replace(&mut v[pos], zeroed());

        let mut child = 2 * pos + 1;
        while child < end {
            let right = child + 1;
            if right < end && compare_idxs(v, child, right, compare) != Greater {
                child = right;
            }
            let x = replace(&mut v[child], zeroed());
            ptr::write(&mut v[pos], x);
            pos = child;
            child = 2 * pos + 1;
        }

        ptr::write(&mut v[pos], new);
        siftup(v, start, pos, compare);
    }
}

#[inline]
fn siftdown<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &mut [T], pos: uint, compare: &C) {
    let len = v.len();
    siftdown_range(v, pos, len, compare);
}

fn log2(x: uint) -> u32 {
    if x <= 1 { return 0; }
    let n = if size_of::<uint>() == 8 {
        (unsafe { ::core::intrinsics::ctlz64(x as u64) }) as u32
    } else {
        unsafe { ::core::intrinsics::ctlz32(x as u32) }
    };
    size_of::<uint>() as u32 * 8 - n
}

// TODO Replace this function when unboxed closures work properly
// Blocked on https://github.com/rust-lang/rust/issues/17661
#[inline(always)]
unsafe fn compare_idxs<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &[T], a: uint, b: uint, compare: &C) -> Ordering {
    let x = v.unsafe_get(a);
    let y = v.unsafe_get(b);
    (*compare)(transmute(x), transmute(y))
}

#[inline(always)]
fn compare_idxs_safe<'a, T: 'a, C: Fn<(&'a T, &'a T), Ordering>>(v: &[T], a: uint, b: uint, compare: &C) -> Ordering {
    unsafe { (*compare)(transmute(&v[a]), transmute(&v[b])) }
}

#[inline(always)]
unsafe fn unsafe_swap<T>(v: &mut[T], a: uint, b: uint) {
    ptr::swap(v.unsafe_mut(a) as *mut T, v.unsafe_mut(b) as *mut T);
}
<|endoftext|>


<commit_before>// mrusty. mruby safe bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use super::mruby::*;

use std::any::Any;

/// A `macro` useful to run mruby specs.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate mrusty;
/// use mrusty::*;
///
/// # fn main() {
/// struct Cont;
///
/// impl MRubyFile for Cont {
///     fn require(mruby: MRubyType) {
///         mruby.def_class::<Cont>("Container");
///     }
/// }
///
/// describe!(Cont, "
///   context 'when 1' do
///     subject { 1 }
///
///     it { is_expected.to eql 1 }
///   end
///
///   context 'when 1' do
///     subject { 1 }
///     let(:one) { 1 }
///
///     it 'won\\'t' do
///       expect(1).to eql one
///     end
///   end
/// ");
/// # }
/// ```
#[macro_export]
macro_rules! describe {
    ( $t:ty, $spec:expr ) => {
        #[test]
        fn spec() {
            let spec = Spec::new::<$t>($spec);

            assert!(spec.run());
        }
    }
}

/// A `struct` useful for mruby spec definition and running.
///
/// Available matchers:
///
/// * `be_a`, `be_an` - type testing
/// * `be_<somehow>` - test boolean-returning `<name>?` methods
/// * `be <`, `be <=`, `be >`, `be >=` - test relation
/// * `be_eq`, `be_eql`, `be_equal` - test equality
/// * `be_falsey` - test falsey things
/// * `be_truthy` - test truthy things
/// * `have_<something>` - test boolean-returning `has_<name>?` methods
/// * `raise_error` - test errors
/// * `respond_to` - test method responding
/// * `be_within(value).of` - test value
///
/// # Examples
///
/// ```
/// # use mrusty::*;
/// struct Cont;
///
/// impl MRubyFile for Cont {
///     fn require(mruby: MRubyType) {
///         mruby.def_class::<Cont>("Container");
///     }
/// }
///
/// let spec = Spec::new::<Cont>("
///     context 'when 1' do
///       subject { 1 }
///
///       it { is_expected.to eql 1 }
///     end
///
///     context 'when 1' do
///       subject { 1 }
///       let(:one) { 1 }
///
///       it 'won\\'t' do
///         expect(1).to eql one
///       end
///     end
/// ");
///
/// assert_eq!(spec.run(), true);
/// ```
pub struct Spec {
    script: String,
    target: String,
    mruby: MRubyType
}

impl Spec {
    /// Creates an mruby spec runner.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::*;
    /// struct Cont;
    ///
    /// impl MRubyFile for Cont {
    ///     fn require(mruby: MRubyType) {
    ///         mruby.def_class::<Cont>("Container");
    ///     }
    /// }
    ///
    /// Spec::new::<Cont>("
    ///     context 'when 1' do
    ///       subject { 1 }
    ///
    ///       it { is_expected.to eql 1 }
    ///     end
    ///
    ///     context 'when 1' do
    ///       subject { 1 }
    ///       let(:one) { 1 }
    ///
    ///       it 'won\\'t' do
    ///         expect(1).to eql one
    ///       end
    ///     end
    /// ");
    /// ```
    pub fn new<T: MRubyFile + Any>(script: &str) -> Spec {
        let mruby = MRuby::new();

        T::require(mruby.clone());

        mruby.filename("matchers/be.rb");
        mruby.run(include_str!("spec/matchers/be.rb")).unwrap();

        mruby.filename("matchers/be_a.rb");
        mruby.run(include_str!("spec/matchers/be_a.rb")).unwrap();

        mruby.filename("matchers/compare.rb");
        mruby.run(include_str!("spec/matchers/compare.rb")).unwrap();

        mruby.filename("matchers/eq.rb");
        mruby.run(include_str!("spec/matchers/eq.rb")).unwrap();

        mruby.filename("matchers/falsey.rb");
        mruby.run(include_str!("spec/matchers/falsey.rb")).unwrap();

        mruby.filename("matchers/have.rb");
        mruby.run(include_str!("spec/matchers/have.rb")).unwrap();

        mruby.filename("matchers/raise.rb");
        mruby.run(include_str!("spec/matchers/raise.rb")).unwrap();

        mruby.filename("matchers/respond.rb");
        mruby.run(include_str!("spec/matchers/respond.rb")).unwrap();

        mruby.filename("matchers/truthy.rb");
        mruby.run(include_str!("spec/matchers/truthy.rb")).unwrap();

        mruby.filename("matchers/within.rb");
        mruby.run(include_str!("spec/matchers/within.rb")).unwrap();

        mruby.filename("context.rb");
        mruby.run(include_str!("spec/context.rb")).unwrap();

        mruby.filename("example.rb");
        mruby.run(include_str!("spec/example.rb")).unwrap();

        mruby.filename("expect.rb");
        mruby.run(include_str!("spec/expect.rb")).unwrap();

        mruby.filename("spec.rb");
        mruby.run(include_str!("spec/spec.rb")).unwrap();

        let name = mruby.class_name::<T>().unwrap();

        Spec {
            script: script.to_string(),
            target: name,
            mruby: mruby
        }
    }

    /// Runs mruby specs.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::*;
    /// struct Cont;
    ///
    /// impl MRubyFile for Cont {
    ///     fn require(mruby: MRubyType) {
    ///         mruby.def_class::<Cont>("Container");
    ///     }
    /// }
    ///
    /// let spec = Spec::new::<Cont>("
    ///     context 'when 1' do
    ///       subject { 1 }
    ///
    ///       it { is_expected.to eql 1 }
    ///     end
    ///
    ///     context 'when 1' do
    ///       subject { 1 }
    ///       let(:one) { 1 }
    ///
    ///       it 'won\\'t' do
    ///         expect(1).to eql one
    ///       end
    ///     end
    /// ");
    ///
    /// assert_eq!(spec.run(), true);
    /// ```
    pub fn run(&self) -> bool {
        let describe = format!("
            Spec.describe {} do
              {}
            end
        ", self.target, self.script);

        self.mruby.run(&describe).unwrap().to_bool().unwrap()
    }
}

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

    struct Empty;

    impl MRubyFile for Empty {
        fn require(mruby: MRubyType) {
            mruby.def_class::<Empty>("Empty");
        }
    }

    describe!(Empty, "
      context Fixnum do
        context 'when 1' do
          subject { 1 }
          let(:two) { 2 }

          it 'does irrelevant things' do
            a = 2
          end

          it { is_expected.to eq 1 }
          it { is_expected.not_to eq two }

          it { is_expected.to be_an Integer }
          it { is_expected.not_to be_a String }

          it { is_expected.to be_within(0).of(1) }
          it { is_expected.not_to be_within(0.0001).of(two) }

          it { is_expected.to be < two }
          it { is_expected.to be <= two }
          it { is_expected.not_to be > two }
          it { is_expected.not_to be >= two }

          it 'is different from 2' do
            expect(1 == two).to be_falsey
            expect(1 == two).not_to be_truthy
            expect(1 != two).to be_truthy
            expect(1 != two).not_to be_falsey
          end

          it 'does not concatenate with String' do
            expect { '' + 1 }.to raise_error TypeError, \"expected String\"
            expect { 1 + '' }.not_to raise_error Exception
          end

          it { is_expected.to respond_to :to_s }
          it { is_expected.not_to respond_to :to_sym }
        end
      end

      context Hash do
        context 'empty' do
          it { is_expected.to be_empty }
        end

        context 'when {a: 1}' do
          subject { {a: 1} }

          it { is_expected.to have_key :a }
          it { is_expected.not_to be_empty }
        end
      end
    ");
}
<commit_msg>Added spec dependencies.<commit_after>// mrusty. mruby safe bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use super::mruby::*;

/// A `macro` useful to run mruby specs. You can pass a tuple of `MRubyFile`s dependencies
/// as a second argument.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate mrusty;
/// use mrusty::*;
///
/// # fn main() {
/// struct Cont;
///
/// impl MRubyFile for Cont {
///     fn require(mruby: MRubyType) {
///         mruby.def_class::<Cont>("Container");
///     }
/// }
///
/// describe!(Cont, (Cont, Cont), "
///   context 'when 1' do
///     subject { 1 }
///
///     it { is_expected.to eql 1 }
///   end
///
///   context 'when 1' do
///     subject { 1 }
///     let(:one) { 1 }
///
///     it 'won\\'t' do
///       expect(1).to eql one
///     end
///   end
/// ");
/// # }
/// ```
#[macro_export]
macro_rules! describe {
    ( $t:ident, $spec:expr ) => {
        #[test]
        fn spec() {
            let mruby = MRuby::new();

            $t::require(mruby.clone());

            let name = mruby.class_name::<$t>().unwrap();

            let spec = Spec::new(mruby, &name, $spec);

            assert!(spec.run());
        }
    };

    ( $t:ident, ( $( $ts:ident ),+ ), $spec:expr ) => {
        #[test]
        fn spec() {
            let mruby = MRuby::new();

            $t::require(mruby.clone());
            $( $ts::require(mruby.clone()); )*

            let name = mruby.class_name::<$t>().unwrap();

            let spec = Spec::new(mruby, &name, $spec);

            assert!(spec.run());
        }
    }
}

/// A `struct` useful for mruby spec definition and running.
///
/// Available matchers:
///
/// * `be_a`, `be_an` - type testing
/// * `be_<somehow>` - test boolean-returning `<name>?` methods
/// * `be <`, `be <=`, `be >`, `be >=` - test relation
/// * `be_eq`, `be_eql`, `be_equal` - test equality
/// * `be_falsey` - test falsey things
/// * `be_truthy` - test truthy things
/// * `have_<something>` - test boolean-returning `has_<name>?` methods
/// * `raise_error` - test errors
/// * `respond_to` - test method responding
/// * `be_within(value).of` - test value
///
/// # Examples
///
/// ```
/// # use mrusty::*;
/// struct Cont;
///
/// impl MRubyFile for Cont {
///     fn require(mruby: MRubyType) {
///         mruby.def_class::<Cont>("Container");
///     }
/// }
///
/// let mruby = MRuby::new();
/// Cont::require(mruby.clone());
///
/// let spec = Spec::new(mruby, "Container", "
///     context 'when 1' do
///       subject { 1 }
///
///       it { is_expected.to eql 1 }
///     end
///
///     context 'when 1' do
///       subject { 1 }
///       let(:one) { 1 }
///
///       it 'won\\'t' do
///         expect(1).to eql one
///       end
///     end
/// ");
///
/// assert_eq!(spec.run(), true);
/// ```
pub struct Spec {
    script: String,
    target: String,
    mruby: MRubyType
}

impl Spec {
    /// Creates an mruby spec runner.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::*;
    /// struct Cont;
    ///
    /// impl MRubyFile for Cont {
    ///     fn require(mruby: MRubyType) {
    ///         mruby.def_class::<Cont>("Container");
    ///     }
    /// }
    ///
    /// let mruby = MRuby::new();
    /// Cont::require(mruby.clone());
    ///
    /// let spec = Spec::new(mruby, "Container", "
    ///     context 'when 1' do
    ///       subject { 1 }
    ///
    ///       it { is_expected.to eql 1 }
    ///     end
    ///
    ///     context 'when 1' do
    ///       subject { 1 }
    ///       let(:one) { 1 }
    ///
    ///       it 'won\\'t' do
    ///         expect(1).to eql one
    ///       end
    ///     end
    /// ");
    /// ```
    pub fn new(mruby: MRubyType, name: &str, script: &str) -> Spec {
        mruby.filename("matchers/be.rb");
        mruby.run(include_str!("spec/matchers/be.rb")).unwrap();

        mruby.filename("matchers/be_a.rb");
        mruby.run(include_str!("spec/matchers/be_a.rb")).unwrap();

        mruby.filename("matchers/compare.rb");
        mruby.run(include_str!("spec/matchers/compare.rb")).unwrap();

        mruby.filename("matchers/eq.rb");
        mruby.run(include_str!("spec/matchers/eq.rb")).unwrap();

        mruby.filename("matchers/falsey.rb");
        mruby.run(include_str!("spec/matchers/falsey.rb")).unwrap();

        mruby.filename("matchers/have.rb");
        mruby.run(include_str!("spec/matchers/have.rb")).unwrap();

        mruby.filename("matchers/raise.rb");
        mruby.run(include_str!("spec/matchers/raise.rb")).unwrap();

        mruby.filename("matchers/respond.rb");
        mruby.run(include_str!("spec/matchers/respond.rb")).unwrap();

        mruby.filename("matchers/truthy.rb");
        mruby.run(include_str!("spec/matchers/truthy.rb")).unwrap();

        mruby.filename("matchers/within.rb");
        mruby.run(include_str!("spec/matchers/within.rb")).unwrap();

        mruby.filename("context.rb");
        mruby.run(include_str!("spec/context.rb")).unwrap();

        mruby.filename("example.rb");
        mruby.run(include_str!("spec/example.rb")).unwrap();

        mruby.filename("expect.rb");
        mruby.run(include_str!("spec/expect.rb")).unwrap();

        mruby.filename("spec.rb");
        mruby.run(include_str!("spec/spec.rb")).unwrap();

        Spec {
            script: script.to_string(),
            target: name.to_string(),
            mruby: mruby
        }
    }

    /// Runs mruby specs.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::*;
    /// struct Cont;
    ///
    /// impl MRubyFile for Cont {
    ///     fn require(mruby: MRubyType) {
    ///         mruby.def_class::<Cont>("Container");
    ///     }
    /// }
    ///
    /// let mruby = MRuby::new();
    /// Cont::require(mruby.clone());
    ///
    /// let spec = Spec::new(mruby, "Container", "
    ///     context 'when 1' do
    ///       subject { 1 }
    ///
    ///       it { is_expected.to eql 1 }
    ///     end
    ///
    ///     context 'when 1' do
    ///       subject { 1 }
    ///       let(:one) { 1 }
    ///
    ///       it 'won\\'t' do
    ///         expect(1).to eql one
    ///       end
    ///     end
    /// ");
    ///
    /// assert_eq!(spec.run(), true);
    /// ```
    pub fn run(&self) -> bool {
        let describe = format!("
            Spec.describe {} do
              {}
            end
        ", self.target, self.script);

        self.mruby.run(&describe).unwrap().to_bool().unwrap()
    }
}

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

    struct Empty;

    impl MRubyFile for Empty {
        fn require(mruby: MRubyType) {
            mruby.def_class::<Empty>("Empty");
        }
    }

    describe!(Empty, "
      context Fixnum do
        context 'when 1' do
          subject { 1 }
          let(:two) { 2 }

          it 'does irrelevant things' do
            a = 2
          end

          it { is_expected.to eq 1 }
          it { is_expected.not_to eq two }

          it { is_expected.to be_an Integer }
          it { is_expected.not_to be_a String }

          it { is_expected.to be_within(0).of(1) }
          it { is_expected.not_to be_within(0.0001).of(two) }

          it { is_expected.to be < two }
          it { is_expected.to be <= two }
          it { is_expected.not_to be > two }
          it { is_expected.not_to be >= two }

          it 'is different from 2' do
            expect(1 == two).to be_falsey
            expect(1 == two).not_to be_truthy
            expect(1 != two).to be_truthy
            expect(1 != two).not_to be_falsey
          end

          it 'does not concatenate with String' do
            expect { '' + 1 }.to raise_error TypeError, \"expected String\"
            expect { 1 + '' }.not_to raise_error Exception
          end

          it { is_expected.to respond_to :to_s }
          it { is_expected.not_to respond_to :to_sym }
        end
      end

      context Hash do
        context 'empty' do
          it { is_expected.to be_empty }
        end

        context 'when {a: 1}' do
          subject { {a: 1} }

          it { is_expected.to have_key :a }
          it { is_expected.not_to be_empty }
        end
      end
    ");
}
<|endoftext|>


<commit_before>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::random_name;

/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return a boolean if the ~/.muxed/ dir exists.
pub fn muxed_dir_exists(name: &String) -> bool {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    path.exists()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

<commit_msg>Spacing.<commit_after>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::random_name;

/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return a boolean if the ~/.muxed/ dir exists.
pub fn muxed_dir_exists(name: &String) -> bool {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    path.exists()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    match fs::rmdir_recursive(muxed_path) {
        Ok(()) => (), // succeeded
        Err(e) => println!("Failed to remove the path {} with error {}", muxed_path.display(), e),
    }
}
<|endoftext|>


<commit_before>use yaml_rust::{Yaml, YamlLoader};
use yaml_rust::yaml::Hash;

extern crate git2;
use self::git2::{Repository, ObjectType};
use self::git2::{Commit, Reference, Oid};

#[derive(Debug)]
pub struct Task {
    pub name: String,
    commit: Option<Oid>,
    changes: Hash
}

impl Task {
    pub fn from_ref(reference: &Reference) -> Task {
        let commit_obj = reference.peel(ObjectType::Commit).unwrap();
        let commit = commit_obj.as_commit().unwrap();
        let name = reference.shorthand().unwrap_or("master");
        Task::from_commit(&name, &commit)
    }

    pub fn from_commit(name: &str, commit: &Commit) -> Task {
        let mut messages = commit.message().unwrap().split("---\n");
        let _message = messages.next().unwrap();
        let mut tasks = vec![];
        if let Some(yaml) = messages.next() {
            if let Ok(loader) = YamlLoader::load_from_str(yaml) {
                for yaml in loader.iter() {
                    if let Some(hash) = yaml.as_hash() {
                        for (key, values) in hash {
                            if let Some(key) = key.as_str() {
                                if let Some(values) = values.as_hash() {
                                    tasks.push(Task {
                                        name: String::from(key),
                                        changes: values.clone(),
                                        commit: Some(commit.id())
                                    });
                                }
                            }
                        }
                    }
                }
            }
        };
        tasks.retain(|c| c.name == name);
        tasks.pop().unwrap_or(Task {
            name: String::from(name),
            commit: Some(commit.id()),
            changes: Hash::new()
        })
    }

    pub fn get(&self, repo: &Repository, property: &Yaml) -> Option<Yaml> {
        if let Some(value) = self.changes.get(&property) {
            Some(value.clone())
        } else if let Some(commit) = self.commit {
            let commit = repo.find_commit(commit).expect("Unable to find commit!");
            let parents = commit.parents().map(|parent| Task::from_commit(&self.name, &parent));
            let mut candidates: Vec<Yaml> = parents.filter_map(|task| task.get(&repo, property)).collect();
            candidates.pop()
        } else {
            None
        }
    }

    pub fn parent(&self, repo: &Repository) -> Option<Task> {
        if let Some(commit_oid) = self.commit {
            let commit = repo.find_commit(commit_oid).unwrap();
            if let Some(parent) = commit.parents().next() {
                Some(Task::from_commit(&self.name, &parent))
            } else { None }
        } else { None }
    }

    pub fn changes(&self, repo: &Repository) -> Vec<(String, Option<String>, String)> {
        let mut changes = vec![];
        for (key, value) in self.changes.clone() {
            let after = match value {
                Yaml::String(ref s) => s.clone(),
                Yaml::Integer(i) => format!("{}", i),
                Yaml::Real(ref i) => format!("{}", i),
                Yaml::Boolean(b) => format!("{}", b),
                Yaml::Null => format!(""),
                _ => String::from("[unknown]")
            };
            let before = if let Some(parent) = self.parent(&repo) {
                match parent.get(&repo, &key) {
                    Some(before_value) => {
                        if before_value == value {
                            None
                        } else {
                            match before_value {
                                Yaml::String(ref s) => Some(s.clone()),
                                Yaml::Integer(i) => Some(format!("{}", i)),
                                Yaml::Real(i) => Some(format!("{}", i)),
                                Yaml::Boolean(b) => Some(format!("{}", b)),
                                Yaml::Null => Some(format!("")),
                                _ => None
                            }
                        }
                    }, _ => None
                }
            } else { None };
            changes.push((String::from(key.as_str().unwrap()), before, after));
        };
        changes
    }

    pub fn properties(&self, repo: &Repository) -> Vec<(String, Option<String>, String)> {
        let mut changes = vec![];
        let properties = ["Ordinal","Status","Project","Estimate","Developer","Description"];
        for property in properties.iter() {
            let prop = Yaml::String(String::from(*property));
            if let Some(value) = self.get(&repo, &prop) {
                let after = match value {
                    Yaml::String(ref s) => s.clone(),
                    Yaml::Integer(i) => format!("{}", i),
                    Yaml::Real(i) => format!("{}", i),
                    Yaml::Boolean(b) => format!("{}", b),
                    Yaml::Null => format!(""),
                    _ => String::from("[unknown]")
                };
                changes.push((String::from(*property), None, after));
            }
        };
        changes
    }
}
<commit_msg>Track manager as well<commit_after>use yaml_rust::{Yaml, YamlLoader};
use yaml_rust::yaml::Hash;

extern crate git2;
use self::git2::{Repository, ObjectType};
use self::git2::{Commit, Reference, Oid};

#[derive(Debug)]
pub struct Task {
    pub name: String,
    commit: Option<Oid>,
    changes: Hash
}

impl Task {
    pub fn from_ref(reference: &Reference) -> Task {
        let commit_obj = reference.peel(ObjectType::Commit).unwrap();
        let commit = commit_obj.as_commit().unwrap();
        let name = reference.shorthand().unwrap_or("master");
        Task::from_commit(&name, &commit)
    }

    pub fn from_commit(name: &str, commit: &Commit) -> Task {
        let mut messages = commit.message().unwrap().split("---\n");
        let _message = messages.next().unwrap();
        let mut tasks = vec![];
        if let Some(yaml) = messages.next() {
            if let Ok(loader) = YamlLoader::load_from_str(yaml) {
                for yaml in loader.iter() {
                    if let Some(hash) = yaml.as_hash() {
                        for (key, values) in hash {
                            if let Some(key) = key.as_str() {
                                if let Some(values) = values.as_hash() {
                                    tasks.push(Task {
                                        name: String::from(key),
                                        changes: values.clone(),
                                        commit: Some(commit.id())
                                    });
                                }
                            }
                        }
                    }
                }
            }
        };
        tasks.retain(|c| c.name == name);
        tasks.pop().unwrap_or(Task {
            name: String::from(name),
            commit: Some(commit.id()),
            changes: Hash::new()
        })
    }

    pub fn get(&self, repo: &Repository, property: &Yaml) -> Option<Yaml> {
        if let Some(value) = self.changes.get(&property) {
            Some(value.clone())
        } else if let Some(commit) = self.commit {
            let commit = repo.find_commit(commit).expect("Unable to find commit!");
            let parents = commit.parents().map(|parent| Task::from_commit(&self.name, &parent));
            let mut candidates: Vec<Yaml> = parents.filter_map(|task| task.get(&repo, property)).collect();
            candidates.pop()
        } else {
            None
        }
    }

    pub fn parent(&self, repo: &Repository) -> Option<Task> {
        if let Some(commit_oid) = self.commit {
            let commit = repo.find_commit(commit_oid).unwrap();
            if let Some(parent) = commit.parents().next() {
                Some(Task::from_commit(&self.name, &parent))
            } else { None }
        } else { None }
    }

    pub fn changes(&self, repo: &Repository) -> Vec<(String, Option<String>, String)> {
        let mut changes = vec![];
        for (key, value) in self.changes.clone() {
            let after = match value {
                Yaml::String(ref s) => s.clone(),
                Yaml::Integer(i) => format!("{}", i),
                Yaml::Real(ref i) => format!("{}", i),
                Yaml::Boolean(b) => format!("{}", b),
                Yaml::Null => format!(""),
                _ => String::from("[unknown]")
            };
            let before = if let Some(parent) = self.parent(&repo) {
                match parent.get(&repo, &key) {
                    Some(before_value) => {
                        if before_value == value {
                            None
                        } else {
                            match before_value {
                                Yaml::String(ref s) => Some(s.clone()),
                                Yaml::Integer(i) => Some(format!("{}", i)),
                                Yaml::Real(i) => Some(format!("{}", i)),
                                Yaml::Boolean(b) => Some(format!("{}", b)),
                                Yaml::Null => Some(format!("")),
                                _ => None
                            }
                        }
                    }, _ => None
                }
            } else { None };
            changes.push((String::from(key.as_str().unwrap()), before, after));
        };
        changes
    }

    pub fn properties(&self, repo: &Repository) -> Vec<(String, Option<String>, String)> {
        let mut changes = vec![];
        let properties = ["Ordinal","Status","Project","Estimate","Developer","Manager","Description"];
        for property in properties.iter() {
            let prop = Yaml::String(String::from(*property));
            if let Some(value) = self.get(&repo, &prop) {
                let after = match value {
                    Yaml::String(ref s) => s.clone(),
                    Yaml::Integer(i) => format!("{}", i),
                    Yaml::Real(i) => format!("{}", i),
                    Yaml::Boolean(b) => format!("{}", b),
                    Yaml::Null => format!(""),
                    _ => String::from("[unknown]")
                };
                changes.push((String::from(*property), None, after));
            }
        };
        changes
    }
}
<|endoftext|>


<commit_before>#![unstable(feature = "futures_api",
            reason = "futures in libcore are unstable",
            issue = "50547")]

use crate::marker::Unpin;
use crate::ops;
use crate::pin::Pin;
use crate::task::{Context, Poll};

/// A future represents an asynchronous computation.
///
/// A future is a value that may not have finished computing yet. This kind of
/// "asynchronous value" makes it possible for a thread to continue doing useful
/// work while it waits for the value to become available.
///
/// # The `poll` method
///
/// The core method of future, `poll`, *attempts* to resolve the future into a
/// final value. This method does not block if the value is not ready. Instead,
/// the current task is scheduled to be woken up when it's possible to make
/// further progress by `poll`ing again. The wake up is performed using
/// the `waker` argument of the `poll()` method, which is a handle for waking
/// up the current task.
///
/// When using a future, you generally won't call `poll` directly, but instead
/// `await!` the value.
#[doc(spotlight)]
#[must_use = "futures do nothing unless polled"]
pub trait Future {
    /// The type of value produced on completion.
    type Output;

    /// Attempt to resolve the future to a final value, registering
    /// the current task for wakeup if the value is not yet available.
    ///
    /// # Return value
    ///
    /// This function returns:
    ///
    /// - [`Poll::Pending`] if the future is not ready yet
    /// - [`Poll::Ready(val)`] with the result `val` of this future if it
    ///   finished successfully.
    ///
    /// Once a future has finished, clients should not `poll` it again.
    ///
    /// When a future is not ready yet, `poll` returns `Poll::Pending` and
    /// stores a clone of the [`Waker`] copied from the current [`Context`].
    /// This [`Waker`] is then woken once the future can make progress.
    /// For example, a future waiting for a socket to become
    /// readable would call `.clone()` on the [`Waker`] and store it.
    /// When a signal arrives elsewhere indicating that the socket is readable,
    /// `[Waker::wake]` is called and the socket future's task is awoken.
    /// Once a task has been woken up, it should attempt to `poll` the future
    /// again, which may or may not produce a final value.
    ///
    /// Note that on multiple calls to `poll`, only the most recent
    /// [`Waker`] passed to `poll` should be scheduled to receive a
    /// wakeup.
    ///
    /// # Runtime characteristics
    ///
    /// Futures alone are *inert*; they must be *actively* `poll`ed to make
    /// progress, meaning that each time the current task is woken up, it should
    /// actively re-`poll` pending futures that it still has an interest in.
    ///
    /// The `poll` function is not called repeatedly in a tight loop -- instead,
    /// it should only be called when the future indicates that it is ready to
    /// make progress (by calling `wake()`). If you're familiar with the
    /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
    /// typically do *not* suffer the same problems of "all wakeups must poll
    /// all events"; they are more like `epoll(4)`.
    ///
    /// An implementation of `poll` should strive to return quickly, and should
    /// not block. Returning quickly prevents unnecessarily clogging up
    /// threads or event loops. If it is known ahead of time that a call to
    /// `poll` may end up taking awhile, the work should be offloaded to a
    /// thread pool (or something similar) to ensure that `poll` can return
    /// quickly.
    ///
    /// An implementation of `poll` may also never cause memory unsafety.
    ///
    /// # Panics
    ///
    /// Once a future has completed (returned `Ready` from `poll`),
    /// then any future calls to `poll` may panic, block forever, or otherwise
    /// cause any kind of bad behavior except causing memory unsafety.
    /// The `Future` trait itself provides no guarantees about the behavior
    /// of `poll` after a future has completed.
    ///
    /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
    /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
    /// [`Context`]: ../task/struct.Context.html
    /// [`Waker`]: ../task/struct.Waker.html
    /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

impl<F: ?Sized + Future + Unpin> Future for &mut F {
    type Output = F::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        F::poll(Pin::new(&mut **self), cx)
    }
}

impl
 Future for Pin

where
    P: Unpin + ops::DerefMut,
    P::Target: Future,
{
    type Output = <
::Target as Future>::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        Pin::get_mut(self).as_mut().poll(cx)
    }
}
<commit_msg>Auto merge of #60128 - jimblandy:futures-doc-fix, r=withoutboats<commit_after>#![unstable(feature = "futures_api",
            reason = "futures in libcore are unstable",
            issue = "50547")]

use crate::marker::Unpin;
use crate::ops;
use crate::pin::Pin;
use crate::task::{Context, Poll};

/// A future represents an asynchronous computation.
///
/// A future is a value that may not have finished computing yet. This kind of
/// "asynchronous value" makes it possible for a thread to continue doing useful
/// work while it waits for the value to become available.
///
/// # The `poll` method
///
/// The core method of future, `poll`, *attempts* to resolve the future into a
/// final value. This method does not block if the value is not ready. Instead,
/// the current task is scheduled to be woken up when it's possible to make
/// further progress by `poll`ing again. The `context` passed to the `poll`
/// method can provide a `Waker`, which is a handle for waking up the current
/// task.
///
/// When using a future, you generally won't call `poll` directly, but instead
/// `await!` the value.
#[doc(spotlight)]
#[must_use = "futures do nothing unless polled"]
pub trait Future {
    /// The type of value produced on completion.
    type Output;

    /// Attempt to resolve the future to a final value, registering
    /// the current task for wakeup if the value is not yet available.
    ///
    /// # Return value
    ///
    /// This function returns:
    ///
    /// - [`Poll::Pending`] if the future is not ready yet
    /// - [`Poll::Ready(val)`] with the result `val` of this future if it
    ///   finished successfully.
    ///
    /// Once a future has finished, clients should not `poll` it again.
    ///
    /// When a future is not ready yet, `poll` returns `Poll::Pending` and
    /// stores a clone of the [`Waker`] copied from the current [`Context`].
    /// This [`Waker`] is then woken once the future can make progress.
    /// For example, a future waiting for a socket to become
    /// readable would call `.clone()` on the [`Waker`] and store it.
    /// When a signal arrives elsewhere indicating that the socket is readable,
    /// [`Waker::wake`] is called and the socket future's task is awoken.
    /// Once a task has been woken up, it should attempt to `poll` the future
    /// again, which may or may not produce a final value.
    ///
    /// Note that on multiple calls to `poll`, only the [`Waker`] from the
    /// [`Context`] passed to the most recent call should be scheduled to
    /// receive a wakeup.
    ///
    /// # Runtime characteristics
    ///
    /// Futures alone are *inert*; they must be *actively* `poll`ed to make
    /// progress, meaning that each time the current task is woken up, it should
    /// actively re-`poll` pending futures that it still has an interest in.
    ///
    /// The `poll` function is not called repeatedly in a tight loop -- instead,
    /// it should only be called when the future indicates that it is ready to
    /// make progress (by calling `wake()`). If you're familiar with the
    /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
    /// typically do *not* suffer the same problems of "all wakeups must poll
    /// all events"; they are more like `epoll(4)`.
    ///
    /// An implementation of `poll` should strive to return quickly, and should
    /// not block. Returning quickly prevents unnecessarily clogging up
    /// threads or event loops. If it is known ahead of time that a call to
    /// `poll` may end up taking awhile, the work should be offloaded to a
    /// thread pool (or something similar) to ensure that `poll` can return
    /// quickly.
    ///
    /// # Panics
    ///
    /// Once a future has completed (returned `Ready` from `poll`), calling its
    /// `poll` method again may panic, block forever, or cause other kinds of
    /// problems; the `Future` trait places no requirements on the effects of
    /// such a call. However, as the `poll` method is not marked `unsafe`,
    /// Rust's usual rules apply: calls must never cause undefined behavior
    /// (memory corruption, incorrect use of `unsafe` functions, or the like),
    /// regardless of the future's state.
    ///
    /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
    /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
    /// [`Context`]: ../task/struct.Context.html
    /// [`Waker`]: ../task/struct.Waker.html
    /// [`Waker::wake`]: ../task/struct.Waker.html#method.wake
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

impl<F: ?Sized + Future + Unpin> Future for &mut F {
    type Output = F::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        F::poll(Pin::new(&mut **self), cx)
    }
}

impl
 Future for Pin

where
    P: Unpin + ops::DerefMut,
    P::Target: Future,
{
    type Output = <
::Target as Future>::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        Pin::get_mut(self).as_mut().poll(cx)
    }
}
<|endoftext|>