<commit_before>#![allow(ctypes)]
#![no_std]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static log_irq : bool = false;
static log_idle : bool = false;
static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

#[inline(never)]
pub fn generic_irq_handler(vec : u8) {
    if log_irq {
        write("IRQ! vec=");
        con::writeUInt(vec as uint);
        con::newline();
    }

    let c = cpu();
    let mask = 1 << (vec - 32);
    if c.irq_delayed & mask != 0 {
        write("IRQ: already delayed\n");
        return;
    }
    c.irq_delayed |= mask;
    if !c.irq_process().is_some() {
        abort("IRQ: no irq process");
    }
    let p = c.irq_process().unwrap();

    if log_irq {
        write("IRQ: proc=");
        con::writeMutPtr(p);
        con::newline();
    }

    syscall::try_deliver_irq(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting in find_add_backing.
    p.aspace().add_pte(back.vaddr(), back.pte());

    unsafe { cpu().switch_to(p); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        if log_idle {
            write("idle\n");
        }
        cpu().process = None;
        unsafe { asm!("sti; hlt; cli" :::: "volatile"); }
    }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,

    irq_process : Option<&'static mut Process>,
    irq_delayed : uint,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None,
            irq_process : None,
            irq_delayed : 0,
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => self.switch_to(&mut *p),
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p as *mut Process);
            write(" rip=");
            con::writeHex(p.regs.rip as uint);
            con::newline();
        }
        p.unset(process::Queued);
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            fastret(p);
        } else {
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    fn get_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn leave_proc(&mut self) {
        match self.process {
            Some(ref mut p) => p.unset(process::Running),
            None => (),
        };
    }

    fn irq_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.irq_process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn is_irq_process(&self, p: &mut Process) -> bool {
        match self.irq_process {
        None => false,
        Some(ref q) => (p as *mut Process) == (*q as *mut Process),
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret : *mut PerCpu;
        asm!("movq %gs:($1), $0" : "=r"(ret) : "r"(0));
        return &mut *ret;
    }
}

#[lang="exchange_malloc"]
#[inline(always)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    #[inline(never)]
    fn malloc_() -> *mut u8 {
        cpu().memory.alloc_frame_panic()
    }
    malloc_()
}

#[inline(always)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    #[inline(never)]
    fn free_(p: *mut u8) {
        cpu().memory.free_frame(p);
    }
    if p.is_not_null() {
        free_(p as *mut u8);
    }
}

#[lang="exchange_free"]
#[inline(always)]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        if i == 0 {
            cpu.irq_process = Some(unsafe { &mut *(p as *mut Process) });
        }
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<commit_msg>More checking in the un/queueing code<commit_after>#![allow(ctypes)]
#![no_std]
#![feature(globs)]
#![feature(asm)]
// Adding pub mod (another fix for this warning) increases footprint, so just
// disble it instead.
#![allow(visible_private_types)]

extern crate core;

use core::prelude::*;
use core::mem::*;

use aspace::AddressSpace;
use con::write;
use dlist::DList;
use process::Process;
use start32::MultiBootInfo;
//use start32::OrigMultiBootInfo;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;
use x86::idt;
pub use x86::idt::irq_entry;
pub use syscall::syscall;

mod aspace;
#[allow(dead_code)]
mod con;
mod dict;
mod dlist;
mod mboot;
mod mem;
mod process;
mod start32;
mod syscall;
pub mod util;
mod x86;

static log_assoc_procs : bool = false;
static log_page_fault : bool = false;
static log_switch : bool = false;
static log_queue : bool = false;
static log_irq : bool = false;
static log_idle : bool = false;

static mem_test : bool = false;

#[allow(dead_code)]
fn writeMBInfo(infop : *mboot::Info) {
    con::write("Multiboot info at ");
    con::writePtr(infop);
    con::putc('\n');

    let &info = unsafe { &*infop };
    con::write("Flags: ");
    con::writeHex(info.flags as uint);
    con::newline();

    if info.has(mboot::MemorySize) {
        con::writeUInt(info.mem_lower as uint);
        con::write("kB lower memory, ");
        con::writeUInt(info.mem_upper as uint);
        con::write("kB upper memory, ");
        con::writeUInt(((info.mem_lower + info.mem_upper + 1023) / 1024) as uint);
        con::write("MB total.\n");
    }
    if info.has(mboot::CommandLine) {
        let cmdline : *u8 = PhysAddr(info.cmdline as uint);
        con::write("Command line @");
        con::writePtr(cmdline);
        con::write(" (");
        con::writeHex(info.cmdline as uint);
        con::write(") \"");
        con::writeCStr(cmdline);
        con::write("\"\n");
    }
}

#[inline(never)]
pub fn generic_irq_handler(vec : u8) {
    if log_irq {
        write("IRQ! vec=");
        con::writeUInt(vec as uint);
        con::newline();
    }

    let c = cpu();
    let mask = 1 << (vec - 32);
    if c.irq_delayed & mask != 0 {
        write("IRQ: already delayed\n");
        return;
    }
    c.irq_delayed |= mask;
    if !c.irq_process().is_some() {
        abort("IRQ: no irq process");
    }
    let p = c.irq_process().unwrap();

    if log_irq {
        write("IRQ: proc=");
        con::writeMutPtr(p);
        con::newline();
    }

    syscall::try_deliver_irq(p);
}

pub fn page_fault(p : &mut Process, error : uint) -> ! {
    mod pf_errors {
        #![allow(dead_code)]
        // Error code flags.
        pub static PRESENT : uint = 1;
        pub static WRITE : uint = 2;
        pub static USER : uint = 4;
        pub static RSVD : uint = 8;
        pub static INSTR : uint = 16;
    }

    if log_page_fault {
        write("page fault ");
        con::writeHex(error);
        write(" cr2=");
        con::writePHex(x86::cr2());
        write(" rip=");
        con::writePHex(p.regs.rip as uint);
        write(" in process ");
        con::writeMutPtr(p);
        con::newline();
    }

    if (error & pf_errors::USER) == 0 {
        abort("kernel page fault\n");
    }

    let back = p.aspace().find_add_backing(x86::cr2() & !0xfff);
    // FIXME should return e.g. Option<> so we can detect failures better than
    // just aborting in find_add_backing.
    p.aspace().add_pte(back.vaddr(), back.pte());

    unsafe { cpu().switch_to(p); }
}

pub fn handler_NM() {
    abort("NM");
}

pub fn idle() -> ! {
    loop {
        if log_idle {
            write("idle\n");
        }
        cpu().process = None;
        unsafe { asm!("sti; hlt; cli" :::: "volatile"); }
    }
}

pub struct PerCpu {
    selfp : *mut PerCpu,
    stack : *mut u8,
    process : Option<&'static mut Process>,

    // End of assembly-fixed fields.
    memory : mem::PerCpu,
    runqueue : DList<Process>,

    irq_process : Option<&'static mut Process>,
    irq_delayed : uint,
}

impl PerCpu {
    unsafe fn new() -> *mut PerCpu {
        let mut mem = mem::PerCpu::new();
        let p : *mut PerCpu = mem.alloc_frame_panic();
        let stack : *mut u8 = mem.alloc_frame_panic();
        *p = PerCpu {
            selfp : p,
            stack : stack,
            memory : mem,
            runqueue : DList::empty(),
            process : None,
            irq_process : None,
            irq_delayed : 0,
        };
        return p
    }

