<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Intterupt handling on x86 machines.
//!
//! This module provides support for interrupt handling on both `x86` and
//! `x86_64` as a black box. Code that depends on this can use the same API
//! regardless of system word size.
use core::fmt;
use core::fmt::Write;

use arch::cpu::{control_regs, Registers};
use arch::cpu::dtable::DTable;

use io::term::CONSOLE;
use io::keyboard;

use vga::Color;

use spin::Mutex;

pub mod idt;
pub mod pics;

use self::idt::Idt;

/// State stored when handling an interrupt.
#[repr(C, packed)]
pub struct InterruptContext { /// callee-saved registers
                              pub registers: Registers
                            , /// interrupt ID number
                              pub int_id: usize
                            //, _pad_1: u32
                            //, /// error number
                            //  pub err_no:  u32
                            //, _pad_2: u32
                            }

//impl InterruptContext {
//    /// Fetches the corresponding CPU exception for this interrupt, if this
//    /// interrupt is a CPU exception.
//    #[inline]
//
//}

#[derive(Debug)]
pub struct ExceptionInfo { pub name: &'static str
                         , pub mnemonic: &'static str
                         , pub irq_type: &'static str
                         , pub source: &'static str
                         }

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: [ExceptionInfo; 20]
    = [ ExceptionInfo { name: "Divide-By-Zero Error"
                      , mnemonic: "#DE", irq_type: "Fault"
                      , source: "DIV or IDIV instruction" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "#DB", irq_type: "Fault/trap"
                      , source: "Reserved for Intel use" }
      , ExceptionInfo { name: "Non-Maskable Interrupt"
                      , mnemonic: "NMI", irq_type: "Interrupt"
                      , source: "Non-maskable external interrupt" }
      , ExceptionInfo { name: "Breakpoint"
                      , mnemonic: "#BP", irq_type: "Trap"
                      , source: "INT 3 instruction" }
      , ExceptionInfo { name: "Overflow"
                      , mnemonic: "#OF", irq_type: "Trap"
                      , source: "INTO instruction" }
      , ExceptionInfo { name: "BOUND Range Exceeded"
                      , mnemonic: "#BR", irq_type: "Fault"
                      , source: "BOUND instruction" }
      , ExceptionInfo { name: "Undefined Opcode"
                     , mnemonic: "#UD", irq_type: "Fault"
                     , source: "UD2 instruction or reserved opcode" }
      , ExceptionInfo { name: "Device Not Available"
                      , mnemonic: "#NM", irq_type: "Fault"
                      , source: "Floating-point or WAIT/FWAIT instruction\
                                 (no math coprocessor)" }
      , ExceptionInfo { name: "Double Fault"
                      , mnemonic: "#DF", irq_type: "Abort"
                      , source: "Any instruction that can generate an\
                                 exception, a NMI, or an INTR" }
      , ExceptionInfo { name: "Coprocessor Segment Overrun"
                      , mnemonic: "", irq_type: "Fault"
                      , source: "Any floating-point instruction" }
      , ExceptionInfo { name: "Invalid TSS"
                      , mnemonic: "#TS", irq_type: "Fault"
                      , source: "Task switch or TSS access" }
      , ExceptionInfo { name: "Segment Not Present"
                      , mnemonic: "#NP", irq_type: "Fault"
                      , source: "Loading segment registers or accessing\
                                 system segments" }
      , ExceptionInfo { name: "Stack-Segment Fault"
                      , mnemonic: "#SS", irq_type: "Fault"
                      , source: "Stack operations and SS register loads" }
      , ExceptionInfo { name: "General Protection"
                      , mnemonic: "#GP", irq_type: "Fault"
                      , source: "Any memory reference or other protection\
                                 checks" }
      , ExceptionInfo { name: "Page Fault"
                      , mnemonic: "#PF", irq_type: "Fault"
                      , source: "Any memory reference" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "", irq_type: ""
                      , source: "RESERVED FOR INTEL USE \n This should never \
                                 happen. Something is very wrong." }
      , ExceptionInfo { name: "x87 FPU Floating-Point Error (Math Fault)"
                      , mnemonic: "#MF", irq_type: "Fault"
                      , source: "x87 FPU floating-point or WAIT/FWAIT\
                                 instruction" }
      , ExceptionInfo { name: "Alignment Check"
                      , mnemonic: "#AC", irq_type: "Fault"
                      , source: "Any data reference in memory" }
      , ExceptionInfo { name: "Machine Check"
                      , mnemonic: "#MC", irq_type: "Abort"
                      , source: "Model-dependent" }
      , ExceptionInfo { name: "SIMD Floating-Point Exception"
                      , mnemonic: "#XM", irq_type: "Fault"
                      , source: "SSE/SSE2/SSE3 floating-point instructions" }
       ];

//==--------------------------------------------------------------------------==
// Top-level interrupt handling
/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt> = Mutex::new(Idt::new());