    unsafe fn start(&mut self) {
        setup_msrs(self.selfp as uint);
    }

    fn queue(&mut self, p: &mut Process) {
        if log_queue {
            write("queue ");
            con::writeMutPtr(p);
            if p.is(process::Queued) {
                write(" already queued");
            }
            con::newline();
        }
        if !p.is_queued() {
            p.set(process::Queued);
            self.runqueue.append(p);
        }
    }

    unsafe fn run(&mut self) -> ! {
        match self.runqueue.pop() {
            Some(p) => {
                let r = &mut *p;
                if !r.is_queued() {
                    abort("popped unqueued item?");
                }
                r.unset(process::Queued);
                self.switch_to(r);
            },
            None => idle()
        }
    }

    unsafe fn switch_to(&mut self, p: &mut Process) -> ! {
        if log_switch {
            write("switch_to ");
            con::writeMutPtr(p);
            write(" rip=");
            con::writeHex(p.regs.rip as uint);
            if p.is(process::FastRet) {
                write(" fastret");
            }
            if p.is(process::Queued) {
                write(" queued");
            }
            con::newline();
        }
        p.set(process::Running);
        self.process = transmute(p as *mut Process);
        // TODO Check fpu_process, see if we need to set/reset TS bit in cr0
        x86::set_cr3(p.cr3);
        extern "C" {
            fn fastret(p : &mut Process) -> !;
            fn slowret(p : &mut Process) -> !;
        }
        if p.is(process::FastRet) {
            p.unset(process::FastRet);
            fastret(p);
        } else {
            slowret(p);
        }
    }

    fn syscall_return(&mut self, p: &mut Process, rax : uint) -> ! {
        p.regs.rax = rax;
        unsafe { self.switch_to(p); }
    }

    fn get_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn leave_proc(&mut self) {
        match self.process {
            Some(ref mut p) => p.unset(process::Running),
            None => (),
        };
    }

    fn irq_process<'a>(&'a mut self) -> Option<&'a mut Process> {
        match self.irq_process {
            Some(ref p) => Some(unsafe { &mut *(*p as *mut Process) }),
            None => None,
        }
    }

    fn is_irq_process(&self, p: &mut Process) -> bool {
        match self.irq_process {
        None => false,
        Some(ref q) => (p as *mut Process) == (*q as *mut Process),
        }
    }
}

// NB: One of the funky guarantees that Rust gives/requires is that there is
// at most one &mut reference to the same thing at any one time. This function
// can't quite guarantee that...
pub fn cpu() -> &mut PerCpu {
    unsafe {
        let mut ret : *mut PerCpu;
        asm!("movq %gs:($1), $0" : "=r"(ret) : "r"(0));
        return &mut *ret;
    }
}

#[lang="exchange_malloc"]
#[inline(always)]
pub fn xmalloc(size : uint, _align: uint) -> *mut u8 {
    malloc(size)
}

pub fn malloc(size : uint) -> *mut u8 {
    if size > 4096 {
        abort("oversized malloc");
    }
    #[inline(never)]
    fn malloc_() -> *mut u8 {
        cpu().memory.alloc_frame_panic()
    }
    malloc_()
}

#[inline(always)]
pub fn alloc<T>() -> *mut T {
    malloc(size_of::<T>()) as *mut T
}

pub fn free<T>(p : *mut T) {
    #[inline(never)]
    fn free_(p: *mut u8) {
        cpu().memory.free_frame(p);
    }
    if p.is_not_null() {
        free_(p as *mut u8);
    }
}

#[lang="exchange_free"]
#[inline(always)]
pub fn xfree(p : *mut u8, _size: uint, _align: uint) {
    free(p);
}

unsafe fn setup_msrs(gs : uint) {
    use x86::msr::*;
    use x86::rflags;
    use x86::efer;
    use x86::seg;
    #[allow(dead_code)]
    extern {
        fn syscall_entry_stub();
        fn syscall_entry_compat();
    }

    wrmsr(STAR, (seg::user_code32_base << 16) | seg::code);
    wrmsr(LSTAR, syscall_entry_stub as uint);
    wrmsr(CSTAR, syscall_entry_compat as uint);
    // FIXME: We want to clear a lot more flags - Direction for instance.
    // FreeBSD sets PSL_NT|PSL_T|PSL_I|PSL_C|PSL_D
    wrmsr(FMASK, rflags::IF | rflags::VM);
    wrmsr(EFER, rdmsr(EFER) | efer::SCE | efer::NXE);
    wrmsr(GSBASE, gs);
}

#[lang="eh_personality"]
fn dummy() {}

#[inline(never)]
fn new_proc_simple(start : uint, end_unaligned : uint) -> *mut Process {
    let end = (end_unaligned + 0xfff) & !0xfff;
    let start_page = start & !0xfff;
    let aspace : *mut AddressSpace = unsafe { transmute(box AddressSpace::new()) };
    let ret : *mut Process = unsafe { transmute(box Process::new(aspace)) };
    unsafe {
        (*ret).regs.rsp = 0x100000;
        (*ret).regs.rip = 0x100000 + (start & 0xfff);
    }

    unsafe {
        use aspace::mapflag::*;
        // Stack at 1MB - 4kB (not executable)
        (*aspace).mapcard_set(0x0ff000, 0, 0, Anon | R | W);
        (*aspace).mapcard_set(0x100000, 0, start_page - 0x100000, Phys | R | X);
        (*aspace).mapcard_set(0x100000 + (end - start_page), 0, 0, 0);
        /*match (*ret).aspace().mapcard_find(0x100000) {
            Some(c) => {
                write("0x100000: ");
                con::writePHex(c.vaddr());
                write(" -> paddr ");
                con::writePHex(c.paddr(0x100000));
                con::newline();
            },
            None => {
                abort("mapcard we added is not there anymore");
            }
        }*/
    }
    return ret;
}

fn assoc_procs(p : &mut Process, i : uint, q : &mut Process, j : uint) {
    if log_assoc_procs {
        con::writeMutPtr(p);
        con::putc(':');
        con::writeUInt(i);
        write(" <-> ");
        con::writeUInt(j);
        con::putc(':');
        con::writeMutPtr(q);
        con::newline();
    }
    p.assoc_handles(j, q, i);
}

fn init_modules(cpu : &mut PerCpu) {
    let &info = start32::MultiBootInfo();
    if !info.has(mboot::Modules) {
        return;
    }
    let mut head = DList::empty();
    let mut count = 0;
    for m in info.modules(start32::PhysAddr).iter() {
        write("Module ");
        con::writeHex(m.start as uint);
        write("..");
        con::writeHex(m.end as uint);
        write(": ");
        con::writeCStr(start32::PhysAddr(m.string as uint));
        con::newline();

        head.append(new_proc_simple(m.start as uint, m.end as uint));
        count += 1;
    }
    con::writeUInt(count);
    con::newline();
    // Now all processes are in our list. We need to remove them before it
    // gets possible to make them runnable.
    let mut i = 0;
    for p in head.iter() {
        if i == 0 {
            cpu.irq_process = Some(unsafe { &mut *(p as *mut Process) });
        }
        i += 1;
        // start iterating at j = i + 1, and q = p.next...
        let mut j = 0;
        for q in head.iter() {
            j += 1;
            if i < j {
                assoc_procs(p, i, q, j);
            }
        }
    }
    while match head.pop() {
        Some(p) => { cpu.queue(unsafe { &mut *p }); true },
        None => false
    } {}
}

pub fn dump_runqueue(queue: &DList<Process>) {
    let mut count = 0;
    for _ in queue.iter() {
        count += 1;
    }
    con::write("runqueue: ");
    con::writeUInt(count);
    con::newline();
    for p in queue.iter() {
        p.dump();
    }
}

#[no_mangle]
pub unsafe fn start64() -> ! {
    con::init(MutPhysAddr(0xb8000), 80, 25);
    con::clear();
    write("Hello World!\n");
    //writeMBInfo(start32::MultiBootInfo());

    x86::lgdt(start32::Gdtr());
    x86::ltr(x86::seg::tss64);

    idt::init();

    mem::global.init(start32::MultiBootInfo(), start32::MemoryStart());
    write("Memory initialized. ");
    mem::global.stat();

    let pcpu = PerCpu::new();
    let ref mut cpu = *pcpu;
    cpu.start();
    if mem_test {
        cpu.memory.test();
        mem::global.stat();
    }

    init_modules(cpu);
    //dump_runqueue(&cpu.runqueue);

//  let mut i = 0;
//  loop {
//      con.writeUInt(i);
//      con.putc('\n');
//      i += 1;
//  }
    cpu.run();
}
<|endoftext|>


<commit_before>

fn main() {

    let mut i: i32 = 1;

    let mut cl_inc_1 = || { i = i + 1; i} ;
    //let mut cl_inc_2 = || { i = i + 2; i} ;

    println!("{:?},", cl_inc_1());

    //println!("{:?}, {:?}", cl_inc_1(), cl_inc_1());
}

/*

Closure captures its enclosing environment and holds on to it even when
enclosing environment is gone while calling the closure.

RUST CLOSURE RULES
------------------

1. While defining a closure, both input and return can be inferred most of the times obserbing the type of captures.

let i = 100 ;

let cl_inc_1 = || { i = i + 1; i} ;

/* Or you could write it as */
let cl_inc_1 = || -> i32 { i = i + 1; i} ;

2. Closure will capture the variable in least restricting way.
	1. Reference (&T)  2. Mutable Reference (&mut T)  3. By Value (T)

THINK OF CAPTURE AS PARAMETER BUT IN SAME SCOPE AS CAPTURING ENVIRONMENT

Capture type = &T
----------------

let i = 100;
let cl_inc_1 = ||{  let j: i32 = i + 1 + i + 2;
					println!("{}", i);
					j}; // 'i' is captured by the closure as &T

let cl_inc_2 = ||{let j: i32 = i + 2 + i + 3; j};

println!("{}", i); // 'i' borrowed as &T

CLOSURE TYPE = &T

// Can call same closure multiple times
cl_inc_1();
cl_inc_1();

// At the same time you can call other closure multiple times which is also capturing the same variable 'i' as &T
cl_inc_2();
cl_inc_2();

This all possible because of immutable references

Capture type = &mut T
---------------------
let mut i = 100;
let k = 1000;

let mut cl_inc_1 = ||{ 	i = i + k + 1;
						let j: i32 = i + 1 + i + 2;
						println!("{}", i);
						j}; //'i' is captured by closure as '&mut T'.
							//'k' is captured by closure as '&T'

//CLOSURE TYPE = &mut T

//Cannot use 'i' as long as 'cl_inc_1' is in scope
//let cl_inc_2 = ||{ i = i + 2; let j: i32 = i + 2 + i + 3; j};
//println!("{}", i);

Capture type = T
----------------
let movable = Box::new(3);

// `drop` requires `T` so this must take by value. A copy type
// would copy into the closure leaving the original untouched.
// A non-copy must move and so `movable` immediately moves into
// the closure.

//*** NOTE: Equivalant to closure having a implicit parameter of movable: Box<i32> = movable (T) but in same scope of enclosing env.
let consume = || {
    println!("`movable`: {:?}", movable); //closure captures 'movable' as &T
    drop(movable);    // closure captures 'movable' as 'T'
	//drop(movable);  // can't use movable ever again. It is moved
};

/* You cannot define another closure which is using 'movable'
let consume2 = || {
    println!("`movable`: {:?}", movable);
    drop(movable);
	//drop(movable);
}; // closure captures 'movable' as 'T'

*/

//CLOSURE TYPE = T

//Can't use 'movable' here as well. 'movable' is already moved. We don't know when the closure gets called and use invalidated box
//drop(movable)

//Cannot call consume twice as closure 'consume' is moving a non-copy type
//and that non-copy type should not me moved again while calling the consume() again
consume();
//consume();

'consume' closure is of type 'T' now. Which means it moved one of the environment
variable into it and we call that closure again because it will again try
to move the variable which is 'dead'. 'movable' in this case.

3. While passing the closure to a function, we need to specify its type.

Closure type 'T' = FnOnce  ==>  Which means function accepts any closure which might have possibly captured the
                                environment variable in all possible ways

								But you cannot call this closure twice because it might have moved an environment variable

Closure type '&mut T' = FnMut  ==> Function accepts a closure which might've captured a variable as '&mut T' or '&T' or both

Closure type '&T' = Fn  ==> Function accepts a closure which only captures a variable as &T
*/<commit_msg>Update main.rs<commit_after>

fn main() {

    let mut i: i32 = 1;

    let mut cl_inc_1 = || { i = i + 1; i} ;
    //let mut cl_inc_2 = || { i = i + 2; i} ;

    println!("{:?},", cl_inc_1());

    //println!("{:?}, {:?}", cl_inc_1(), cl_inc_1());
}

/*

Closure captures its enclosing environment and holds on to it even when
enclosing environment is gone while calling the closure.

RUST CLOSURE RULES
------------------

1. While defining a closure, both input and return can be inferred most of the times obserbing the type of captures.

let i = 100 ;

let cl_inc_1 = || { i = i + 1; i} ;

/* Or you could write it as */
let cl_inc_1 = || -> i32 { i = i + 1; i} ;

2. Closure will capture the variable in least restricting way.
	1. Reference (&T)  2. Mutable Reference (&mut T)  3. By Value (T)

W.r.t closures, THINK OF CAPTURE AS AN AUTO GENERATED ANONYMOUS STRUCT THAT COMPILER GENERATES
WHERE ITS MEMBERS ARE ASSIGNMETS TO ENVIRONMENT VARIABLES WITH SAME NAMES. THINK OF CLOSURE BODY AS
ITS METHOD. LET'S CALL THIS METHOD AS 'M1'

WHETHER THESE ASSIGNMENTS ARE REFERENCES or MUTABLE REFERENCES or MOVES IS DECIDED BY
THE COMPILER IN A LEAST RESTRESTIVE MANNER BY SEEING HOW VARIABLES ARE BEING USED IN CLOSURE.

THINK THAT THIS STRUCT IS IN SAME SCOPE AS THE ENVIRONMENT. SO ALL THE OWNERSHIP/BORROW RULES APPLY.

YOU CAN THINK OF ASSIGNMENT TO CLOSURE AS CREATING A STRUCTURE 'VARIABLE'. SO ALL THE ASSIGNMENTS ARE DONE AND
OWNERSHIP/BORROW RULES COMES INTO PICTURE.