macro_rules! isr {
    (exception $ex:expr, $name:ident, handler: $handler:expr) => {
        #[inline(never)] #[naked] #[no_mangle]
        pub unsafe extern fn $name() {
            asm!(  "push 0
                    push $0"
                :: "i"($ex)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            Registers::pop();
            asm!( "add rsp, 16
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (error $ex:expr, $name:ident, handler: $handler:expr) => {
        #[inline(never)] #[naked] #[no_mangle]
        pub unsafe extern fn $name() {
            asm!(  "push $0"
                :: "i"($ex)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            $crate::arch::cpu::Registers::pop();
            asm!( "add rsp, 16
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (interrupt $id:expr, $name:ident, handler: $handler:expr) => {
        #[naked] #[no_mangle]
        pub unsafe extern fn $name() {
            asm!(  "push $0"
                :: "i"($id)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            $crate::arch::cpu::Registers::pop();
            asm!( "add rsp, 8
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (interrupt $id:expr, $name:ident) => {
        isr! { interrupt $id, $name, handler: "handle_interrupt" }
    };
    (error $id:expr, $name:ident) => {
        isr! { error $id, $name, handler: "handle_cpu_exception" }
    };
    (exception $id:expr, $name:ident) => {
        isr! { exception $id, $name, handler: "handle_cpu_exception" }
    };
}

/// Kernel interrupt-handling function.
///
/// Assembly ISRs call into this, and it dispatches interrupts to
/// the appropriate consumers.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_interrupt(state: &InterruptContext) {
    let id = state.int_id;
    match id {
        // System timer
        0x20 => { /* TODO: make this work
                    TODO: should this IRQ get its own handler?
                  */ }
        // Keyboard
      , 0x21 => {
          // TODO: dispatch keypress event to subscribers (NYI)
            // TODO: should this interrupt get its own handler for perf?
            if let Some(input) = keyboard::read_char() {
                if input == '\r' {
                    println!("");
                } else {
                    print!("{}", input);
                }
            }
        }
        // Loonix syscall vector
      , 0x80 => { // TODO: currently, we do nothing here, do we want
                  // our syscalls on this vector as well?
        }
      , _ => panic!("Unknown interrupt: #{} Sorry!", id)
    }
    // send the PICs the end interrupt signal
    unsafe { pics::end_pic_interrupt(id as u8) };
}

#[no_mangle] #[inline(never)]
pub extern "C" fn keyboard_handler(state: &InterruptContext) {
    if let Some(input) = keyboard::read_char() {
        if input == '\r' {
            println!("");
        } else {
            print!("{}", input);
        }
    }
    // send the PICs the end interrupt signal
    unsafe { pics::end_pic_interrupt(state.int_id as u8) };
}

/// Handle a CPU exception with a given interrupt context.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_cpu_exception( state: &InterruptContext
                                      , err_code: usize) -> ! {
    let id = state.int_id;
    let ex_info = &EXCEPTIONS[id];
    let cr_state = control_regs::dump();
    let _ = write!( CONSOLE.lock()
                          .set_colors(Color::White, Color::Blue)
                        //   .clear()
                  , "CPU EXCEPTION {}: {}\n\
                     {} on vector {} with error code {:#x}\n\
                     Source: {}.\nThis is fine.\n\n"
                  , ex_info.mnemonic, ex_info.name, ex_info.irq_type
                  , id, err_code
                  , ex_info.source );

    // TODO: parse error codes
    let _ = match id {
        // TODO: page fault needs its own handler
        14 => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "{}"
                    , PageFaultErrorCode::from_bits_truncate(err_code as u32)
                    )
       , _ => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "Registers:\n{:?}\n    {}\n"
                    , state.registers
                    , cr_state
                    )
    };
    // TODO: stack dumps please

    loop { }
}

isr! { interrupt 0x21
     , keyboard_isr
     , handler: "keyboard_handler" }

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {
   // println!(" . Enabling interrupts:" );
   // println!( " . . Initialising PICs {:>40}"
   //         , pics::initialize().unwrap_or("[ FAIL ]") );
   pics::initialize();
   // TODO: consider loading double-fault handler before anything else in case
   //       a double fault occurs during init?
   IDT.lock()
      .add_handlers()
      .add_gate(0x21, keyboard_isr)
      .load();                 // Load the IDT pointer
   // print!("Testing interrupt handling...");
   // asm!("int $0" :: "N" (0x80));
   // println!("   [DONE]");
   Idt::enable_interrupts(); // enable interrupts

}

bitflags! {
    flags PageFaultErrorCode: u32 {
        /// If 1, the error was caused by a page that was present.
        /// Otherwise, the page was non-present.
        const PRESENT = 1 << 0
      , /// If 1, the error was caused by a read. If 0, the cause was a write.
        const READ_WRITE = 1 << 1
      , /// If 1, the error was caused during user-mode execution.
        /// If 0, the processor was in kernel mode.
        const USER_MODE = 1 << 2
      , /// If 1, the fault was caused by reserved bits set to 1 during a fetch.
        const RESERVED = 1 << 3
      , /// If 1, the fault was caused during an instruction fetch.
        const INST_FETCH = 1 << 4
      , /// If 1, there was a protection key violation.
        const PROTECTION = 1 << 5
    }
}

impl fmt::Display for PageFaultErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "Caused by {}{}{} during a {}{} executing in {} mode."
              , if self.contains(PRESENT) { "a present page" }
                else { "a non-present page" }
              , if self.contains(PROTECTION) { " protection-key violation" }
                else { "" }
              , if self.contains(RESERVED) { " reserved bits set to one "}
                else { "" }
              , if self.contains(READ_WRITE) { "read" } else { "write" }
              , if self.contains(INST_FETCH) { " in an instruction fetch"}
                else { "" }
              , if self.contains(USER_MODE) { "user" } else { "kernel" }            )
    }
}
<commit_msg>[x86_all] make ISRs unsafe extern "C"<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Intterupt handling on x86 machines.
//!
//! This module provides support for interrupt handling on both `x86` and
//! `x86_64` as a black box. Code that depends on this can use the same API
//! regardless of system word size.
use core::fmt;
use core::fmt::Write;

use arch::cpu::{control_regs, Registers};
use arch::cpu::dtable::DTable;

use io::term::CONSOLE;
use io::keyboard;

use vga::Color;

use spin::Mutex;

pub mod idt;
pub mod pics;

use self::idt::Idt;

/// State stored when handling an interrupt.
#[repr(C, packed)]
pub struct InterruptContext { /// callee-saved registers
                              pub registers: Registers
                            , /// interrupt ID number
                              pub int_id: usize
                            //, _pad_1: u32
                            //, /// error number
                            //  pub err_no:  u32
                            //, _pad_2: u32
                            }

//impl InterruptContext {
//    /// Fetches the corresponding CPU exception for this interrupt, if this
//    /// interrupt is a CPU exception.
//    #[inline]
//
//}

#[derive(Debug)]
pub struct ExceptionInfo { pub name: &'static str
                         , pub mnemonic: &'static str
                         , pub irq_type: &'static str
                         , pub source: &'static str
                         }

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: [ExceptionInfo; 20]
    = [ ExceptionInfo { name: "Divide-By-Zero Error"
                      , mnemonic: "#DE", irq_type: "Fault"
                      , source: "DIV or IDIV instruction" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "#DB", irq_type: "Fault/trap"
                      , source: "Reserved for Intel use" }
      , ExceptionInfo { name: "Non-Maskable Interrupt"
                      , mnemonic: "NMI", irq_type: "Interrupt"
                      , source: "Non-maskable external interrupt" }
      , ExceptionInfo { name: "Breakpoint"
                      , mnemonic: "#BP", irq_type: "Trap"
                      , source: "INT 3 instruction" }
      , ExceptionInfo { name: "Overflow"
                      , mnemonic: "#OF", irq_type: "Trap"
                      , source: "INTO instruction" }
      , ExceptionInfo { name: "BOUND Range Exceeded"
                      , mnemonic: "#BR", irq_type: "Fault"
                      , source: "BOUND instruction" }
      , ExceptionInfo { name: "Undefined Opcode"
                     , mnemonic: "#UD", irq_type: "Fault"
                     , source: "UD2 instruction or reserved opcode" }
      , ExceptionInfo { name: "Device Not Available"
                      , mnemonic: "#NM", irq_type: "Fault"
                      , source: "Floating-point or WAIT/FWAIT instruction\
                                 (no math coprocessor)" }
      , ExceptionInfo { name: "Double Fault"
                      , mnemonic: "#DF", irq_type: "Abort"
                      , source: "Any instruction that can generate an\
                                 exception, a NMI, or an INTR" }
      , ExceptionInfo { name: "Coprocessor Segment Overrun"
                      , mnemonic: "", irq_type: "Fault"
                      , source: "Any floating-point instruction" }
      , ExceptionInfo { name: "Invalid TSS"
                      , mnemonic: "#TS", irq_type: "Fault"
                      , source: "Task switch or TSS access" }
      , ExceptionInfo { name: "Segment Not Present"
                      , mnemonic: "#NP", irq_type: "Fault"
                      , source: "Loading segment registers or accessing\
                                 system segments" }
      , ExceptionInfo { name: "Stack-Segment Fault"
                      , mnemonic: "#SS", irq_type: "Fault"
                      , source: "Stack operations and SS register loads" }
      , ExceptionInfo { name: "General Protection"
                      , mnemonic: "#GP", irq_type: "Fault"
                      , source: "Any memory reference or other protection\
                                 checks" }
      , ExceptionInfo { name: "Page Fault"
                      , mnemonic: "#PF", irq_type: "Fault"
                      , source: "Any memory reference" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "", irq_type: ""
                      , source: "RESERVED FOR INTEL USE \n This should never \
                                 happen. Something is very wrong." }
      , ExceptionInfo { name: "x87 FPU Floating-Point Error (Math Fault)"
                      , mnemonic: "#MF", irq_type: "Fault"
                      , source: "x87 FPU floating-point or WAIT/FWAIT\
                                 instruction" }
      , ExceptionInfo { name: "Alignment Check"
                      , mnemonic: "#AC", irq_type: "Fault"
                      , source: "Any data reference in memory" }
      , ExceptionInfo { name: "Machine Check"
                      , mnemonic: "#MC", irq_type: "Abort"
                      , source: "Model-dependent" }
      , ExceptionInfo { name: "SIMD Floating-Point Exception"
                      , mnemonic: "#XM", irq_type: "Fault"
                      , source: "SSE/SSE2/SSE3 floating-point instructions" }
       ];

//==--------------------------------------------------------------------------==
// Top-level interrupt handling
/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt> = Mutex::new(Idt::new());

macro_rules! isr {
    (exception $ex:expr, $name:ident, handler: $handler:expr) => {
        #[inline(never)] #[naked] #[no_mangle]
        pub unsafe extern "C" fn $name() {
            asm!(  "push 0
                    push $0"
                :: "i"($ex)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            Registers::pop();
            asm!( "add rsp, 16
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (error $ex:expr, $name:ident, handler: $handler:expr) => {
        #[inline(never)] #[naked] #[no_mangle]
        pub unsafe extern "C" fn $name() {
            asm!(  "push $0"
                :: "i"($ex)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            $crate::arch::cpu::Registers::pop();
            asm!( "add rsp, 16
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (interrupt $id:expr, $name:ident, handler: $handler:expr) => {
        #[naked] #[no_mangle]
        pub unsafe extern "C" fn $name() {
            asm!(  "push $0"
                :: "i"($id)
                :: "volatile", "intel" );
            $crate::arch::cpu::Registers::push();
            asm!( concat!(
                    "mov rdi, rsp
                     call ", $handler)
                :: :: "volatile", "intel");
            $crate::arch::cpu::Registers::pop();
            asm!( "add rsp, 8
                   iretq"
                 :::: "volatile", "intel");
            unreachable!();
        }
    };
    (interrupt $id:expr, $name:ident) => {
        isr! { interrupt $id, $name, handler: "handle_interrupt" }
    };
    (error $id:expr, $name:ident) => {
        isr! { error $id, $name, handler: "handle_cpu_exception" }
    };
    (exception $id:expr, $name:ident) => {
        isr! { exception $id, $name, handler: "handle_cpu_exception" }
    };
}

/// Kernel interrupt-handling function.
///
/// Assembly ISRs call into this, and it dispatches interrupts to
/// the appropriate consumers.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_interrupt(state: &InterruptContext) {
    let id = state.int_id;
    match id {
        // System timer
        0x20 => { /* TODO: make this work
                    TODO: should this IRQ get its own handler?
                  */ }
        // Keyboard
      , 0x21 => {
          // TODO: dispatch keypress event to subscribers (NYI)
            // TODO: should this interrupt get its own handler for perf?
            if let Some(input) = keyboard::read_char() {
                if input == '\r' {
                    println!("");
                } else {
                    print!("{}", input);
                }
            }
        }
        // Loonix syscall vector
      , 0x80 => { // TODO: currently, we do nothing here, do we want
                  // our syscalls on this vector as well?
        }
      , _ => panic!("Unknown interrupt: #{} Sorry!", id)
    }
    // send the PICs the end interrupt signal
    unsafe { pics::end_pic_interrupt(id as u8) };
}

#[no_mangle] #[inline(never)]
pub unsafe extern "C" fn keyboard_handler(state: &InterruptContext) {
    if let Some(input) = keyboard::read_char() {
        if input == '\r' {
            println!("");
        } else {
            print!("{}", input);
        }
    }
    // send the PICs the end interrupt signal
    pics::end_pic_interrupt(state.int_id as u8);
}

/// Handle a CPU exception with a given interrupt context.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_cpu_exception( state: &InterruptContext
                                      , err_code: usize) -> ! {
    let id = state.int_id;
    let ex_info = &EXCEPTIONS[id];
    let cr_state = control_regs::dump();
    let _ = write!( CONSOLE.lock()
                          .set_colors(Color::White, Color::Blue)
                        //   .clear()
                  , "CPU EXCEPTION {}: {}\n\
                     {} on vector {} with error code {:#x}\n\
                     Source: {}.\nThis is fine.\n\n"
                  , ex_info.mnemonic, ex_info.name, ex_info.irq_type
                  , id, err_code
                  , ex_info.source );

    // TODO: parse error codes
    let _ = match id {
        // TODO: page fault needs its own handler
        14 => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "{}"
                    , PageFaultErrorCode::from_bits_truncate(err_code as u32)
                    )
       , _ => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "Registers:\n{:?}\n    {}\n"
                    , state.registers
                    , cr_state
                    )
    };
    // TODO: stack dumps please

    loop { }
}

isr! { interrupt 0x21
     , keyboard_isr
     , handler: "keyboard_handler" }

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {
   // println!(" . Enabling interrupts:" );
   // println!( " . . Initialising PICs {:>40}"
   //         , pics::initialize().unwrap_or("[ FAIL ]") );
   pics::initialize();
   // TODO: consider loading double-fault handler before anything else in case
   //       a double fault occurs during init?
   IDT.lock()
      .add_handlers()
      .add_gate(0x21, keyboard_isr)
      .load();                 // Load the IDT pointer
   // print!("Testing interrupt handling...");
   // asm!("int $0" :: "N" (0x80));
   // println!("   [DONE]");
   Idt::enable_interrupts(); // enable interrupts

}

bitflags! {
    flags PageFaultErrorCode: u32 {
        /// If 1, the error was caused by a page that was present.
        /// Otherwise, the page was non-present.
        const PRESENT = 1 << 0
      , /// If 1, the error was caused by a read. If 0, the cause was a write.
        const READ_WRITE = 1 << 1
      , /// If 1, the error was caused during user-mode execution.
        /// If 0, the processor was in kernel mode.
        const USER_MODE = 1 << 2
      , /// If 1, the fault was caused by reserved bits set to 1 during a fetch.
        const RESERVED = 1 << 3
      , /// If 1, the fault was caused during an instruction fetch.
        const INST_FETCH = 1 << 4
      , /// If 1, there was a protection key violation.
        const PROTECTION = 1 << 5
    }
}

impl fmt::Display for PageFaultErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "Caused by {}{}{} during a {}{} executing in {} mode."
              , if self.contains(PRESENT) { "a present page" }
                else { "a non-present page" }
              , if self.contains(PROTECTION) { " protection-key violation" }
                else { "" }
              , if self.contains(RESERVED) { " reserved bits set to one "}
                else { "" }
              , if self.contains(READ_WRITE) { "read" } else { "write" }
              , if self.contains(INST_FETCH) { " in an instruction fetch"}
                else { "" }
              , if self.contains(USER_MODE) { "user" } else { "kernel" }            )
    }
}
<|endoftext|>


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

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::Retry;
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

    fn len(&self) -> usize {
        self.inner.borrow_mut().len()
    }
}

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item = (), Error = ()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        tobox(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item = (), Error = ()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let network_tx_cloned = network_tx.clone();
                    let outgoing_connections_cloned = outgoing_connections.clone();
                    let new_connection = || {
                        if outgoing_connections_cloned.len() >= outgoing_connections_limit {
                            warn!(
                                "Rejected outgoing connection with peer={}, \
                                 connections limit reached.",
                                peer
                            );
                            None
                        } else {
                            // Register outgoing channel.
                            let (conn_tx, conn_rx) = mpsc::channel(10);
                            outgoing_connections.insert(peer, &conn_tx);
                            // Enable retry feature for outgoing connection.
                            let timeout = network_config.tcp_connect_retry_timeout;
                            let max_tries = network_config.tcp_connect_max_retries as usize;
                            let strategy = FixedInterval::from_millis(timeout).map(jitter).take(
                                max_tries,
                            );
                            let handle_cloned = handle.clone();
                            let action = move || TcpStream::connect(&peer, &handle_cloned);
                            let connect_handle = Retry::spawn(handle.clone(), strategy, action)
                                .map_err(into_other)
                                // Configure socket
                                .and_then(move |sock| {
                                    sock.set_nodelay(network_config.tcp_nodelay)?;
                                    let duration =
                                        network_config.tcp_keep_alive.map(Duration::from_millis);
                                    sock.set_keepalive(duration)?;
                                    Ok(sock)
                                })
                                // Connect socket with the outgoing channel
                                .and_then(move |sock| {
                                    trace!("Established connection with peer={}", peer);

                                    let stream = sock.framed(MessagesCodec);
                                    let (sink, stream) = stream.split();

                                    let writer = conn_rx
                                        .map_err(|_| other_error("Can't send data into socket"))
                                        .forward(sink);
                                    let reader = stream.for_each(result_ok);

                                    reader
                                        .select2(writer)
                                        .map_err(|_| other_error("Socket error"))
                                        .and_then(|res| match res {
                                            Either::A((_, _reader)) => Ok("by reader"),
                                            Either::B((_, _writer)) => Ok("by writer"),
                                        })
                                })
                                .then(move |res| {
                                    trace!(
                                        "Connection with peer={} closed, reason={:?}",
                                        peer,
                                        res
                                    );
                                    outgoing_connections_cloned.clone().remove(&peer);
                                    network_tx_cloned
                                        .clone()
                                        .send(NetworkEvent::PeerDisconnected(peer))
                                        .map(drop)
                                })
                                .map_err(log_error);
                            handle.spawn(connect_handle);
                            Some(conn_tx)
                        }
                    };
                    let conn_tx = outgoing_connections.get(peer).or_else(new_connection);
                    if let Some(conn_tx) = conn_tx {
                        let send_handle = conn_tx.send(msg).map_err(log_error);
                        tobox(send_handle)
                    } else {
                        let event = NetworkEvent::PeerDisconnected(peer);
                        let fut = network_tx
                            .clone()
                            .send(event)
                            .map_err(log_error)
                            .into_future();
                        tobox(fut)
                    }
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    let event = NetworkEvent::PeerDisconnected(peer);
                    let fut = network_tx.clone().send(event).map(drop).map_err(log_error);
                    tobox(fut)
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    let fut = cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)
                        .into_future();
                    tobox(fut)
                }
            }
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    tobox(future::ok(()))
                } else {
                    trace!("Accepted incoming connection with peer={}", addr);
                    let stream = sock.framed(MessagesCodec);
                    let (_, stream) = stream.split();
                    let network_tx = network_tx.clone();
                    let connection_handler = stream
                        .into_future()
                        .map_err(|e| e.0)
                        .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                            Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                            Some(Ok(other)) => Err(other_error(&format!(
                                "First message is not Connect, got={:?}",
                                other
                            ))),
                            Some(Err(e)) => Err(into_other(e)),
                            None => Err(other_error("Incoming socket closed")),
                        })
                        .and_then(move |(connect, stream)| {
                            trace!("Received handshake message={:?}", connect);
                            let event = NetworkEvent::PeerConnected(addr, connect);
                            let stream = network_tx
                                .clone()
                                .send(event)
                                .map_err(into_other)
                                .and_then(move |_| Ok(stream))
                                .flatten_stream();

                            stream.for_each(move |raw| {
                                let event = NetworkEvent::MessageReceived(addr, raw);
                                network_tx.clone().send(event).map_err(into_other).map(drop)
                            })
                        })
                        .map(|_| {
                            // Ensure that holder lives until the stream ends.
                            let _holder = holder;
                        });
                    tobox(connection_handler)
                }
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(drop);
        let fut = server
            .join(requests_handle)
            .map(drop)
            .select(cancel_handler)
            .map_err(drop);

        tobox(fut)
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item = (), Error = F::Error>> {
    Box::new(f.map(drop))
}
<commit_msg>Add const with outgoing channel size<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::Retry;
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

const OUTGOING_CHANNEL_SIZE: usize = 10;

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

    fn len(&self) -> usize {
        self.inner.borrow_mut().len()
    }
}

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item = (), Error = ()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        tobox(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item = (), Error = ()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let network_tx_cloned = network_tx.clone();
                    let outgoing_connections_cloned = outgoing_connections.clone();
                    let new_connection = || {
                        if outgoing_connections_cloned.len() >= outgoing_connections_limit {
                            warn!(
                                "Rejected outgoing connection with peer={}, \
                                 connections limit reached.",
                                peer
                            );
                            None
                        } else {
                            // Register outgoing channel.
                            let (conn_tx, conn_rx) = mpsc::channel(OUTGOING_CHANNEL_SIZE);
                            outgoing_connections.insert(peer, &conn_tx);
                            // Enable retry feature for outgoing connection.
                            let timeout = network_config.tcp_connect_retry_timeout;
                            let max_tries = network_config.tcp_connect_max_retries as usize;
                            let strategy = FixedInterval::from_millis(timeout).map(jitter).take(
                                max_tries,
                            );
                            let handle_cloned = handle.clone();
                            let action = move || TcpStream::connect(&peer, &handle_cloned);
                            let connect_handle = Retry::spawn(handle.clone(), strategy, action)
                                .map_err(into_other)
                                // Configure socket
                                .and_then(move |sock| {
                                    sock.set_nodelay(network_config.tcp_nodelay)?;
                                    let duration =
                                        network_config.tcp_keep_alive.map(Duration::from_millis);
                                    sock.set_keepalive(duration)?;
                                    Ok(sock)
                                })
                                // Connect socket with the outgoing channel
                                .and_then(move |sock| {
                                    trace!("Established connection with peer={}", peer);

                                    let stream = sock.framed(MessagesCodec);
                                    let (sink, stream) = stream.split();

                                    let writer = conn_rx
                                        .map_err(|_| other_error("Can't send data into socket"))
                                        .forward(sink);
                                    let reader = stream.for_each(result_ok);

                                    reader
                                        .select2(writer)
                                        .map_err(|_| other_error("Socket error"))
                                        .and_then(|res| match res {
                                            Either::A((_, _reader)) => Ok("by reader"),
                                            Either::B((_, _writer)) => Ok("by writer"),
                                        })
                                })
                                .then(move |res| {
                                    trace!(
                                        "Connection with peer={} closed, reason={:?}",
                                        peer,
                                        res
                                    );
                                    outgoing_connections_cloned.clone().remove(&peer);
                                    network_tx_cloned
                                        .clone()
                                        .send(NetworkEvent::PeerDisconnected(peer))
                                        .map(drop)
                                })
                                .map_err(log_error);
                            handle.spawn(connect_handle);
                            Some(conn_tx)
                        }
                    };
                    let conn_tx = outgoing_connections.get(peer).or_else(new_connection);
                    if let Some(conn_tx) = conn_tx {
                        let send_handle = conn_tx.send(msg).map_err(log_error);
                        tobox(send_handle)
                    } else {
                        let event = NetworkEvent::PeerDisconnected(peer);
                        let fut = network_tx
                            .clone()
                            .send(event)
                            .map_err(log_error)
                            .into_future();
                        tobox(fut)
                    }
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    let event = NetworkEvent::PeerDisconnected(peer);
                    let fut = network_tx.clone().send(event).map(drop).map_err(log_error);
                    tobox(fut)
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    let fut = cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)
                        .into_future();
                    tobox(fut)
                }
            }
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    tobox(future::ok(()))
                } else {
                    trace!("Accepted incoming connection with peer={}", addr);
                    let stream = sock.framed(MessagesCodec);
                    let (_, stream) = stream.split();
                    let network_tx = network_tx.clone();
                    let connection_handler = stream
                        .into_future()
                        .map_err(|e| e.0)
                        .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                            Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                            Some(Ok(other)) => Err(other_error(&format!(
                                "First message is not Connect, got={:?}",
                                other
                            ))),
                            Some(Err(e)) => Err(into_other(e)),
                            None => Err(other_error("Incoming socket closed")),
                        })
                        .and_then(move |(connect, stream)| {
                            trace!("Received handshake message={:?}", connect);
                            let event = NetworkEvent::PeerConnected(addr, connect);
                            let stream = network_tx
                                .clone()
                                .send(event)
                                .map_err(into_other)
                                .and_then(move |_| Ok(stream))
                                .flatten_stream();

                            stream.for_each(move |raw| {
                                let event = NetworkEvent::MessageReceived(addr, raw);
                                network_tx.clone().send(event).map_err(into_other).map(drop)
                            })
                        })
                        .map(|_| {
                            // Ensure that holder lives until the stream ends.
                            let _holder = holder;
                        });
                    tobox(connection_handler)
                }
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(drop);
        let fut = server
            .join(requests_handle)
            .map(drop)
            .select(cancel_handler)
            .map_err(drop);

        tobox(fut)
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item = (), Error = F::Error>> {
    Box::new(f.map(drop))
}
<|endoftext|>


<commit_before>// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

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

fn read_file<P: AsRef<Path>>(p: P) -> String {
    let mut f = File::open(p).expect("read_file::open failed");
    let mut content = String::new();
    f.read_to_string(&mut content).expect("read_file::read_to_end failed");
    content
}

fn check_inner_imports(
    filename: &str,
    line_pos: usize,
    line: &str,
    imports: &[String],
    errors: &mut u32
) {
    if imports.len() > 1 {
        for pos in 0..imports.len() - 1 {
            if imports[pos] == "self" {
                continue
            }
            if imports[pos] > imports[pos + 1] {
                writeln!(&mut io::stderr(), "[{}:{}] In \"{}\": \"{}\" should be after \"{}\"",
                         filename,
                         line_pos,
                         line,
                         imports[pos],
                         imports[pos + 1]).unwrap();
                *errors += 1;
            }
        }
    }
}

fn check_import_sorting<P: AsRef<Path>>(
    p: P,
    errors: &mut u32
) {
    let r_p = p.as_ref();
    let s_path = r_p.to_str().unwrap();
    let file_content = read_file(r_p);
    let mut imports: Vec<(usize, Vec<String>)> = Vec::new();
    let mut current_import: Option<Vec<String>> = None;
    for (pos, line) in file_content.lines().enumerate() {
        if !line.starts_with("use ") && current_import.is_none() {
            continue
        }
        if current_import.is_some() {
            let line = line.trim();
            if line.ends_with("};") {
                if let Some(ref current_import) = current_import {
                    {
                        let last = imports.last().unwrap();
                        check_inner_imports(s_path,
                                            last.0,
                                            &last.1.join("::"),
                                            &current_import, errors);
                    }
                    let len = imports.len() - 1;
                    imports[len].1.push(format!("{{{}}};", current_import.join(", ")));
                }
                current_import = None;
            } else if let Some(ref mut current_import) = current_import {
                current_import.push(line.replace(",", "").trim().to_owned());
            }
        } else {
            let new_entry: Vec<String> = line.split("use ")
                                             .skip(1)
                                             .next()
                                             .unwrap()
                                             .split("::")
                                             .map(|s| s.to_owned())
                                             .collect();
            if let Some(last) = new_entry.last() {
                if last.ends_with("};") {
                    check_inner_imports(s_path,
                                        pos + 1,
                                        &new_entry.join("::"),
                                        &last.replace("{", "")
                                             .replace("}", "")
                                             .replace(";", "")
                                             .trim()
                                             .split(",")
                                             .map(|s| s.trim().to_owned())
                                             .collect::<Vec<String>>(), errors);
                } else {
                    if last.split(',').count() == 0 {
                        current_import = Some(Vec::new());
                    }
                }
            }
            imports.push((pos + 1, new_entry));
        }
    }
    if imports.len() > 1 {
        for pos in 0..imports.len() - 1 {
            let mut i = 0;
            while i < imports[pos].1.len() - 1 && i < imports[pos + 1].1.len() - 1 &&
                  imports[pos].1[i] == imports[pos + 1].1[i] {
                i += 1;
            }
            if i >= imports[pos].1.len() - 1 {
                continue
            }
            if i >= imports[pos + 1].1.len() - 1 || imports[pos].1[i] > imports[pos + 1].1[i] {
                writeln!(&mut io::stderr(), "[{}:{}] \"use {}\" should be after \"use {}\"",
                         s_path,
                         imports[pos].1.join("::"),
                         imports[pos].0,
                         imports[pos + 1].1.join("::")).unwrap();
                *errors += 1;
            }
        }
    }
}