ANONYMOUS_STRUCT_VARIABLE() IS JUST SYNTACTIC SUGAR FOR CALLING M1()

Capture type = &T
----------------

let i = 100;

		 /*1. Anonymous struct TYPE created (generated) with the implemementation as its method */
let cl_inc_1 = ||{  let j: i32 = i + 1 + i + 2;
		    println!("{}", i);
		    j
		 };

// 'i' is captured by the closure as &T

/*2. Anonymous struct VARIABLE
created with all the
assignments to the environment */

/* After 2, Owner/Borrow rules apply. */

let cl_inc_2 = ||{let j: i32 = i + 2 + i + 3; j};

println!("{}", i); // 'i' borrowed as &T

// Can call same closure multiple times
cl_inc_1();
cl_inc_1();

// At the same time you can call other closure multiple times which is also capturing the same variable 'i' as &T
cl_inc_2();
cl_inc_2();

This all possible because of immutable references

Capture type = &mut T
---------------------
let mut i = 100;
let k = 1000;

let mut cl_inc_1 = ||{ 	i = i + k + 1;
			let j: i32 = i + 1 + i + 2;
			println!("{}", i);
		        j
		     };

//'i' is captured by closure as '&mut T'.
//'k' is captured by closure as '&T'

//'i' already assigned mutably to 'cl_inc_1' struct variable. Cannot use 'i' as long as 'cl_inc_1' is in scope.
//let cl_inc_2 = ||{ i = i + 2; let j: i32 = i + 2 + i + 3; j};
//println!("{}", i);

Closure compiler struct = {
				i: &mut i32,
				k: &i32
			  }

Capture type = T
----------------
let movable = Box::new(3);

// `drop` requires `T` so this must take by value. A copy type
// would copy into the closure leaving the original untouched.
// A non-copy must move and so `movable` immediately moves into
// the closure.

let consume = || {
    println!("`movable`: {:?}", movable); // closure captures 'movable' as &T
    drop(movable);                        // closure captures 'movable' as 'T'
    //drop(movable);                      // can't use movable ever again. This time the internal 'movable' is also moved into 'drop'
};

Closure compiler struct = {
				movable: Box<i32>
			  }

/* You cannot define another closure which is using 'movable'
let consume2 = || {
    println!("`movable`: {:?}", movable);
    drop(movable);
	//drop(movable);
}; // closure captures 'movable' as 'T'

*/

//CLOSURE TYPE = T

//Can't use 'movable' here as well. 'movable' is already moved. We don't know when the closure gets called and use invalidated box
//drop(movable)

//Cannot call consume twice as closure 'consume' is moving a non-copy type
//and that non-copy type should not me moved again while calling the consume() again
consume();
//consume();

'consume' closure is of type 'T' now. Which means it moved one of the environment
variable into it and we call that closure again because it will again try
to move the variable which is 'dead'. 'movable' in this case.

3. While passing the closure to a function, we need to specify its type.

FnOnce  ==>  Which means function accepts any closure which might have possibly captured the
             environment variable in all possible ways

But you cannot call this closure twice because it might have moved an environment variable

Closure type '&mut T' = FnMut  ==> Function accepts a closure which might've captured a variable as '&mut T' or '&T' or both

Closure type '&T' = Fn  ==> Function accepts a closure which only captures a variable as &T
*/
<|endoftext|>


<commit_before>#![feature(old_path)]

extern crate piston;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::cell::RefCell;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};
use sdl2_window::Sdl2Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let image = Path::new("./bin/assets/rust-logo.png");
    let image = Texture::from_path(&image).unwrap();
    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::RenderEvent;

        if let Some(args) = e.render_args() {
            use graphics::*;
            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                graphics::clear([1.0; 4], gl);
                graphics::image(&image, c.transform, gl);
            });
        };
    }
}
<commit_msg>Fix old_path import in image example<commit_after>#![feature(old_path)]

extern crate piston;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::old_path::*;

use std::cell::RefCell;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};
use sdl2_window::Sdl2Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let rust_logo = Path::new("./bin/assets/rust-logo.png");
    let rust_logo = Texture::from_path(&rust_logo).unwrap();
    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::*;

        if let Some(args) = e.render_args() {
            use graphics::*;

            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                clear([1.0; 4], gl);
                image(&rust_logo, c.transform, gl);
            });
        };
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

#[derive(Debug)]
pub struct Errors(HashMap<String, Vec<String>>);

impl Errors {
    pub fn new() -> Errors {
        Errors(HashMap::new())
    }

    pub fn inner(self) -> HashMap<String, Vec<String>> {
        self.0
    }