fn read_dirs<P: AsRef<Path>>(
    dir: P,
    errors: &mut u32
) {
    for entry in read_dir(dir).expect("read_dir failed") {
        let entry = entry.expect("entry failed");
        let path = entry.path();
        if path.is_dir() {
            read_dirs(path, errors);
        } else {
            check_import_sorting(path, errors);
        }
    }
}

#[test]
fn check_imports_sorting() {
    let mut errors = 0;
    read_dirs("src", &mut errors);
    assert!(errors == 0, "Not sorted import(s) found");
}
<commit_msg>Fix import checkup (#413)<commit_after>// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.

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

fn read_file<P: AsRef<Path>>(p: P) -> String {
    let mut f = File::open(p).expect("read_file::open failed");
    let mut content = String::new();
    f.read_to_string(&mut content).expect("read_file::read_to_end failed");
    content
}

fn check_inner_imports(
    filename: &str,
    line_pos: usize,
    line: &str,
    imports: &[String],
    errors: &mut u32
) {
    if imports.len() > 1 {
        let mut issues = false;
        for pos in 0..imports.len() - 1 {
            if imports[pos] == "self" {
                continue
            }
            if imports[pos] > imports[pos + 1] {
                writeln!(&mut io::stderr(), "[{}:{}] In \"{}\": \"{}\" should be after \"{}\"",
                         filename,
                         line_pos,
                         line,
                         imports[pos],
                         imports[pos + 1]).unwrap();
                *errors += 1;
                issues = true;
            }
        }
        if issues == true {
            let mut sorted = imports.to_vec();
            sorted.sort();
            writeln!(&mut io::stderr(), "==> Correct imports: \"use {}{{{}}};\"",
                     line.split('{').next().unwrap(),
                     sorted.join(", ")).unwrap();
        }
    }
}

fn check_import_sorting<P: AsRef<Path>>(
    p: P,
    errors: &mut u32
) {
    let r_p = p.as_ref();
    let s_path = r_p.to_str().unwrap();
    let file_content = read_file(r_p);
    let mut imports: Vec<(usize, Vec<String>)> = Vec::new();
    let mut current_import: Option<Vec<String>> = None;
    for (pos, line) in file_content.lines().enumerate() {
        if !line.starts_with("use ") && current_import.is_none() {
            continue
        }
        if current_import.is_some() {
            let line = line.trim();
            if line.ends_with("};") {
                if let Some(ref current_import) = current_import {
                    {
                        let last = imports.last().unwrap();
                        check_inner_imports(s_path,
                                            last.0,
                                            &last.1.join("::"),
                                            &current_import, errors);
                    }
                    let len = imports.len() - 1;
                    imports[len].1.push(format!("{{{}}};", current_import.join(", ")));
                }
                current_import = None;
            } else if let Some(ref mut current_import) = current_import {
                current_import.push(line.replace(",", "").trim().to_owned());
            }
        } else {
            let new_entry: Vec<String> = line.split("use ")
                                             .skip(1)
                                             .next()
                                             .unwrap()
                                             .split("::")
                                             .map(|s| s.to_owned())
                                             .collect();
            if let Some(last) = new_entry.last() {
                if last.ends_with("};") {
                    check_inner_imports(s_path,
                                        pos + 1,
                                        &new_entry.join("::"),
                                        &last.replace("{", "")
                                             .replace("}", "")
                                             .replace(";", "")
                                             .trim()
                                             .split(",")
                                             .map(|s| s.trim().to_owned())
                                             .collect::<Vec<String>>(), errors);
                } else {
                    if last.split(',').count() == 0 {
                        current_import = Some(Vec::new());
                    }
                }
            }
            imports.push((pos + 1, new_entry));
        }
    }
    if imports.len() > 1 {
        for pos in 0..imports.len() - 1 {
            let mut i = 0;
            while i < imports[pos].1.len() - 1 && i < imports[pos + 1].1.len() - 1 &&
                  imports[pos].1[i] == imports[pos + 1].1[i] {
                i += 1;
            }
            if i >= imports[pos].1.len() - 1 {
                continue
            }
            if i >= imports[pos + 1].1.len() - 1 || imports[pos].1[i] > imports[pos + 1].1[i] {
                writeln!(&mut io::stderr(), "[{}:{}] \"use {}\" should be after \"use {}\"",
                         s_path,
                         imports[pos].0,
                         imports[pos].1.join("::"),
                         imports[pos + 1].1.join("::")).unwrap();
                *errors += 1;
            }
        }
    }
}

fn read_dirs<P: AsRef<Path>>(
    dir: P,
    errors: &mut u32
) {
    for entry in read_dir(dir).expect("read_dir failed") {
        let entry = entry.expect("entry failed");
        let path = entry.path();
        if path.is_dir() {
            read_dirs(path, errors);
        } else {
            check_import_sorting(path, errors);
        }
    }
}

#[test]
fn check_imports_sorting() {
    let mut errors = 0;
    read_dirs("src", &mut errors);
    check_import_sorting("build.rs", &mut errors);
    assert!(errors == 0, "Not sorted import(s) found");
}
<|endoftext|>


<commit_before>use aardwolf_models::sql_types::{Mime, PostVisibility};
use mime::TEXT_HTML;
use serde_derive::{Deserialize, Serialize};

use crate::{
    error::AardwolfFail,
    traits::Validate,
    wrapper::{ValidateWrapper, Wrapped},
};

#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(feature = "use-rocket", derive(FromForm))]
pub struct PostCreationForm {
    csrf_token: String,
    visibility: PostVisibility,
    #[serde(default)]
    name: Option<String>,
    source: String,
}

impl PostCreationForm {
    pub fn as_state(&self) -> PostCreationFormState {
        PostCreationFormState {
            csrf_token: self.csrf_token.clone(),
            visibility: self.visibility,
            name: self.name.clone(),
            source: self.source.clone(),
        }
    }
}

pub struct PostCreationFormState {
    pub csrf_token: String,
    pub visibility: PostVisibility,
    pub name: Option<String>,
    pub source: String,
}

#[derive(Clone, Debug, Fail, Serialize)]
#[fail(display = "Error validating post creation form")]
pub struct ValidatePostCreationFail {
    pub visibility: Option<String>,
    pub source: Option<String>,
    pub name: Option<String>,
}

impl ValidatePostCreationFail {
    pub fn is_empty(&self) -> bool {
        self.visibility.is_none() && self.source.is_none() && self.name.is_none()
    }
}

impl AardwolfFail for ValidatePostCreationFail {}

pub struct ValidatePostCreationForm(pub PostCreationForm);

impl Wrapped for ValidatePostCreationForm {
    type Wrapper = ValidateWrapper<Self, <Self as Validate>::Item, <Self as Validate>::Error>;
}

impl Validate for ValidatePostCreationForm {
    type Item = ValidatedPostCreationForm;
    type Error = ValidatePostCreationFail;

    fn validate(self) -> Result<Self::Item, Self::Error> {
        let mut err = ValidatePostCreationFail {
            visibility: None,
            source: None,
            name: None,
        };

        let source = self.0.source.trim().to_owned();
        let content = source.clone(); // TODO: translate things here
        let visibility = self.0.visibility;
        let media_type = TEXT_HTML.into();

        let name = if let Some(name) = self.0.name {
            if name.trim().is_empty() {
                None
            } else {
                Some(name)
            }
        } else {
            None
        };

        if source.is_empty() {
            err.source = Some("Post cannot be empty".to_owned());
        }

        if !err.is_empty() {
            return Err(err);
        }

        Ok(ValidatedPostCreationForm {
            media_type,
            visibility,
            content,
            source,
            name,
        })
    }
}

pub struct ValidatedPostCreationForm {
    pub(crate) media_type: Mime,
    pub(crate) visibility: PostVisibility,
    pub(crate) content: String,
    pub(crate) source: String,
    pub(crate) name: Option<String>,
}
<commit_msg>csrf token shouldn't be in state<commit_after>use aardwolf_models::sql_types::{Mime, PostVisibility};
use mime::TEXT_HTML;
use serde_derive::{Deserialize, Serialize};

use crate::{
    error::AardwolfFail,
    traits::Validate,
    wrapper::{ValidateWrapper, Wrapped},
};

#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(feature = "use-rocket", derive(FromForm))]
pub struct PostCreationForm {
    csrf_token: String,
    visibility: PostVisibility,
    #[serde(default)]
    name: Option<String>,
    source: String,
}

impl PostCreationForm {
    pub fn as_state(&self) -> PostCreationFormState {
        PostCreationFormState {
            visibility: self.visibility,
            name: self.name.clone(),
            source: self.source.clone(),
        }
    }
}

pub struct PostCreationFormState {
    pub visibility: PostVisibility,
    pub name: Option<String>,
    pub source: String,
}

#[derive(Clone, Debug, Fail, Serialize)]
#[fail(display = "Error validating post creation form")]
pub struct ValidatePostCreationFail {
    pub visibility: Option<String>,
    pub source: Option<String>,
    pub name: Option<String>,
}

impl ValidatePostCreationFail {
    pub fn is_empty(&self) -> bool {
        self.visibility.is_none() && self.source.is_none() && self.name.is_none()
    }
}

impl AardwolfFail for ValidatePostCreationFail {}

pub struct ValidatePostCreationForm(pub PostCreationForm);

impl Wrapped for ValidatePostCreationForm {
    type Wrapper = ValidateWrapper<Self, <Self as Validate>::Item, <Self as Validate>::Error>;
}

impl Validate for ValidatePostCreationForm {
    type Item = ValidatedPostCreationForm;
    type Error = ValidatePostCreationFail;

    fn validate(self) -> Result<Self::Item, Self::Error> {
        let mut err = ValidatePostCreationFail {
            visibility: None,
            source: None,
            name: None,
        };

        let source = self.0.source.trim().to_owned();
        let content = source.clone(); // TODO: translate things here
        let visibility = self.0.visibility;
        let media_type = TEXT_HTML.into();

        let name = if let Some(name) = self.0.name {
            if name.trim().is_empty() {
                None
            } else {
                Some(name)
            }
        } else {
            None
        };

        if source.is_empty() {
            err.source = Some("Post cannot be empty".to_owned());
        }

        if !err.is_empty() {
            return Err(err);
        }

        Ok(ValidatedPostCreationForm {
            media_type,
            visibility,
            content,
            source,
            name,
        })
    }
}

pub struct ValidatedPostCreationForm {
    pub(crate) media_type: Mime,
    pub(crate) visibility: PostVisibility,
    pub(crate) content: String,
    pub(crate) source: String,
    pub(crate) name: Option<String>,
}
<|endoftext|>


<commit_before>#![feature(test)]

#[macro_use]
extern crate maplit;
extern crate test;
extern crate abra;

use test::Bencher;

use abra::term::Term;
use abra::token::Token;
use abra::schema::FieldType;
use abra::document::Document;
use abra::store::{IndexStore, IndexReader};
use abra::store::memory::{MemoryIndexStore, MemoryIndexStoreReader};
use abra::query_set::QuerySetIterator;

fn make_test_store() -> MemoryIndexStore {
    let mut store = MemoryIndexStore::new();
    let body_field = store.add_field("body".to_string(), FieldType::Text).unwrap();

    for i in 0..10000 {
        let mut tokens = Vec::new();

        if i % 3 == 0 {
            let position = tokens.len() as u32 + 1;
            tokens.push(Token {
                term: Term::String("fizz".to_string()),
                position: position,
            });
        }

        if i % 5 == 0 {
            let position = tokens.len() as u32 + 1;
            tokens.push(Token {
                term: Term::String("buzz".to_string()),
                position: position,
            });
        }

        store.insert_or_update_document(Document {
            key: i.to_string(),
            fields: hashmap! {
                body_field => tokens
            }
        });
    }

    store
}

#[bench]
fn bench_all(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::All {
            iter: reader.iter_all_docs(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizz_term(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_buzz_term(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_conjunction(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Conjunction {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_disjunction(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Disjunction {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_exclusion(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Exclusion {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}
<commit_msg>Added missing import to benchmarks<commit_after>#![feature(test)]

#[macro_use]
extern crate maplit;
extern crate test;
extern crate abra;

use test::Bencher;

use abra::term::Term;
use abra::token::Token;
use abra::schema::{SchemaRead, FieldType};
use abra::document::Document;
use abra::store::{IndexStore, IndexReader};
use abra::store::memory::{MemoryIndexStore, MemoryIndexStoreReader};
use abra::query_set::QuerySetIterator;

fn make_test_store() -> MemoryIndexStore {
    let mut store = MemoryIndexStore::new();
    let body_field = store.add_field("body".to_string(), FieldType::Text).unwrap();

    for i in 0..10000 {
        let mut tokens = Vec::new();

        if i % 3 == 0 {
            let position = tokens.len() as u32 + 1;
            tokens.push(Token {
                term: Term::String("fizz".to_string()),
                position: position,
            });
        }

        if i % 5 == 0 {
            let position = tokens.len() as u32 + 1;
            tokens.push(Token {
                term: Term::String("buzz".to_string()),
                position: position,
            });
        }

        store.insert_or_update_document(Document {
            key: i.to_string(),
            fields: hashmap! {
                body_field => tokens
            }
        });
    }

    store
}

#[bench]
fn bench_all(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::All {
            iter: reader.iter_all_docs(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizz_term(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_buzz_term(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_conjunction(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Conjunction {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_disjunction(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Disjunction {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}

#[bench]
fn bench_fizzbuzz_exclusion(b: &mut Bencher) {
    let store = make_test_store();
    let reader = store.reader();
    let body_field = reader.schema().get_field_by_name("body").unwrap();

    let fizz_term = Term::String("fizz".to_string()).to_bytes();
    let buzz_term = Term::String("buzz".to_string()).to_bytes();

    b.iter(|| {
        let mut fizz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&fizz_term, &body_field).unwrap(),
        };
        let mut buzz_iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Term {
            iter: reader.iter_docs_with_term(&buzz_term, &body_field).unwrap(),
        };
        let mut iterator: QuerySetIterator<MemoryIndexStoreReader> = QuerySetIterator::Exclusion {
            iter_a: Box::new(fizz_iterator),
            iter_b: Box::new(buzz_iterator),
            initialised: false,
            current_doc_a: None,
            current_doc_b: None,
        };

        for doc_id in iterator {}
    });
}
<|endoftext|>


<commit_before>mod util;

use crate::util::*;
use semver::{BuildMetadata, Prerelease, Version};

#[test]
fn test_parse() {
    let err = version_err("");
    assert_to_string(
        err,
        "unexpected end of input while parsing major version number",
    );

    let err = version_err("  ");
    assert_to_string(
        err,
        "unexpected character ' ' while parsing major version number",
    );

    let err = version_err("1");
    assert_to_string(
        err,
        "unexpected end of input while parsing major version number",
    );

    let err = version_err("1.2");
    assert_to_string(
        err,
        "unexpected end of input while parsing minor version number",
    );

    let err = version_err("1.2.3-");
    assert_to_string(err, "empty identifier segment in pre-release identifier");

    let err = version_err("a.b.c");
    assert_to_string(
        err,
        "unexpected character 'a' while parsing major version number",
    );

    let err = version_err("1.2.3 abc");
    assert_to_string(err, "unexpected character ' ' after patch version number");

    let parsed = version("1.2.3");
    let expected = Version::new(1, 2, 3);
    assert_eq!(parsed, expected);
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-alpha1");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("alpha1"),
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3+build5");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: build_metadata("build5"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3+5build");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: build_metadata("5build"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-alpha1+build5");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("alpha1"),
        build: build_metadata("build5"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-1.alpha1.9+build5.7.3aedf");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("1.alpha1.9"),
        build: build_metadata("build5.7.3aedf"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-0a.alpha1.9+05build.7.3aedf");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("0a.alpha1.9"),
        build: build_metadata("05build.7.3aedf"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("0.4.0-beta.1+0851523");
    let expected = Version {
        major: 0,
        minor: 4,
        patch: 0,
        pre: prerelease("beta.1"),
        build: build_metadata("0851523"),
    };
    assert_eq!(parsed, expected);

    // for https://nodejs.org/dist/index.json, where some older npm versions are "1.1.0-beta-10"
    let parsed = version("1.1.0-beta-10");
    let expected = Version {
        major: 1,
        minor: 1,
        patch: 0,
        pre: prerelease("beta-10"),
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);
}

#[test]
fn test_eq() {
    assert_eq!(version("1.2.3"), version("1.2.3"));
    assert_eq!(version("1.2.3-alpha1"), version("1.2.3-alpha1"));
    assert_eq!(version("1.2.3+build.42"), version("1.2.3+build.42"));
    assert_eq!(version("1.2.3-alpha1+42"), version("1.2.3-alpha1+42"));
}

#[test]
fn test_ne() {
    assert_ne!(version("0.0.0"), version("0.0.1"));
    assert_ne!(version("0.0.0"), version("0.1.0"));
    assert_ne!(version("0.0.0"), version("1.0.0"));
    assert_ne!(version("1.2.3-alpha"), version("1.2.3-beta"));
    assert_ne!(version("1.2.3+23"), version("1.2.3+42"));
}

#[test]
fn test_display() {
    assert_to_string(version("1.2.3"), "1.2.3");
    assert_to_string(version("1.2.3-alpha1"), "1.2.3-alpha1");
    assert_to_string(version("1.2.3+build.42"), "1.2.3+build.42");
    assert_to_string(version("1.2.3-alpha1+42"), "1.2.3-alpha1+42");
}

#[test]
fn test_lt() {
    assert!(version("0.0.0") < version("1.2.3-alpha2"));
    assert!(version("1.0.0") < version("1.2.3-alpha2"));
    assert!(version("1.2.0") < version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha1") < version("1.2.3"));
    assert!(version("1.2.3-alpha1") < version("1.2.3-alpha2"));
    assert!(!(version("1.2.3-alpha2") < version("1.2.3-alpha2")));
    assert!(version("1.2.3+23") < version("1.2.3+42"));
}

#[test]
fn test_le() {
    assert!(version("0.0.0") <= version("1.2.3-alpha2"));
    assert!(version("1.0.0") <= version("1.2.3-alpha2"));
    assert!(version("1.2.0") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha1") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha2") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3+23") <= version("1.2.3+42"));
}

#[test]
fn test_gt() {
    assert!(version("1.2.3-alpha2") > version("0.0.0"));
    assert!(version("1.2.3-alpha2") > version("1.0.0"));
    assert!(version("1.2.3-alpha2") > version("1.2.0"));
    assert!(version("1.2.3-alpha2") > version("1.2.3-alpha1"));
    assert!(version("1.2.3") > version("1.2.3-alpha2"));
    assert!(!(version("1.2.3-alpha2") > version("1.2.3-alpha2")));
    assert!(!(version("1.2.3+23") > version("1.2.3+42")));
}

#[test]
fn test_ge() {
    assert!(version("1.2.3-alpha2") >= version("0.0.0"));
    assert!(version("1.2.3-alpha2") >= version("1.0.0"));
    assert!(version("1.2.3-alpha2") >= version("1.2.0"));
    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha1"));
    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha2"));
    assert!(!(version("1.2.3+23") >= version("1.2.3+42")));
}

#[test]
fn test_spec_order() {
    let vs = [
        "1.0.0-alpha",
        "1.0.0-alpha.1",
        "1.0.0-alpha.beta",
        "1.0.0-beta",
        "1.0.0-beta.2",
        "1.0.0-beta.11",
        "1.0.0-rc.1",
        "1.0.0",
    ];
    let mut i = 1;
    while i < vs.len() {
        let a = version(vs[i - 1]);
        let b = version(vs[i]);
        assert!(a < b, "nope {:?} < {:?}", a, b);
        i += 1;
    }
}
<commit_msg>Add a test for a new parse error<commit_after>mod util;

use crate::util::*;
use semver::{BuildMetadata, Prerelease, Version};

#[test]
fn test_parse() {
    let err = version_err("");
    assert_to_string(
        err,
        "unexpected end of input while parsing major version number",
    );

    let err = version_err("  ");
    assert_to_string(
        err,
        "unexpected character ' ' while parsing major version number",
    );

    let err = version_err("1");
    assert_to_string(
        err,
        "unexpected end of input while parsing major version number",
    );

    let err = version_err("1.2");
    assert_to_string(
        err,
        "unexpected end of input while parsing minor version number",
    );

    let err = version_err("1.2.3-");
    assert_to_string(err, "empty identifier segment in pre-release identifier");

    let err = version_err("a.b.c");
    assert_to_string(
        err,
        "unexpected character 'a' while parsing major version number",
    );

    let err = version_err("1.2.3 abc");
    assert_to_string(err, "unexpected character ' ' after patch version number");

    let err = version_err("1.2.3-01");
    assert_to_string(err, "invalid leading zero in pre-release identifier");

    let parsed = version("1.2.3");
    let expected = Version::new(1, 2, 3);
    assert_eq!(parsed, expected);
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-alpha1");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("alpha1"),
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3+build5");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: build_metadata("build5"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3+5build");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: Prerelease::EMPTY,
        build: build_metadata("5build"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-alpha1+build5");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("alpha1"),
        build: build_metadata("build5"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-1.alpha1.9+build5.7.3aedf");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("1.alpha1.9"),
        build: build_metadata("build5.7.3aedf"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("1.2.3-0a.alpha1.9+05build.7.3aedf");
    let expected = Version {
        major: 1,
        minor: 2,
        patch: 3,
        pre: prerelease("0a.alpha1.9"),
        build: build_metadata("05build.7.3aedf"),
    };
    assert_eq!(parsed, expected);

    let parsed = version("0.4.0-beta.1+0851523");
    let expected = Version {
        major: 0,
        minor: 4,
        patch: 0,
        pre: prerelease("beta.1"),
        build: build_metadata("0851523"),
    };
    assert_eq!(parsed, expected);

    // for https://nodejs.org/dist/index.json, where some older npm versions are "1.1.0-beta-10"
    let parsed = version("1.1.0-beta-10");
    let expected = Version {
        major: 1,
        minor: 1,
        patch: 0,
        pre: prerelease("beta-10"),
        build: BuildMetadata::EMPTY,
    };
    assert_eq!(parsed, expected);
}

#[test]
fn test_eq() {
    assert_eq!(version("1.2.3"), version("1.2.3"));
    assert_eq!(version("1.2.3-alpha1"), version("1.2.3-alpha1"));
    assert_eq!(version("1.2.3+build.42"), version("1.2.3+build.42"));
    assert_eq!(version("1.2.3-alpha1+42"), version("1.2.3-alpha1+42"));
}

#[test]
fn test_ne() {
    assert_ne!(version("0.0.0"), version("0.0.1"));
    assert_ne!(version("0.0.0"), version("0.1.0"));
    assert_ne!(version("0.0.0"), version("1.0.0"));
    assert_ne!(version("1.2.3-alpha"), version("1.2.3-beta"));
    assert_ne!(version("1.2.3+23"), version("1.2.3+42"));
}

#[test]
fn test_display() {
    assert_to_string(version("1.2.3"), "1.2.3");
    assert_to_string(version("1.2.3-alpha1"), "1.2.3-alpha1");
    assert_to_string(version("1.2.3+build.42"), "1.2.3+build.42");
    assert_to_string(version("1.2.3-alpha1+42"), "1.2.3-alpha1+42");
}

#[test]
fn test_lt() {
    assert!(version("0.0.0") < version("1.2.3-alpha2"));
    assert!(version("1.0.0") < version("1.2.3-alpha2"));
    assert!(version("1.2.0") < version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha1") < version("1.2.3"));
    assert!(version("1.2.3-alpha1") < version("1.2.3-alpha2"));
    assert!(!(version("1.2.3-alpha2") < version("1.2.3-alpha2")));
    assert!(version("1.2.3+23") < version("1.2.3+42"));
}

#[test]
fn test_le() {
    assert!(version("0.0.0") <= version("1.2.3-alpha2"));
    assert!(version("1.0.0") <= version("1.2.3-alpha2"));
    assert!(version("1.2.0") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha1") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3-alpha2") <= version("1.2.3-alpha2"));
    assert!(version("1.2.3+23") <= version("1.2.3+42"));
}

#[test]
fn test_gt() {
    assert!(version("1.2.3-alpha2") > version("0.0.0"));
    assert!(version("1.2.3-alpha2") > version("1.0.0"));
    assert!(version("1.2.3-alpha2") > version("1.2.0"));
    assert!(version("1.2.3-alpha2") > version("1.2.3-alpha1"));
    assert!(version("1.2.3") > version("1.2.3-alpha2"));
    assert!(!(version("1.2.3-alpha2") > version("1.2.3-alpha2")));
    assert!(!(version("1.2.3+23") > version("1.2.3+42")));
}

#[test]
fn test_ge() {
    assert!(version("1.2.3-alpha2") >= version("0.0.0"));
    assert!(version("1.2.3-alpha2") >= version("1.0.0"));
    assert!(version("1.2.3-alpha2") >= version("1.2.0"));
    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha1"));
    assert!(version("1.2.3-alpha2") >= version("1.2.3-alpha2"));
    assert!(!(version("1.2.3+23") >= version("1.2.3+42")));
}

#[test]
fn test_spec_order() {
    let vs = [
        "1.0.0-alpha",
        "1.0.0-alpha.1",
        "1.0.0-alpha.beta",
        "1.0.0-beta",
        "1.0.0-beta.2",
        "1.0.0-beta.11",
        "1.0.0-rc.1",
        "1.0.0",
    ];
    let mut i = 1;
    while i < vs.len() {
        let a = version(vs[i - 1]);
        let b = version(vs[i]);
        assert!(a < b, "nope {:?} < {:?}", a, b);
        i += 1;
    }
}
<|endoftext|>