    pub fn add(&mut self, field: &str, err: &str) {
        self.0.entry(field.to_string()).or_insert_with(|| vec![]).push(err.to_string());
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

pub trait Validate {
    fn validate(&self) -> Result<(), Errors>;
}

/// Contains all the validators that can be used
///
/// In this crate as it's not allowed to export more than the proc macro
/// in a proc macro crate
#[derive(Debug, Clone)]
pub enum Validator {
    // String is the path to the function
    Custom(String),
    // String is the name of the field to match
    MustMatch(String),
    // value is a &str
    Email,
    // value is a &str
    Url,
    // value is a &str or a HashMap<String, ..>
    Contains(String),
    // value is a &str
    Regex(String),
    // value is a number
    Range {
        min: f64,
        max: f64,
    },
    // value is anything that impl HasLen
    Length {
        min: Option<u64>,
        max: Option<u64>,
        equal: Option<u64>,
    },
}
<commit_msg>Implement Error for Errors.<commit_after>use std::{self, fmt};
use std::collections::HashMap;

#[derive(Debug)]
pub struct Errors(HashMap<String, Vec<String>>);

impl Errors {
    pub fn new() -> Errors {
        Errors(HashMap::new())
    }

    pub fn inner(self) -> HashMap<String, Vec<String>> {
        self.0
    }

    pub fn add(&mut self, field: &str, err: &str) {
        self.0.entry(field.to_string()).or_insert_with(|| vec![]).push(err.to_string());
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl fmt::Display for Errors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Validation failed:\n")?;
        for (field, errs) in &self.0 {
            write!(fmt, "    {}: [", field)?;

            let last = errs.len() - 1;
            for (index, err) in errs.iter().enumerate() {
                write!(fmt, "{}", err)?;
                if index < last { write!(fmt, ", ")? }
            }
            write!(fmt, "]\n")?;
        }
        Ok(())
    }
}

impl std::error::Error for Errors {
    fn description(&self) -> &str {
        "validation failed"
    }

    fn cause(&self) -> Option<&std::error::Error> {
        None
    }
}

pub trait Validate {
    fn validate(&self) -> Result<(), Errors>;
}

/// Contains all the validators that can be used
///
/// In this crate as it's not allowed to export more than the proc macro
/// in a proc macro crate
#[derive(Debug, Clone)]
pub enum Validator {
    // String is the path to the function
    Custom(String),
    // String is the name of the field to match
    MustMatch(String),
    // value is a &str
    Email,
    // value is a &str
    Url,
    // value is a &str or a HashMap<String, ..>
    Contains(String),
    // value is a &str
    Regex(String),
    // value is a number
    Range {
        min: f64,
        max: f64,
    },
    // value is anything that impl HasLen
    Length {
        min: Option<u64>,
        max: Option<u64>,
        equal: Option<u64>,
    },
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::{Probe, ServerAddress, SharedState, State, StateChangeImpl, StateMachine};
use chrono::{DateTime, Duration, Utc};
use rand::Rng;
use slog_scope::{debug, info};
use std::{
    sync::{Arc, Condvar, Mutex},
    thread,
};

#[derive(Debug, PartialEq)]
pub(super) struct Poll {}

// create_state_step!(Poll => Probe);

/// Implements the state change for `State<Poll>`.
///
/// This state is used to control when to go to the `State<Probe>`.
impl StateChangeImpl for State<Poll> {
    fn name(&self) -> &'static str {
        "poll"
    }

    fn handle(self, shared_state: &mut SharedState) -> Result<StateMachine, failure::Error> {
        let current_time: DateTime<Utc> = Utc::now();

        if shared_state.runtime_settings.is_polling_forced() {
            debug!("Moving to Probe state as soon as possible.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let last_poll = shared_state
            .runtime_settings
            .last_polling()
            .unwrap_or_else(|| {
                // When no polling has been done before, we choose an
                // offset between current time and the intended polling
                // interval and use it as last_poll
                let mut rnd = rand::thread_rng();
                let interval = shared_state.settings.polling.interval.num_seconds();
                let offset = Duration::seconds(rnd.gen_range(0, interval));

                current_time + offset
            });

        if last_poll > current_time {
            info!("Forcing to Probe state as last polling seems to happened in future.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let extra_interval = shared_state.runtime_settings.polling_extra_interval();
        if last_poll + extra_interval.unwrap_or_else(|| Duration::seconds(0)) < current_time {
            debug!("Moving to Probe state as the polling's due extra interval.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let probe = Arc::new((Mutex::new(()), Condvar::new()));
        let probe2 = probe.clone();
        let interval = shared_state.settings.polling.interval;
        thread::spawn(move || {
            let (_, ref cvar) = *probe2;
            thread::sleep(interval.to_std().unwrap());
            cvar.notify_one();
        });

        let (ref lock, ref cvar) = *probe;
        let _ = cvar.wait(lock.lock().unwrap());

        debug!("Moving to Probe state.");
        Ok(StateMachine::Probe(State(Probe {
            server_address: ServerAddress::Default,
        })))
    }
}

#[test]
fn extra_poll_in_past() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings
        .set_last_polling(Utc::now() - Duration::seconds(10))
        .unwrap();
    runtime_settings
        .set_polling_extra_interval(Duration::seconds(10))
        .unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn probe_now() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings.set_last_polling(Utc::now()).unwrap();
    runtime_settings
        .force_poll()
        .expect("failed to force polling");

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn last_poll_in_future() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings
        .set_last_polling(Utc::now() + Duration::days(1))
        .unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn interval_1_second() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;
    settings.polling.interval = Duration::seconds(1);

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings.set_last_polling(Utc::now()).unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn never_polled() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;
    settings.polling.interval = Duration::seconds(1);

    let runtime_settings = RuntimeSettings::default();
    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}
<commit_msg>updatehub: Fix condition for moving to probe state due to extra interval<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use super::{Probe, ServerAddress, SharedState, State, StateChangeImpl, StateMachine};
use chrono::{DateTime, Duration, Utc};
use rand::Rng;
use slog_scope::{debug, info};
use std::{
    sync::{Arc, Condvar, Mutex},
    thread,
};

#[derive(Debug, PartialEq)]
pub(super) struct Poll {}

// create_state_step!(Poll => Probe);

/// Implements the state change for `State<Poll>`.
///
/// This state is used to control when to go to the `State<Probe>`.
impl StateChangeImpl for State<Poll> {
    fn name(&self) -> &'static str {
        "poll"
    }

    fn handle(self, shared_state: &mut SharedState) -> Result<StateMachine, failure::Error> {
        let current_time: DateTime<Utc> = Utc::now();

        if shared_state.runtime_settings.is_polling_forced() {
            debug!("Moving to Probe state as soon as possible.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let last_poll = shared_state
            .runtime_settings
            .last_polling()
            .unwrap_or_else(|| {
                // When no polling has been done before, we choose an
                // offset between current time and the intended polling
                // interval and use it as last_poll
                let mut rnd = rand::thread_rng();
                let interval = shared_state.settings.polling.interval.num_seconds();
                let offset = Duration::seconds(rnd.gen_range(0, interval));

                current_time + offset
            });

        if last_poll > current_time {
            info!("Forcing to Probe state as last polling seems to happened in future.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let extra_interval = shared_state.runtime_settings.polling_extra_interval();
        if last_poll + extra_interval.unwrap_or_else(|| Duration::seconds(0)) > current_time {
            debug!("Moving to Probe state as the polling's due extra interval.");
            return Ok(StateMachine::Probe(State(Probe {
                server_address: ServerAddress::Default,
            })));
        }

        let probe = Arc::new((Mutex::new(()), Condvar::new()));
        let probe2 = probe.clone();
        let interval = shared_state.settings.polling.interval;
        thread::spawn(move || {
            let (_, ref cvar) = *probe2;
            thread::sleep(interval.to_std().unwrap());
            cvar.notify_one();
        });

        let (ref lock, ref cvar) = *probe;
        let _ = cvar.wait(lock.lock().unwrap());

        debug!("Moving to Probe state.");
        Ok(StateMachine::Probe(State(Probe {
            server_address: ServerAddress::Default,
        })))
    }
}

#[test]
fn extra_poll_in_past() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings
        .set_last_polling(Utc::now() - Duration::seconds(10))
        .unwrap();
    runtime_settings
        .set_polling_extra_interval(Duration::seconds(20))
        .unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn probe_now() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings.set_last_polling(Utc::now()).unwrap();
    runtime_settings
        .force_poll()
        .expect("failed to force polling");

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn last_poll_in_future() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings
        .set_last_polling(Utc::now() + Duration::days(1))
        .unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn interval_1_second() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;
    settings.polling.interval = Duration::seconds(1);

    let mut runtime_settings = RuntimeSettings::default();
    runtime_settings.set_last_polling(Utc::now()).unwrap();

    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}

#[test]
fn never_polled() {
    use super::*;
    use crate::firmware::tests::{create_fake_metadata, FakeDevice};

    let mut settings = Settings::default();
    settings.polling.enabled = true;
    settings.polling.interval = Duration::seconds(1);

    let runtime_settings = RuntimeSettings::default();
    let firmware = Metadata::from_path(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();
    let mut shared_state = SharedState {
        settings,
        runtime_settings,
        firmware,
    };

    let machine = StateMachine::Poll(State(Poll {})).move_to_next_state(&mut shared_state);

    assert_state!(machine, Probe);
}
<|endoftext|>


<commit_before>pub use response::mime::{Mime, TopLevel, SubLevel};
use response::mime::Param;
use std::default::Default;

use std::str::FromStr;
use std::borrow::Borrow;
use std::fmt;

use router::Collider;

/// Rocket's representation of HTTP Content-Types.
///
/// This type wraps raw HTTP `Content-Type`s in a type-safe manner. It provides
/// methods to create and test against common HTTP content-types. It also
/// provides methods to parse HTTP Content-Type values
/// ([from_str](#method.from_str)) and to return the ContentType associated with
/// a file extension ([from_ext](#method.from_extension)).
#[derive(Debug, Clone, PartialEq)]
pub struct ContentType(pub TopLevel, pub SubLevel, Option<Vec<Param>>);

macro_rules! is_some {
    ($ct:ident, $name:ident: $top:ident/$sub:ident) => {
        /// Returns a new ContentType that matches the MIME for this method's
        /// name.
        pub fn $ct() -> ContentType {
            ContentType::of(TopLevel::$top, SubLevel::$sub)
        }

        is_some!($name: $top/$sub);
    };

    ($name:ident: $top:ident/$sub:ident) => {
        /// Returns true if `self` is the content type matching the method's
        /// name.
        pub fn $name(&self) -> bool {
            self.0 == TopLevel::$top && self.1 == SubLevel::$sub
        }
    };
}

impl ContentType {
    #[doc(hidden)]
    #[inline(always)]
    pub fn new(t: TopLevel, s: SubLevel, params: Option<Vec<Param>>) -> ContentType {
        ContentType(t, s, params)
    }

    /// Constructs a new content type of the given top level and sub level
    /// types.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use rocket::response::mime::{TopLevel, SubLevel};
    ///
    /// let html = ContentType::of(TopLevel::Application, SubLevel::Html);
    /// assert!(html.is_html());
    /// ```
    #[inline(always)]
    pub fn of(t: TopLevel, s: SubLevel) -> ContentType {
        ContentType(t, s, None)
    }

    /// Returns a new ContentType for `*/*`, i.e., any.
    #[inline(always)]
    pub fn any() -> ContentType {
        ContentType::of(TopLevel::Star, SubLevel::Star)
    }

    /// Returns true if this content type is not one of the standard content
    /// types, that if, if it is an "extended" content type.
    pub fn is_ext(&self) -> bool {
        if let TopLevel::Ext(_) = self.0 {
            true
        } else if let SubLevel::Ext(_) = self.1 {
            true
        } else {
            false
        }
    }

    /// Returns true if the content type is JSON, i.e: `application/json`.
    is_some!(json, is_json: Application/Json);

    /// Returns true if the content type is XML, i.e: `application/xml`.
    is_some!(xml, is_xml: Application/Xml);

    /// Returns true if the content type is any, i.e.: `*/*`.
    is_some!(is_any: Star/Star);

    /// Returns true if the content type is HTML, i.e.: `application/html`.
    is_some!(html, is_html: Application/Html);

    /// Returns true if the content type is that for non-data HTTP forms, i.e.:
    /// `application/x-www-form-urlencoded`.
    is_some!(is_form: Application/WwwFormUrlEncoded);

    /// Returns true if the content type is that for data HTTP forms, i.e.:
    /// `multipart/form-data`.
    is_some!(is_data: Multipart/FormData);

    /// Returns the Content-Type associated with the extension `ext`. Not all
    /// extensions are recognized. If an extensions is not recognized, then this
    /// method returns a ContentType of `any`.
    ///
    /// # Example
    ///
    /// A recognized content type:
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let xml = ContentType::from_extension("xml");
    /// assert!(xml.is_xml());
    /// ```
    ///
    /// An unrecognized content type:
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let foo = ContentType::from_extension("foo");
    /// assert!(foo.is_any());
    /// ```
    pub fn from_extension(ext: &str) -> ContentType {
        let (top_level, sub_level) = match ext {
            "txt" => (TopLevel::Text, SubLevel::Plain),
            "html" => (TopLevel::Text, SubLevel::Html),
            "xml" => (TopLevel::Application, SubLevel::Xml),
            "js" => (TopLevel::Application, SubLevel::Javascript),
            "css" => (TopLevel::Text, SubLevel::Css),
            "json" => (TopLevel::Application, SubLevel::Json),
            "png" => (TopLevel::Image, SubLevel::Png),
            "gif" => (TopLevel::Image, SubLevel::Gif),
            "bmp" => (TopLevel::Image, SubLevel::Bmp),
            "jpeg" => (TopLevel::Image, SubLevel::Jpeg),
            "jpg" => (TopLevel::Image, SubLevel::Jpeg),
            "pdf" => (TopLevel::Application, SubLevel::Ext("pdf".into())),
            _ => (TopLevel::Star, SubLevel::Star),
        };

        ContentType::of(top_level, sub_level)
    }
}

impl Default for ContentType {
    /// Returns a ContentType of `any`, or `*/*`.
    #[inline(always)]
    fn default() -> ContentType {
        ContentType::any()
    }
}

#[doc(hidden)]
impl Into<Mime> for ContentType {
    fn into(self) -> Mime {
        Mime(self.0, self.1, self.2.unwrap_or_default())
    }
}

#[doc(hidden)]
impl<T: Borrow<Mime>> From<T> for ContentType {
    default fn from(mime: T) -> ContentType {
        let mime: Mime = mime.borrow().clone();
        ContentType::from(mime)
    }
}

#[doc(hidden)]
impl From<Mime> for ContentType {
    fn from(mime: Mime) -> ContentType {
        let params = match mime.2.len() {
            0 => None,
            _ => Some(mime.2),
        };

        ContentType(mime.0, mime.1, params)
    }
}

fn is_valid_first_char(c: char) -> bool {
    match c {
        'a'...'z' | 'A'...'Z' | '0'...'9' | '*' => true,
        _ => false,
    }
}

fn is_valid_char(c: char) -> bool {
    is_valid_first_char(c) || match c {
        '!' | '#' | '$' | '&' | '-' | '^' | '.' | '+' | '_' => true,
        _ => false,
    }
}

impl FromStr for ContentType {
    type Err = &'static str;

    /// Parses a ContentType from a given Content-Type header value.
    ///
    /// # Examples
    ///
    /// Parsing an `application/json`:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    ///
    /// let json = ContentType::from_str("application/json");
    /// assert_eq!(json, Ok(ContentType::json()));
    /// ```
    ///
    /// Parsing a content-type extension:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    /// use rocket::response::mime::{TopLevel, SubLevel};
    ///
    /// let custom = ContentType::from_str("application/x-custom").unwrap();
    /// assert!(custom.is_ext());
    /// assert_eq!(custom.0, TopLevel::Application);
    /// assert_eq!(custom.1, SubLevel::Ext("x-custom".into()));
    /// ```
    ///
    /// Parsing an invalid Content-Type value:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    ///
    /// let custom = ContentType::from_str("application//x-custom");
    /// assert!(custom.is_err());
    /// ```
    fn from_str(raw: &str) -> Result<ContentType, &'static str> {
        let slash = match raw.find('/') {
            Some(i) => i,
            None => return Err("Missing / in MIME type."),
        };

        let top_s = &raw[..slash];
        let (sub_s, _rest) = match raw.find(';') {
            Some(j) => (&raw[(slash + 1)..j], Some(&raw[(j + 1)..])),
            None => (&raw[(slash + 1)..], None),
        };

        if top_s.len() < 1 || sub_s.len() < 1 {
            return Err("Empty string.");
        }

        if !is_valid_first_char(top_s.chars().next().unwrap())
                || !is_valid_first_char(sub_s.chars().next().unwrap()) {
            return Err("Invalid first char.");
        }

        if top_s.contains(|c| !is_valid_char(c))
                || sub_s.contains(|c| !is_valid_char(c)) {
            return Err("Invalid character in string.");
        }

        let (top_s, sub_s) = (&*top_s.to_lowercase(), &*sub_s.to_lowercase());
        let top_level = TopLevel::from_str(top_s).map_err(|_| "Bad TopLevel")?;
        let sub_level = SubLevel::from_str(sub_s).map_err(|_| "Bad SubLevel")?;

        // FIXME: Use `rest` to find params.
        Ok(ContentType::new(top_level, sub_level, None))
    }
}

impl fmt::Display for ContentType {
    /// Formats the ContentType as an HTTP Content-Type value.
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let http_ct = format!("{}", ContentType::xml());
    /// assert_eq!(http_ct, "application/xml".to_string());
    /// ```
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}/{}", self.0.as_str(), self.1.as_str())?;

        self.2.as_ref().map_or(Ok(()), |params| {
            for param in params.iter() {
                let (ref attr, ref value) = *param;
                write!(f, "; {}={}", attr, value)?;
            }

            Ok(())
        })
    }
}

impl Collider for ContentType {
    fn collides_with(&self, other: &ContentType) -> bool {
        self.0.collides_with(&other.0) && self.1.collides_with(&other.1)
    }
}

impl Collider for TopLevel {
    fn collides_with(&self, other: &TopLevel) -> bool {
        *self == TopLevel::Star || *other == TopLevel::Star || *self == *other
    }
}

impl Collider for SubLevel {
    fn collides_with(&self, other: &SubLevel) -> bool {
        *self == SubLevel::Star || *other == SubLevel::Star || *self == *other
    }
}

#[cfg(test)]
mod test {
    use super::ContentType;
    use hyper::mime::{TopLevel, SubLevel};
    use std::str::FromStr;

    macro_rules! assert_no_parse {
        ($string:expr) => ({
            let result = ContentType::from_str($string);
            if !result.is_err() {
                println!("{} parsed!", $string);
            }

            assert!(result.is_err());
        });
    }

    macro_rules! assert_parse {
        ($string:expr) => ({
            let result = ContentType::from_str($string);
            assert!(result.is_ok());
            result.unwrap()
        });
        ($string:expr, $top:tt/$sub:tt) => ({
            let c = assert_parse!($string);
            assert_eq!(c.0, TopLevel::$top);
            assert_eq!(c.1, SubLevel::$sub);
            c
        })
    }

    #[test]
    fn test_simple() {
        assert_parse!("application/json", Application/Json);
        assert_parse!("*/json", Star/Json);
        assert_parse!("text/html", Text/Html);
        assert_parse!("TEXT/html", Text/Html);
        assert_parse!("*/*", Star/Star);
        assert_parse!("application/*", Application/Star);
    }

    #[test]
    fn test_params() {
        assert_parse!("application/json; charset=utf8", Application/Json);
        assert_parse!("application/*;charset=utf8;else=1", Application/Star);
        assert_parse!("*/*;charset=utf8;else=1", Star/Star);
    }

    #[test]
    fn test_bad_parses() {
        assert_no_parse!("application//json");
        assert_no_parse!("application///json");
        assert_no_parse!("/json");
        assert_no_parse!("text/");
        assert_no_parse!("text//");
        assert_no_parse!("/");
        assert_no_parse!("*/");
        assert_no_parse!("/*");
        assert_no_parse!("///");
    }
}
<commit_msg>Revert visibility change in ContentType constructor.<commit_after>pub use response::mime::{Mime, TopLevel, SubLevel};
use response::mime::Param;
use std::default::Default;

use std::str::FromStr;
use std::borrow::Borrow;
use std::fmt;

use router::Collider;

/// Rocket's representation of HTTP Content-Types.
///
/// This type wraps raw HTTP `Content-Type`s in a type-safe manner. It provides
/// methods to create and test against common HTTP content-types. It also
/// provides methods to parse HTTP Content-Type values
/// ([from_str](#method.from_str)) and to return the ContentType associated with
/// a file extension ([from_ext](#method.from_extension)).
#[derive(Debug, Clone, PartialEq)]
pub struct ContentType(pub TopLevel, pub SubLevel, pub Option<Vec<Param>>);

macro_rules! is_some {
    ($ct:ident, $name:ident: $top:ident/$sub:ident) => {
        /// Returns a new ContentType that matches the MIME for this method's
        /// name.
        pub fn $ct() -> ContentType {
            ContentType::of(TopLevel::$top, SubLevel::$sub)
        }

        is_some!($name: $top/$sub);
    };

    ($name:ident: $top:ident/$sub:ident) => {
        /// Returns true if `self` is the content type matching the method's
        /// name.
        pub fn $name(&self) -> bool {
            self.0 == TopLevel::$top && self.1 == SubLevel::$sub
        }
    };
}

impl ContentType {
    #[doc(hidden)]
    #[inline(always)]
    pub fn new(t: TopLevel, s: SubLevel, params: Option<Vec<Param>>) -> ContentType {
        ContentType(t, s, params)
    }

    /// Constructs a new content type of the given top level and sub level
    /// types.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use rocket::response::mime::{TopLevel, SubLevel};
    ///
    /// let html = ContentType::of(TopLevel::Application, SubLevel::Html);
    /// assert!(html.is_html());
    /// ```
    #[inline(always)]
    pub fn of(t: TopLevel, s: SubLevel) -> ContentType {
        ContentType(t, s, None)
    }

    /// Returns a new ContentType for `*/*`, i.e., any.
    #[inline(always)]
    pub fn any() -> ContentType {
        ContentType::of(TopLevel::Star, SubLevel::Star)
    }

    /// Returns true if this content type is not one of the standard content
    /// types, that if, if it is an "extended" content type.
    pub fn is_ext(&self) -> bool {
        if let TopLevel::Ext(_) = self.0 {
            true
        } else if let SubLevel::Ext(_) = self.1 {
            true
        } else {
            false
        }
    }

    /// Returns true if the content type is JSON, i.e: `application/json`.
    is_some!(json, is_json: Application/Json);

    /// Returns true if the content type is XML, i.e: `application/xml`.
    is_some!(xml, is_xml: Application/Xml);

    /// Returns true if the content type is any, i.e.: `*/*`.
    is_some!(is_any: Star/Star);

    /// Returns true if the content type is HTML, i.e.: `application/html`.
    is_some!(html, is_html: Application/Html);

    /// Returns true if the content type is that for non-data HTTP forms, i.e.:
    /// `application/x-www-form-urlencoded`.
    is_some!(is_form: Application/WwwFormUrlEncoded);

    /// Returns true if the content type is that for data HTTP forms, i.e.:
    /// `multipart/form-data`.
    is_some!(is_data: Multipart/FormData);

    /// Returns the Content-Type associated with the extension `ext`. Not all
    /// extensions are recognized. If an extensions is not recognized, then this
    /// method returns a ContentType of `any`.
    ///
    /// # Example
    ///
    /// A recognized content type:
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let xml = ContentType::from_extension("xml");
    /// assert!(xml.is_xml());
    /// ```
    ///
    /// An unrecognized content type:
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let foo = ContentType::from_extension("foo");
    /// assert!(foo.is_any());
    /// ```
    pub fn from_extension(ext: &str) -> ContentType {
        let (top_level, sub_level) = match ext {
            "txt" => (TopLevel::Text, SubLevel::Plain),
            "html" => (TopLevel::Text, SubLevel::Html),
            "xml" => (TopLevel::Application, SubLevel::Xml),
            "js" => (TopLevel::Application, SubLevel::Javascript),
            "css" => (TopLevel::Text, SubLevel::Css),
            "json" => (TopLevel::Application, SubLevel::Json),
            "png" => (TopLevel::Image, SubLevel::Png),
            "gif" => (TopLevel::Image, SubLevel::Gif),
            "bmp" => (TopLevel::Image, SubLevel::Bmp),
            "jpeg" => (TopLevel::Image, SubLevel::Jpeg),
            "jpg" => (TopLevel::Image, SubLevel::Jpeg),
            "pdf" => (TopLevel::Application, SubLevel::Ext("pdf".into())),
            _ => (TopLevel::Star, SubLevel::Star),
        };

        ContentType::of(top_level, sub_level)
    }
}

impl Default for ContentType {
    /// Returns a ContentType of `any`, or `*/*`.
    #[inline(always)]
    fn default() -> ContentType {
        ContentType::any()
    }
}

#[doc(hidden)]
impl Into<Mime> for ContentType {
    fn into(self) -> Mime {
        Mime(self.0, self.1, self.2.unwrap_or_default())
    }
}

#[doc(hidden)]
impl<T: Borrow<Mime>> From<T> for ContentType {
    default fn from(mime: T) -> ContentType {
        let mime: Mime = mime.borrow().clone();
        ContentType::from(mime)
    }
}

#[doc(hidden)]
impl From<Mime> for ContentType {
    fn from(mime: Mime) -> ContentType {
        let params = match mime.2.len() {
            0 => None,
            _ => Some(mime.2),
        };

        ContentType(mime.0, mime.1, params)
    }
}

fn is_valid_first_char(c: char) -> bool {
    match c {
        'a'...'z' | 'A'...'Z' | '0'...'9' | '*' => true,
        _ => false,
    }
}

fn is_valid_char(c: char) -> bool {
    is_valid_first_char(c) || match c {
        '!' | '#' | '$' | '&' | '-' | '^' | '.' | '+' | '_' => true,
        _ => false,
    }
}

impl FromStr for ContentType {
    type Err = &'static str;

    /// Parses a ContentType from a given Content-Type header value.
    ///
    /// # Examples
    ///
    /// Parsing an `application/json`:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    ///
    /// let json = ContentType::from_str("application/json");
    /// assert_eq!(json, Ok(ContentType::json()));
    /// ```
    ///
    /// Parsing a content-type extension:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    /// use rocket::response::mime::{TopLevel, SubLevel};
    ///
    /// let custom = ContentType::from_str("application/x-custom").unwrap();
    /// assert!(custom.is_ext());
    /// assert_eq!(custom.0, TopLevel::Application);
    /// assert_eq!(custom.1, SubLevel::Ext("x-custom".into()));
    /// ```
    ///
    /// Parsing an invalid Content-Type value:
    ///
    /// ```rust
    /// use rocket::ContentType;
    /// use std::str::FromStr;
    ///
    /// let custom = ContentType::from_str("application//x-custom");
    /// assert!(custom.is_err());
    /// ```
    fn from_str(raw: &str) -> Result<ContentType, &'static str> {
        let slash = match raw.find('/') {
            Some(i) => i,
            None => return Err("Missing / in MIME type."),
        };

        let top_s = &raw[..slash];
        let (sub_s, _rest) = match raw.find(';') {
            Some(j) => (&raw[(slash + 1)..j], Some(&raw[(j + 1)..])),
            None => (&raw[(slash + 1)..], None),
        };

        if top_s.len() < 1 || sub_s.len() < 1 {
            return Err("Empty string.");
        }

        if !is_valid_first_char(top_s.chars().next().unwrap())
                || !is_valid_first_char(sub_s.chars().next().unwrap()) {
            return Err("Invalid first char.");
        }

        if top_s.contains(|c| !is_valid_char(c))
                || sub_s.contains(|c| !is_valid_char(c)) {
            return Err("Invalid character in string.");
        }

        let (top_s, sub_s) = (&*top_s.to_lowercase(), &*sub_s.to_lowercase());
        let top_level = TopLevel::from_str(top_s).map_err(|_| "Bad TopLevel")?;
        let sub_level = SubLevel::from_str(sub_s).map_err(|_| "Bad SubLevel")?;

        // FIXME: Use `rest` to find params.
        Ok(ContentType::new(top_level, sub_level, None))
    }
}

impl fmt::Display for ContentType {
    /// Formats the ContentType as an HTTP Content-Type value.
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::ContentType;
    ///
    /// let http_ct = format!("{}", ContentType::xml());
    /// assert_eq!(http_ct, "application/xml".to_string());
    /// ```
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}/{}", self.0.as_str(), self.1.as_str())?;

        self.2.as_ref().map_or(Ok(()), |params| {
            for param in params.iter() {
                let (ref attr, ref value) = *param;
                write!(f, "; {}={}", attr, value)?;
            }

            Ok(())
        })
    }
}

impl Collider for ContentType {
    fn collides_with(&self, other: &ContentType) -> bool {
        self.0.collides_with(&other.0) && self.1.collides_with(&other.1)
    }
}

impl Collider for TopLevel {
    fn collides_with(&self, other: &TopLevel) -> bool {
        *self == TopLevel::Star || *other == TopLevel::Star || *self == *other
    }
}

impl Collider for SubLevel {
    fn collides_with(&self, other: &SubLevel) -> bool {
        *self == SubLevel::Star || *other == SubLevel::Star || *self == *other
    }
}

#[cfg(test)]
mod test {
    use super::ContentType;
    use hyper::mime::{TopLevel, SubLevel};
    use std::str::FromStr;

    macro_rules! assert_no_parse {
        ($string:expr) => ({
            let result = ContentType::from_str($string);
            if !result.is_err() {
                println!("{} parsed!", $string);
            }

            assert!(result.is_err());
        });
    }

    macro_rules! assert_parse {
        ($string:expr) => ({
            let result = ContentType::from_str($string);
            assert!(result.is_ok());
            result.unwrap()
        });
        ($string:expr, $top:tt/$sub:tt) => ({
            let c = assert_parse!($string);
            assert_eq!(c.0, TopLevel::$top);
            assert_eq!(c.1, SubLevel::$sub);
            c
        })
    }

    #[test]
    fn test_simple() {
        assert_parse!("application/json", Application/Json);
        assert_parse!("*/json", Star/Json);
        assert_parse!("text/html", Text/Html);
        assert_parse!("TEXT/html", Text/Html);
        assert_parse!("*/*", Star/Star);
        assert_parse!("application/*", Application/Star);
    }

    #[test]
    fn test_params() {
        assert_parse!("application/json; charset=utf8", Application/Json);
        assert_parse!("application/*;charset=utf8;else=1", Application/Star);
        assert_parse!("*/*;charset=utf8;else=1", Star/Star);
    }

    #[test]
    fn test_bad_parses() {
        assert_no_parse!("application//json");
        assert_no_parse!("application///json");
        assert_no_parse!("/json");
        assert_no_parse!("text/");
        assert_no_parse!("text//");
        assert_no_parse!("/");
        assert_no_parse!("*/");
        assert_no_parse!("/*");
        assert_no_parse!("///");
    }
}
<|endoftext|>