<commit_before>#![feature(core_intrinsics, patchpoint_call_intrinsic, stackmap_call_intrinsic)]
#![feature(question_mark)]
#![feature(type_ascription)]
#![feature(rustc_attrs)]
#![feature(libc)]
#![feature(const_fn)]
#![allow(unused_features)]
extern crate libc;

use std::intrinsics;

#[allow(dead_code)]
mod byteorder;
#[allow(dead_code, unused_imports, unused_variables, unused_mut)]
mod elf;

mod backtrace_hack;
mod llvm_stackmaps;

fn subcall(data: *mut u8) {
    println!("Enter `subcall`");
    let mut i = 0;
    let mut saw_one = false;
    print!("[");
    let mut datum = unsafe { *data.offset(i) as char };
    while datum != '\0' {
        if saw_one {
            print!(", ");
        }
        print!("{}", datum);
        saw_one = true;
        i += 1;
        datum = unsafe { *data.offset(i) as char };
    }
    println!("]");
    println!("Finis `subcall`");
}

static mut MAIN_LOCAL: usize = 0;
pub fn main_height() -> usize { unsafe { MAIN_LOCAL } }

fn main() {
    let local: u32 = 0;
    unsafe { MAIN_LOCAL = &local as *const _ as usize; }
    demo().unwrap();
}

#[derive(Debug)]
enum DemoError {
    ParseError(elf::ParseError),
    MissingSection(String),
}

impl std::convert::From<elf::ParseError> for DemoError {
    fn from(x: elf::ParseError) -> Self { DemoError::ParseError(x) }
}

#[rustc_mir(borrowck_graphviz_postflow="/tmp/foo.dot")]
fn demo() -> Result<(), DemoError> {
    use std::path::Path;
    use libc::{c_char, c_int, c_void};
    use self::llvm_stackmaps::StackMap;
    use self::byteorder::LittleEndian;

    // FIXME: this is not a reliable way to extract the executable file path.
    // (look into alternatives)
    let binary = std::env::args().next().unwrap();

    println!("Hello World from {}", binary);

    let path = Path::new(&binary);
    let file = elf::File::open_path(&path)?;

    let stackmap_section = {
        let name = ".llvm_stackmaps".to_string();
        file.get_section(name.clone())
            .ok_or_else(|| DemoError::MissingSection(name))?
    };

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

    let stack_map = StackMap::read_from::<LittleEndian>(&mut &stackmap_section.data[..]);
    println!("stack_map: {:?}", stack_map);

    let mut data = vec![b'h', b'e', b'l', b'l', b'o', 0];
    println!("path addr: {:?}", &path as *const _);
    println!("file addr: {:?}", &file as *const _);
    println!("data addr: {:?}", &data as *const _);

    println!("conservative stack_height: {}", backtrace_hack::stack_height());
    let addresses = backtrace_hack::backtrace_return_addresses();
    println!("backtrace:      {:?}", addresses);
    println!("backtrace map*: {:?}", {
        let derefed: Vec<_> = addresses.iter()
            .map(|p| unsafe { *(*p as *const *mut c_void) })
            .collect();
        derefed
    });
    unsafe {
        intrinsics::stackmap_call(0, 13, subcall, data.as_mut_ptr());
    }

    println!("Goodbye World");

    Ok(())
}
<commit_msg>stackmap status: querying ReturnAddress against StackMap yields sketchy results.<commit_after>#![feature(core_intrinsics, patchpoint_call_intrinsic, stackmap_call_intrinsic)]
#![feature(question_mark)]
#![feature(type_ascription)]
#![feature(rustc_attrs)]
#![feature(libc)]
#![feature(const_fn)]
#![feature(rustc_private)]
#![allow(unused_features)]

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

use std::intrinsics;

#[allow(dead_code)]
mod byteorder;
#[allow(dead_code, unused_imports, unused_variables, unused_mut)]
mod elf;

mod backtrace_hack;
mod llvm_stackmaps;

const DEMO_ID: i64 = 0;
const SUBCALL_ID: i64 = 1;
const SUBCALL2_ID: i64 = 2;

fn subcall(data: *mut u8) {
    println!("Enter `subcall`");
    unsafe {
        intrinsics::stackmap_call(SUBCALL_ID, 0, subcall_2, data);
    }
    println!("Finis `subcall`");
}

fn subcall_2(data: *mut u8) {
    println!("Enter `subcall_2`");
    let mut i = 0;
    let mut saw_one = false;
    print!("[");
    let mut datum = unsafe { *data.offset(i) as char };
    while datum != '\0' {
        if saw_one {
            print!(", ");
        }
        print!("{}", datum);
        saw_one = true;
        i += 1;
        datum = unsafe { *data.offset(i) as char };
    }
    println!("]");
    unsafe {
        intrinsics::stackmap_call(SUBCALL2_ID, 0, subcall_3, data);
    }
    println!("Finis `subcall_2`");
}

use self::backtrace_hack::ReturnAddress;
use self::backtrace_hack::dlinfo_fbase;

#[derive(Debug)]
enum FrameInfo<'a> {
    Addr(ReturnAddress),
    StackMap(StackMapFrameInfo<'a>),
}

#[derive(Debug)]
struct StackMapFrameInfo<'a> {
    addr: ReturnAddress,
    patchpoint_id: u64,
    instruction_offset: u32,
    locations: &'a [Location],
    live_outs: &'a [LiveOut],
}

impl StackMap {
    pub fn frame_info(&self, addr: ReturnAddress) -> FrameInfo {
        let mut frame_info = None;

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

	if let Some(base) = dlinfo_fbase(addr.0) {
            // Okay, we have a base address for the shared object of addr

            // We still need to find the function itself as well as
            // the call-site within it; here's a naive way to do it.
            //
            // FIXME: build structure ahead of time that allows
            // something faster than this linear scan.
            for r in self.records() {
                let fn_address = address_id(r.patchpoint_id()).unwrap();
                let instr_offset = r.instruction_offset() as usize;
                let call_address = fn_address + instr_offset;

                println!("frame_info addr {:?} \
                                     record {:?} \
                                     fn_address: 0x{:x} \
                                     instr_offset: {:?} \
                                     call_address: 0x{:x} \
                                     delta: {}",
                         addr, r, fn_address, instr_offset, call_address,
                         call_address as isize - (addr.0 as isize));

                if call_address == addr.0 as usize {
                    assert!(frame_info.is_none());
                    frame_info = Some(StackMapFrameInfo {
                        addr: addr,
                        patchpoint_id: r.patchpoint_id(),
                        instruction_offset: r.instruction_offset(),
                        locations: r.locations(),
                        live_outs: r.live_outs(),
                    })
                }
            }
        } else {
            println!("frame_info no fbase for {:?}", addr);
        }

        if let Some(frame_info) = frame_info {
            FrameInfo::StackMap(frame_info)
        } else {
            FrameInfo::Addr(addr)
        }
    }
}

fn subcall_3(data: *mut u8) {
    println!("Start `subcall_3`");
    let map = unsafe { STACK_MAP.get() };
    println!("map:                   {:?}", map);
    let addresses = backtrace_hack::backtrace_return_addresses();
    println!("backtrace:             {:?}", addresses);
    let dlinfos = backtrace_hack::backtrace_dlinfos(&addresses);
    println!("backtrace dlinfos:     {:?}", dlinfos);
    let frame_infos: Vec<_> = addresses.iter()
        .map(|a| map.frame_info(*a))
        .collect();
    println!("backtrace frame_infos: {:?}", frame_infos);
    println!("Finis `subcall_3`");
}

static mut MAIN_LOCAL: usize = 0;
pub fn main_height() -> usize { unsafe { MAIN_LOCAL } }

fn main() {
    let local: u32 = 0;
    unsafe { MAIN_LOCAL = &local as *const _ as usize; }
    demo().unwrap();
}

#[derive(Debug)]
pub enum DemoError {
    ParseError(elf::ParseError),
    MissingSection(String),
}

impl std::convert::From<elf::ParseError> for DemoError {
    fn from(x: elf::ParseError) -> Self { DemoError::ParseError(x) }
}

use self::llvm_stackmaps::{LiveOut, Location, StackMap};
use std::cell::UnsafeCell;
use std::sync::RwLock;

struct SharedStackMap {
    map: UnsafeCell<Option<StackMap>>
}

impl SharedStackMap {
    /// Initializes the map. Unsafe because it does not attempt to
    /// enforce thread safety in any way: its your own job to make
    /// sure you do this once and only once.
    unsafe fn initialize(&self, map: StackMap) { *self.map.get() = Some(map); }
    const fn new() -> Self { SharedStackMap { map: UnsafeCell::new(None) } }
    unsafe fn get(&self) -> &'static StackMap {
        (*self.map.get()).as_ref().expect("Cannot get an uninitialized stack map")
    }
}

/// I am going to manually ensure that all clients of SharedStackMap access
/// it in solely a read-only fashion.
unsafe impl Sync for SharedStackMap { }

static STACK_MAP: SharedStackMap = SharedStackMap::new();
static mut ADDRESS_IDS: [usize; 3] = [0; 3];
fn address_id(patchpoint_id: u64) -> Option<usize> {
    unsafe {
        if patchpoint_id > ::std::usize::MAX as u64 { return None; }
        let idx = patchpoint_id as usize;
        if idx >= ADDRESS_IDS.len() { return None; }
        Some(ADDRESS_IDS[idx])
    }
}

fn initialize_shared_state() -> Result<(), DemoError> {
    use std::path::Path;
    use self::byteorder::LittleEndian;

    // FIXME: LLVM's stackmap/patchpoint intrinsic API does
    // not embed the identity of the function context where the
    // stackmap/patchpoint.
    //
    // It instead requires each stackmap/patchpoint invocation to
    // include a (globally unique) 64-bit ID, known at compile time.
    //
    // The problem for me is that it means that I have the
    // responsibility of building a mapping from functions to their
    // unique ID's, just so I can map from the return address to the
    // appropriate stackmap record. :(
    //
    // (I do not quite understand why they made this so hard;
    // presumably it is because the stackmap/patchpoint interface was
    // designed for use in JITs, not in AOT compilers.)
    //
    // In any case, for now I can just hardcode a table with the
    // necessary mapping from patchpoint IDs to function addresses.
    // (And hopefully I can hack the Rust backend to generate a table
    // like this automatically, though ensuring the IDs are unique
    // will be a bit harder than what I do here. I guess for that
    // I might as well re-use the SVH approach... we'll cross that
    // bridge when we get to it.)
    unsafe {
        ADDRESS_IDS[DEMO_ID as usize] = demo as usize;
        ADDRESS_IDS[SUBCALL_ID as usize] = subcall as usize;
        ADDRESS_IDS[SUBCALL2_ID as usize] = subcall_2 as usize;
    }

    // FIXME: this is not a reliable way to extract the executable file path.
    // (look into alternatives)
    let binary = std::env::args().next().unwrap();

    println!("Hello World from {}", binary);

    let path = Path::new(&binary);
    let file = elf::File::open_path(&path)?;

    let stackmap_section = {
        let name = ".llvm_stackmaps".to_string();
        file.get_section(name.clone())
            .ok_or_else(|| DemoError::MissingSection(name))?
    };

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

    let stack_map = StackMap::read_from::<LittleEndian>(&mut &stackmap_section.data[..]);
    println!("stack_map: {:?}", stack_map);
    let map = stack_map.expect("Cannot do demo without valid stack map");
    unsafe { STACK_MAP.initialize(map); }
    Ok(())
}

#[no_mangle]
#[rustc_mir(borrowck_graphviz_postflow="/tmp/foo.dot")]
pub fn demo() -> Result<(), DemoError> {
    try!(initialize_shared_state());

    let mut data = vec![b'h', b'e', b'l', b'l', b'o', 0];
    println!("data addr: {:?}", &data as *const _);

    unsafe {
        intrinsics::stackmap_call(DEMO_ID, 0, subcall, data.as_mut_ptr());
    }

    println!("Goodbye World");

    Ok(())
}
<|endoftext|>


<commit_before>use dora_parser::ast::Ast;
use dora_parser::lexer::reader::Reader;
use dora_parser::parser::Parser;

use crate::driver::cmd::Args;
use crate::os;
use crate::semck;
use crate::vm::VM;

pub fn parse<F, T>(code: &'static str, f: F) -> T
where
    F: FnOnce(&VM) -> T,
{
    parse_with_errors(code, |vm| {
        if vm.diag.lock().has_errors() {
            vm.diag.lock().dump(vm);
            println!("{}", code);
            panic!("unexpected error in test::parse()");
        }

        f(vm)
    })
}

pub fn parse_with_errors<F, T>(code: &'static str, f: F) -> T
where
    F: FnOnce(&VM) -> T,
{
    os::mem::init_page_size();

    let mut ast = Ast::new();
    let args: Args = Default::default();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    for file in &[
        "stdlib/Identity.dora",
        "stdlib/Equals.dora",
        "stdlib/Bool.dora",
        "stdlib/Byte.dora",
        "stdlib/Char.dora",
        "stdlib/Int.dora",
        "stdlib/Long.dora",
        "stdlib/Float.dora",
        "stdlib/Double.dora",
        "stdlib/Array.dora",
        "stdlib/String.dora",
        "stdlib/Stringable.dora",
        "stdlib/StringBuffer.dora",
        "stdlib/Throwable.dora",
        "stdlib/Error.dora",
        "stdlib/Exception.dora",
        "stdlib/Thread.dora",
        "stdlib/Comparable.dora",
        "stdlib/Sortable.dora",
        "stdlib/Hash.dora",
        "stdlib/Default.dora",
        "stdlib/prelude.dora",
        "stdlib/Testing.dora",
    ] {
        let reader = Reader::from_file(file).unwrap();
        let parser = Parser::new(reader, &vm.id_generator, &mut ast, &mut vm.interner);
        parser.parse().unwrap();
    }

    {
        let reader = Reader::from_string("<<code>>", code);
        let parser = Parser::new(reader, &vm.id_generator, &mut ast, &mut vm.interner);
        parser.parse().unwrap();
    }

    vm.ast = *

    semck::check(&mut vm);

    f(&vm)
}
<commit_msg>test: better error message for non-existing files<commit_after>use dora_parser::ast::Ast;
use dora_parser::lexer::reader::Reader;
use dora_parser::parser::Parser;

use crate::driver::cmd::Args;
use crate::os;
use crate::semck;
use crate::vm::VM;

pub fn parse<F, T>(code: &'static str, f: F) -> T
where
    F: FnOnce(&VM) -> T,
{
    parse_with_errors(code, |vm| {
        if vm.diag.lock().has_errors() {
            vm.diag.lock().dump(vm);
            println!("{}", code);
            panic!("unexpected error in test::parse()");
        }

        f(vm)
    })
}

pub fn parse_with_errors<F, T>(code: &'static str, f: F) -> T
where
    F: FnOnce(&VM) -> T,
{
    os::mem::init_page_size();

    let mut ast = Ast::new();
    let args: Args = Default::default();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    for file in &[
        "stdlib/Identity.dora",
        "stdlib/Equals.dora",
        "stdlib/Bool.dora",
        "stdlib/Byte.dora",
        "stdlib/Char.dora",
        "stdlib/Int.dora",
        "stdlib/Long.dora",
        "stdlib/Float.dora",
        "stdlib/Double.dora",
        "stdlib/Array.dora",
        "stdlib/String.dora",
        "stdlib/Stringable.dora",
        "stdlib/StringBuffer.dora",
        "stdlib/Throwable.dora",
        "stdlib/Error.dora",
        "stdlib/Exception.dora",
        "stdlib/Thread.dora",
        "stdlib/Comparable.dora",
        "stdlib/Sortable.dora",
        "stdlib/Hash.dora",
        "stdlib/Default.dora",
        "stdlib/prelude.dora",
        "stdlib/Testing.dora",
    ] {
        let msg = format!("cannot open stdlib file `{}`.", file);
        let reader = Reader::from_file(file).expect(&msg);
        let parser = Parser::new(reader, &vm.id_generator, &mut ast, &mut vm.interner);
        parser.parse().unwrap();
    }

    {
        let reader = Reader::from_string("<<code>>", code);
        let parser = Parser::new(reader, &vm.id_generator, &mut ast, &mut vm.interner);
        parser.parse().unwrap();
    }

    vm.ast = *

    semck::check(&mut vm);

    f(&vm)
}
<|endoftext|>


<commit_before>#![feature(test)]
#![feature(slice_patterns)]
#![feature(core)]
#![allow(unused_imports)]

extern crate rand;
extern crate time;
extern crate core;
#[macro_use]
extern crate eve;
extern crate test;

use rand::distributions::{IndependentSample, Range};

use eve::query::*;
use eve::value::*;
use eve::value::Value::*;
use eve::interpreter::EveFn;
use core::num::ToPrimitive;

/*
fn main() {

    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.to_value()},Ref::Constant{value: 2.to_value()}]};

    // Build the query
    let query = Query{clauses: clausevec![c0]};

    for result in query.iter(vec![]) {
        println!("{:?}",result);
    }

 	let a = vec![(0.0, 1.0, 2.0), (5.0, 6.0, 6.0), (7.0, 8.0, 9.0)].to_relation();
    let b = vec![(-7.0,), (8.0,), (10.0,)].to_relation();
    let a0_lt_b0 = Constraint{
        my_column: 0,
        op: ConstraintOp::LT,
        other_ref: 1.to_constref(),
    };

    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![a0_lt_b0]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
    	println!("--------------------------------");
    	println!("{:?}",result);
    	println!("--------------------------------");
        resultvec.push(result);
    }

	// Build the correct answer
    //let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    //let e2 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    //let e3 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    //let correct = vec![e1,e2,e3];

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Relation(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![]}),
    ]};
    for result in query.iter(vec![&a, &b]) {
        println!("{:?}", result);
    }
    println!("");

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Relation(Source{relation: 1, constraints: vec![]}),
    ]};
    for result in query.iter(vec![&a, &b]) {
        println!("{:?}", result);
    }
    println!("");

}
*/

#[test]
fn test1() {

	let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a, &b]) {
        resultvec.push(result);
    }

    // Build the correct answer
    let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e2 = ((0.0,1.0,2.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let e3 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e4 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let correct = vec![e1,e2,e3,e4];

    // Test correctness
    assert_eq!(resultvec,correct);
}

#[test]
fn constrainttest() {

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let b0_lt_a0 = Constraint{
        my_column: 0,
        op: ConstraintOp::LT,
        other_ref: Ref::Value{clause: 0, column: 0},
    };
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![b0_lt_a0]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a, &b]) {
        resultvec.push(result);
    }

	// Build the correct answer
    let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e2 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e3 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let correct = vec![e1,e2,e3];

    // Test correctness
    assert_eq!(resultvec,correct);

}

/*
#[test]
fn stringtest() {

	let a = vec![("Andy Warhol",), ("Leonardo da Vinci",)].to_relation();
	let c0 = Call{fun: EveFn::StrUpper, arg_refs: vec![Ref::Value{clause: 0, column: 0}]};
	let c1 = Call{fun: EveFn::StrSplit, arg_refs: vec![Ref::Call{clause: 1}]};

	// Query takes a relation and nested function calls on a set of strings
	let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
        resultvec.push(result);
    }

    let s0 = ("ANDY","WARHOL").to_tuple();
    let s1 = ("LEONARDO","DA","VINCI").to_tuple();

    // Test equality hack
	assert_eq!(resultvec[0][2][0],s0[0]); // ANDY
	assert_eq!(resultvec[0][2][1],s0[1]); // WARHOL
    assert_eq!(resultvec[1][2][0],s1[0]); // LEONARDO
    assert_eq!(resultvec[1][2][1],s1[1]); // DA
    assert_eq!(resultvec[1][2][2],s1[2]); // VINCI

}

#[test]
fn opstest() {

	// General math with a relation (((1.3 + 2) * [1 2 3 4]) + (7 - 4) / 10) ^ 2
    let a = vec![(1.0,),(2.0,),(3.0,),(4.0,)].to_relation();
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.3.to_value()},Ref::Constant{value: 2.to_value()}]};       // C0 = 1.3 + 2
    let c1 = Call{fun: EveFn::Multiply, arg_refs: vec![Ref::Call{clause: 1},Ref::Value{clause: 0, column: 0}]};                    // C1 = C0 * [1 2 3 4]
    let c2 = Call{fun: EveFn::Subtract, arg_refs: vec![Ref::Constant{value: 7.to_value()},Ref::Constant{value: 4.to_value()}]};    // C2 = 7 - 4
    let c3 = Call{fun: EveFn::Divide, arg_refs: vec![Ref::Call{clause: 3},Ref::Constant{value: 10.to_value()}]};                   // C3 = C2 / 10
    let c4 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Call{clause: 2},Ref::Call{clause: 4}]};                                     // C4 = C1 + C3
    let c5 = Call{fun: EveFn::Exponentiate, arg_refs: vec![Ref::Call{clause: 5},Ref::Constant{value: 2.to_value()}]};              // C5 = C4 ^ 2

    // Build the query
    let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1,c2,c3,c4,c5]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
        resultvec.push(result);
    }

   	// Test against the correct answer
	assert_eq!(resultvec[0][6].to_f64().unwrap() as f32, 12.96f32);
	assert_eq!(resultvec[1][6].to_f64().unwrap() as f32, 47.61f32);
	assert_eq!(resultvec[2][6].to_f64().unwrap() as f32, 104.04f32);
	assert_eq!(resultvec[3][6].to_f64().unwrap() as f32, 182.25f32);

}

#[bench]
fn simplemath(b: &mut test::Bencher) {

    // C0 = 1 + 2
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.to_value()},Ref::Constant{value: 2.to_value()}]};

    // Build the query
    let query = Query{clauses: clausevec![c0]};

    b.iter(|| {
        query.iter(vec![]).count()
    });
}

#[bench]
fn opsbench(b: &mut test::Bencher) {

     // General math with a relation (((1.3 + 2) * [1 2 3 4]) + (7 - 4) / 10) ^ 2
    let a = vec![(1.0,),(2.0,),(3.0,),(4.0,)].to_relation();
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.3.to_value()},Ref::Constant{value: 2.to_value()}]};       // C0 = 1.3 + 2
    let c1 = Call{fun: EveFn::Multiply, arg_refs: vec![Ref::Call{clause: 1},Ref::Value{clause: 0, column: 0}]};                    // C1 = C0 * [1 2 3 4]
    let c2 = Call{fun: EveFn::Subtract, arg_refs: vec![Ref::Constant{value: 7.to_value()},Ref::Constant{value: 4.to_value()}]};    // C2 = 7 - 4
    let c3 = Call{fun: EveFn::Divide, arg_refs: vec![Ref::Call{clause: 3},Ref::Constant{value: 10.to_value()}]};                   // C3 = C2 / 10
    let c4 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Call{clause: 2},Ref::Call{clause: 4}]};                                     // C4 = C1 + C3
    let c5 = Call{fun: EveFn::Exponentiate, arg_refs: vec![Ref::Call{clause: 5},Ref::Constant{value: 2.to_value()}]};              // C5 = C4 ^ 2

    // Build the query
    let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1,c2,c3,c4,c5]};

    b.iter(|| {
        query.iter(vec![&a]).count();
    });
}

#[bench]
fn userip(b: &mut test::Bencher) {

    let bench_size = 1000;
    let between = Range::new(0, bench_size);
    let mut rng = rand::thread_rng();

    let mut users = vec![];
    let mut logins = vec![];
    let mut bans = vec![];

    for i in (0..bench_size) {
        users.push(vec![String(format!("email{}", i)), String(format!("user{}", i))]);
    }

    for i in (0..bench_size) {
        let user = between.ind_sample(&mut rng);
        logins.push(vec![String(format!("user{}", user)), String(format!("ip{}", i))]);
    }

    for i in (0..bench_size) {
        bans.push(vec![String(format!("ip{}", i))]);
    }

    //let start = time::precise_time_s();
    let users = users.into_iter().collect();
    let logins = logins.into_iter().collect();
    let bans = bans.into_iter().collect();
    //let end = time::precise_time_s();

    //println!("index: {}s", end - start);

    let user_eq_user = Constraint{
        my_column: 0,
        op: ConstraintOp::EQ,
        other_ref: Ref::Value{clause: 0, column: 0},
    };

    let ip_eq_ip = Constraint{
        my_column: 0,
        op: ConstraintOp::EQ,
        other_ref: Ref::Value{clause: 1, column: 1},
    };

    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![user_eq_user]}),
        Clause::Tuple(Source{relation: 2, constraints: vec![ip_eq_ip]}),
    ]};

    b.iter(|| {
        query.iter(vec![&users, &logins, &bans]).count()
    });

    //let start = time::precise_time_s();
    //drop(query);
    //let end = time::precise_time_s();
    //println!("erase: {}s", end - start);
}
*/<commit_msg>Uncomment main function<commit_after>#![feature(test)]
#![feature(slice_patterns)]
#![feature(core)]
#![allow(unused_imports)]

extern crate rand;
extern crate time;
extern crate core;
#[macro_use]
extern crate eve;
extern crate test;

use rand::distributions::{IndependentSample, Range};

use eve::query::*;
use eve::value::*;
use eve::value::Value::*;
use eve::interpreter::EveFn;
use core::num::ToPrimitive;

fn main() {
/*

    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.to_value()},Ref::Constant{value: 2.to_value()}]};

    // Build the query
    let query = Query{clauses: clausevec![c0]};

    for result in query.iter(vec![]) {
        println!("{:?}",result);
    }

 	let a = vec![(0.0, 1.0, 2.0), (5.0, 6.0, 6.0), (7.0, 8.0, 9.0)].to_relation();
    let b = vec![(-7.0,), (8.0,), (10.0,)].to_relation();
    let a0_lt_b0 = Constraint{
        my_column: 0,
        op: ConstraintOp::LT,
        other_ref: 1.to_constref(),
    };

    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![a0_lt_b0]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
    	println!("--------------------------------");
    	println!("{:?}",result);
    	println!("--------------------------------");
        resultvec.push(result);
    }

	// Build the correct answer
    //let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    //let e2 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    //let e3 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    //let correct = vec![e1,e2,e3];

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Relation(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![]}),
    ]};
    for result in query.iter(vec![&a, &b]) {
        println!("{:?}", result);
    }
    println!("");

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Relation(Source{relation: 1, constraints: vec![]}),
    ]};
    for result in query.iter(vec![&a, &b]) {
        println!("{:?}", result);
    }
    println!("");

*/
}

#[test]
fn test1() {

	let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a, &b]) {
        resultvec.push(result);
    }

    // Build the correct answer
    let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e2 = ((0.0,1.0,2.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let e3 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e4 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let correct = vec![e1,e2,e3,e4];

    // Test correctness
    assert_eq!(resultvec,correct);
}

#[test]
fn constrainttest() {

    let a = vec![(0.0, 1.0, 2.0), (4.0, 5.0, 6.0)].to_relation();
    let b = vec![(-0.0, -1.0, -2.0), (-4.0, -5.0, -6.0)].to_relation();
    let b0_lt_a0 = Constraint{
        my_column: 0,
        op: ConstraintOp::LT,
        other_ref: Ref::Value{clause: 0, column: 0},
    };
    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![b0_lt_a0]}),
    ]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a, &b]) {
        resultvec.push(result);
    }

	// Build the correct answer
    let e1 = ((0.0,1.0,2.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e2 = ((4.0,5.0,6.0).to_tuple(),(-4.0,-5.0,-6.0).to_tuple()).to_tuple();
    let e3 = ((4.0,5.0,6.0).to_tuple(),(0.0,-1.0,-2.0).to_tuple()).to_tuple();
    let correct = vec![e1,e2,e3];

    // Test correctness
    assert_eq!(resultvec,correct);

}

/*
#[test]
fn stringtest() {

	let a = vec![("Andy Warhol",), ("Leonardo da Vinci",)].to_relation();
	let c0 = Call{fun: EveFn::StrUpper, arg_refs: vec![Ref::Value{clause: 0, column: 0}]};
	let c1 = Call{fun: EveFn::StrSplit, arg_refs: vec![Ref::Call{clause: 1}]};

	// Query takes a relation and nested function calls on a set of strings
	let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
        resultvec.push(result);
    }

    let s0 = ("ANDY","WARHOL").to_tuple();
    let s1 = ("LEONARDO","DA","VINCI").to_tuple();

    // Test equality hack
	assert_eq!(resultvec[0][2][0],s0[0]); // ANDY
	assert_eq!(resultvec[0][2][1],s0[1]); // WARHOL
    assert_eq!(resultvec[1][2][0],s1[0]); // LEONARDO
    assert_eq!(resultvec[1][2][1],s1[1]); // DA
    assert_eq!(resultvec[1][2][2],s1[2]); // VINCI

}

#[test]
fn opstest() {

	// General math with a relation (((1.3 + 2) * [1 2 3 4]) + (7 - 4) / 10) ^ 2
    let a = vec![(1.0,),(2.0,),(3.0,),(4.0,)].to_relation();
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.3.to_value()},Ref::Constant{value: 2.to_value()}]};       // C0 = 1.3 + 2
    let c1 = Call{fun: EveFn::Multiply, arg_refs: vec![Ref::Call{clause: 1},Ref::Value{clause: 0, column: 0}]};                    // C1 = C0 * [1 2 3 4]
    let c2 = Call{fun: EveFn::Subtract, arg_refs: vec![Ref::Constant{value: 7.to_value()},Ref::Constant{value: 4.to_value()}]};    // C2 = 7 - 4
    let c3 = Call{fun: EveFn::Divide, arg_refs: vec![Ref::Call{clause: 3},Ref::Constant{value: 10.to_value()}]};                   // C3 = C2 / 10
    let c4 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Call{clause: 2},Ref::Call{clause: 4}]};                                     // C4 = C1 + C3
    let c5 = Call{fun: EveFn::Exponentiate, arg_refs: vec![Ref::Call{clause: 5},Ref::Constant{value: 2.to_value()}]};              // C5 = C4 ^ 2

    // Build the query
    let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1,c2,c3,c4,c5]};

 	let mut resultvec = Vec::new();

    for result in query.iter(vec![&a]) {
        resultvec.push(result);
    }

   	// Test against the correct answer
	assert_eq!(resultvec[0][6].to_f64().unwrap() as f32, 12.96f32);
	assert_eq!(resultvec[1][6].to_f64().unwrap() as f32, 47.61f32);
	assert_eq!(resultvec[2][6].to_f64().unwrap() as f32, 104.04f32);
	assert_eq!(resultvec[3][6].to_f64().unwrap() as f32, 182.25f32);

}

#[bench]
fn simplemath(b: &mut test::Bencher) {

    // C0 = 1 + 2
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.to_value()},Ref::Constant{value: 2.to_value()}]};

    // Build the query
    let query = Query{clauses: clausevec![c0]};

    b.iter(|| {
        query.iter(vec![]).count()
    });
}

#[bench]
fn opsbench(b: &mut test::Bencher) {

     // General math with a relation (((1.3 + 2) * [1 2 3 4]) + (7 - 4) / 10) ^ 2
    let a = vec![(1.0,),(2.0,),(3.0,),(4.0,)].to_relation();
    let c0 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Constant{value: 1.3.to_value()},Ref::Constant{value: 2.to_value()}]};       // C0 = 1.3 + 2
    let c1 = Call{fun: EveFn::Multiply, arg_refs: vec![Ref::Call{clause: 1},Ref::Value{clause: 0, column: 0}]};                    // C1 = C0 * [1 2 3 4]
    let c2 = Call{fun: EveFn::Subtract, arg_refs: vec![Ref::Constant{value: 7.to_value()},Ref::Constant{value: 4.to_value()}]};    // C2 = 7 - 4
    let c3 = Call{fun: EveFn::Divide, arg_refs: vec![Ref::Call{clause: 3},Ref::Constant{value: 10.to_value()}]};                   // C3 = C2 / 10
    let c4 = Call{fun: EveFn::Add, arg_refs: vec![Ref::Call{clause: 2},Ref::Call{clause: 4}]};                                     // C4 = C1 + C3
    let c5 = Call{fun: EveFn::Exponentiate, arg_refs: vec![Ref::Call{clause: 5},Ref::Constant{value: 2.to_value()}]};              // C5 = C4 ^ 2

    // Build the query
    let query = Query{clauses: clausevec![Clause::Tuple(Source{relation: 0, constraints: vec![]}),c0,c1,c2,c3,c4,c5]};

    b.iter(|| {
        query.iter(vec![&a]).count();
    });
}

#[bench]
fn userip(b: &mut test::Bencher) {

    let bench_size = 1000;
    let between = Range::new(0, bench_size);
    let mut rng = rand::thread_rng();

    let mut users = vec![];
    let mut logins = vec![];
    let mut bans = vec![];

    for i in (0..bench_size) {
        users.push(vec![String(format!("email{}", i)), String(format!("user{}", i))]);
    }

    for i in (0..bench_size) {
        let user = between.ind_sample(&mut rng);
        logins.push(vec![String(format!("user{}", user)), String(format!("ip{}", i))]);
    }

    for i in (0..bench_size) {
        bans.push(vec![String(format!("ip{}", i))]);
    }

    //let start = time::precise_time_s();
    let users = users.into_iter().collect();
    let logins = logins.into_iter().collect();
    let bans = bans.into_iter().collect();
    //let end = time::precise_time_s();

    //println!("index: {}s", end - start);

    let user_eq_user = Constraint{
        my_column: 0,
        op: ConstraintOp::EQ,
        other_ref: Ref::Value{clause: 0, column: 0},
    };

    let ip_eq_ip = Constraint{
        my_column: 0,
        op: ConstraintOp::EQ,
        other_ref: Ref::Value{clause: 1, column: 1},
    };

    let query = Query{clauses: vec![
        Clause::Tuple(Source{relation: 0, constraints: vec![]}),
        Clause::Tuple(Source{relation: 1, constraints: vec![user_eq_user]}),
        Clause::Tuple(Source{relation: 2, constraints: vec![ip_eq_ip]}),
    ]};

    b.iter(|| {
        query.iter(vec![&users, &logins, &bans]).count()
    });

    //let start = time::precise_time_s();
    //drop(query);
    //let end = time::precise_time_s();
    //println!("erase: {}s", end - start);
}
*/<|endoftext|>


<commit_before>extern crate regex;

#[cfg(test)]
mod tests;

use regex::Regex;
use std::io::{self, Read};
use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::thread;

#[derive(PartialEq, Debug)]
enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

#[derive(Debug)]
struct Request {
    method:  Method,
    path:    String,
    headers: HashMap<String, String>,
    payload: String,
}

impl Request {
    fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            payload: String::new(),
        }
    }

    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    pub fn parse(&mut self, rqstr: &str) {
        let mut chunks: Vec<&str> = rqstr.splitn(2, "\r\n\r\n").collect();
        let mut header: Vec<&str> = chunks.pop().unwrap().split("\r\n").collect();
        let ask: Vec<&str> = header.pop().unwrap().splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);

        for line in header {
            let hdr: Vec<&str> = line.splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

#[derive(Debug)]
pub struct Route {
    pathdef: String,
    matcher: Regex,
    params:  HashMap<String, String>,
}

impl Route {
    fn new(path: &str) -> Route {
        let re = Regex::new(r"^<(?:(int|str):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let mut parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, String> = HashMap::new();

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: String = match caps.at(1) {
                        Some(x)     => String::from(x),
                        None        => String::from("str"),
                    };

                    let mstr: String = match ptype.as_ref() {
                        "str" => String::from("(?:[^/])+"),
                        "int" => String::from("[0-9]+"),
                        _     => String::from("(?:[^/])+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), mstr.clone());

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            pathdef: String::from(path),
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
        }
    }

    fn is_match(&self, path: &str) -> bool {
        self.matcher.is_match(&path)
    }

    fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut rqstr: String = String::new();
    let mut buf: String = String::new();

    while stream.read_to_string(&mut buf).unwrap() > 0 {
        rqstr.push_str(&buf);
    }

    println!("{}", rqstr);
    println!("");

    let req = Request::from_str(&rqstr);
}

fn main() {
    let rt = Route::new("/api/v1/foo/<foo_stuff>");
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").unwrap().get("foo_stuff").unwrap());
    /*
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                thread::spawn(move || {
                    handle_client(stream)
                });
            },
            Err(e)      => {},
        }
    }

    drop(listener);
    */
}
<commit_msg>add in payload support for non-GET requests, strip debug message<commit_after>extern crate regex;

#[cfg(test)]
mod tests;

use regex::Regex;
use std::io::{self, Read};
use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::thread;

#[derive(PartialEq, Debug)]
enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

#[derive(Debug)]
struct Request {
    method:  Method,
    path:    String,
    headers: HashMap<String, String>,
    payload: String,
}

impl Request {
    fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            payload: String::new(),
        }
    }

    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    pub fn parse(&mut self, rqstr: &str) {
        let mut chunks: Vec<&str> = rqstr.splitn(2, "\r\n\r\n").collect();
        let mut header: Vec<&str> = chunks.pop().unwrap().split("\r\n").collect();
        let ask: Vec<&str> = header.pop().unwrap().splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);
        self.payload = match chunks.pop() {
            Some(x) => String::from(x),
            None    => String::new(),
        };

        for line in header {
            let hdr: Vec<&str> = line.splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

#[derive(Debug)]
pub struct Route {
    pathdef: String,
    matcher: Regex,
    params:  HashMap<String, String>,
}

impl Route {
    fn new(path: &str) -> Route {
        let re = Regex::new(r"^<(?:(int|str):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let mut parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, String> = HashMap::new();

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: String = match caps.at(1) {
                        Some(x)     => String::from(x),
                        None        => String::from("str"),
                    };

                    let mstr: String = match ptype.as_ref() {
                        "str" => String::from("(?:[^/])+"),
                        "int" => String::from("[0-9]+"),
                        _     => String::from("(?:[^/])+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), mstr.clone());

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            pathdef: String::from(path),
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
        }
    }

    fn is_match(&self, path: &str) -> bool {
        self.matcher.is_match(&path)
    }

    fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut rqstr: String = String::new();
    let mut buf: String = String::new();

    while stream.read_to_string(&mut buf).unwrap() > 0 {
        rqstr.push_str(&buf);
    }

    let req = Request::from_str(&rqstr);
}

fn main() {
    let rt = Route::new("/api/v1/foo/<foo_stuff>");
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").unwrap().get("foo_stuff").unwrap());
    /*
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                thread::spawn(move || {
                    handle_client(stream)
                });
            },
            Err(e)      => {},
        }
    }

    drop(listener);
    */
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate glib;

use std::str::FromStr;

use self::glib::translate::*;

use error::*;
use length::*;
use parsers::ParseError;

pub enum RsvgPropertyBag {}

extern "C" {
    fn rsvg_property_bag_size (pbag: *const RsvgPropertyBag) -> libc::c_uint;
    fn rsvg_property_bag_lookup (pbag: *const RsvgPropertyBag, key: *const libc::c_char) -> *const libc::c_char;
}

pub fn get_size (pbag: *const RsvgPropertyBag) -> usize {
    unsafe { rsvg_property_bag_size (pbag) as usize }
}

pub fn lookup (pbag: *const RsvgPropertyBag, key: &str) -> Option<String> {
    unsafe {
        let c_value = rsvg_property_bag_lookup (pbag, key.to_glib_none ().0);
        from_glib_none (c_value)
    }
}

pub fn length_or_default (pbag: *const RsvgPropertyBag, key: &'static str, length_dir: LengthDir) -> Result <RsvgLength, NodeError> {
    let value = lookup (pbag, key);

    if let Some (v) = value {
        RsvgLength::parse (&v, length_dir).map_err (|e| NodeError::attribute_error (key, e))
    } else {
        Ok (RsvgLength::default ())
    }
}

pub fn parse_or_default<T> (pbag: *const RsvgPropertyBag, key: &'static str) -> Result <T, NodeError>
    where T: Default + FromStr<Err = AttributeError>
{
    let value = lookup (pbag, key);

    if let Some (v) = value {
        T::from_str (&v).map_err (|e| NodeError::attribute_error (key, e))
    } else {
        Ok (T::default ())
    }
}
<commit_msg>property_bag.rs: Implement parse_or_none(), length_or_none()<commit_after>extern crate libc;
extern crate glib;

use std::str::FromStr;

use self::glib::translate::*;

use error::*;
use length::*;
use parsers::ParseError;

pub enum RsvgPropertyBag {}

extern "C" {
    fn rsvg_property_bag_size (pbag: *const RsvgPropertyBag) -> libc::c_uint;
    fn rsvg_property_bag_lookup (pbag: *const RsvgPropertyBag, key: *const libc::c_char) -> *const libc::c_char;
}

pub fn get_size (pbag: *const RsvgPropertyBag) -> usize {
    unsafe { rsvg_property_bag_size (pbag) as usize }
}

pub fn lookup (pbag: *const RsvgPropertyBag, key: &str) -> Option<String> {
    unsafe {
        let c_value = rsvg_property_bag_lookup (pbag, key.to_glib_none ().0);
        from_glib_none (c_value)
    }
}

pub fn length_or_none (pbag: *const RsvgPropertyBag, key: &'static str, length_dir: LengthDir) -> Result <Option<RsvgLength>, NodeError> {
    let value = lookup (pbag, key);

    if let Some (v) = value {
        RsvgLength::parse (&v, length_dir).map (|l| Some (l))
            .map_err (|e| NodeError::attribute_error (key, e))
    } else {
        Ok (None)
    }
}

pub fn length_or_default (pbag: *const RsvgPropertyBag, key: &'static str, length_dir: LengthDir) -> Result <RsvgLength, NodeError> {
    let r = length_or_none (pbag, key, length_dir);

    match r {
        Ok (Some (v)) => Ok (v),
        Ok (None)     => Ok (RsvgLength::default ()),
        Err (e)       => Err (e)
    }
}

pub fn parse_or_none<T> (pbag: *const RsvgPropertyBag, key: &'static str) -> Result <Option<T>, NodeError>
    where T: Default + FromStr<Err = AttributeError>
{
    let value = lookup (pbag, key);

    match value {
        Some (v) => {
            T::from_str (&v).map (|v| Some (v))
                .map_err (|e| NodeError::attribute_error (key, e))
        },

        None => Ok (None)
    }
}

pub fn parse_or_default<T> (pbag: *const RsvgPropertyBag, key: &'static str) -> Result <T, NodeError>
    where T: Default + FromStr<Err = AttributeError>
{
    let r = parse_or_none::<T> (pbag, key);

    match r {
        Ok (Some (v)) => Ok (v),
        Ok (None)     => Ok (T::default ()),
        Err (e)       => Err (e)
    }
}
<|endoftext|>


<commit_before>use crate::utils::{differing_macro_contexts, in_macro_or_desugar, snippet_opt, span_note_and_lint};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax::ast::*;
use syntax::ptr::P;

declare_clippy_lint! {
    /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
    /// operators.
    ///
    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
    /// confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
    /// ```
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    style,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
    /// is followed immediately by a newline or the `else` seems to be missing.
    ///
    /// **Why is this bad?** This is probably some refactoring remnant, even if the
    /// code is correct, it might look confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if foo {
    /// } { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } if bar { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// { // this is the `else` block of the previous `if`, but should it be?
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// if bar { // this is the `else` block of the previous `if`, but should it be?
    /// }
    /// ```
    pub SUSPICIOUS_ELSE_FORMATTING,
    style,
    "suspicious formatting of `else`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for possible missing comma in an array. It lints if
    /// an array element is a binary operator expression and it lies on two lines.
    ///
    /// **Why is this bad?** This could lead to unexpected results.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a = &[
    ///     -1, -2, -3 // <= no comma here
    ///     -4, -5, -6
    /// ];
    /// ```
    pub POSSIBLE_MISSING_COMMA,
    correctness,
    "possible missing comma in array"
}

declare_lint_pass!(Formatting => [
    SUSPICIOUS_ASSIGNMENT_FORMATTING,
    SUSPICIOUS_ELSE_FORMATTING,
    POSSIBLE_MISSING_COMMA
]);

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&StmtKind::Expr(ref first), &StmtKind::Expr(ref second))
                | (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => {
                    check_missing_else(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        check_assign(cx, expr);
        check_else(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
    if let ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro_or_desugar(lhs.span) {
            let eq_span = lhs.span.between(rhs.span);
            if let ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = UnOp::to_string(op);
                    let eqop_span = lhs.span.between(sub_rhs.span);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(
                            cx,
                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
                            eqop_span,
                            &format!(
                                "this looks like you are trying to use `.. {op}= ..`, but you \
                                 really are doing `.. = ({op} ..)`",
                                op = op
                            ),
                            eqop_span,
                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
                        );
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
    if_chain! {
        if let Some((then, &Some(ref else_))) = unsugar_if(expr);
        if is_block(else_) || unsugar_if(else_).is_some();
        if !differing_macro_contexts(then.span, else_.span);
        if !in_macro_or_desugar(then.span) && !in_external_macro(cx.sess, expr.span);

        // workaround for rust-lang/rust#43081
        if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;

        // this will be a span from the closing ‘}’ of the “then” block (excluding) to
        // the “if” of the “else if” block (excluding)
        let else_span = then.span.between(else_.span);

        // the snippet should look like " else \n    " with maybe comments anywhere
        // it’s bad when there is a ‘\n’ after the “else”
        if let Some(else_snippet) = snippet_opt(cx, else_span);
        if let Some(else_pos) = else_snippet.find("else");
        if else_snippet[else_pos..].contains('\n');
        let else_desc = if unsugar_if(else_).is_some() { "if" } else { "{..}" };

        then {
            span_note_and_lint(
                cx,
                SUSPICIOUS_ELSE_FORMATTING,
                else_span,
                &format!("this is an `else {}` but the formatting might hide it", else_desc),
                else_span,
                &format!(
                    "to remove this lint, remove the `else` or remove the new line between \
                     `else` and `{}`",
                    else_desc,
                ),
            );
        }
    }
}

fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
    // &, *, -
    bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
}

/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
    if let ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ExprKind::Binary(ref op, ref lhs, _) = element.node {
                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span) {
                    let space_span = lhs.span.between(op.span);
                    if let Some(space_snippet) = snippet_opt(cx, space_span) {
                        let lint_span = lhs.span.with_lo(lhs.span.hi());
                        if space_snippet.contains('\n') {
                            span_note_and_lint(
                                cx,
                                POSSIBLE_MISSING_COMMA,
                                lint_span,
                                "possibly missing a comma here",
                                lint_span,
                                "to remove this lint, add a comma or write the expr in a single line",
                            );
                        }
                    }
                }
            }
        }
    }
}

fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
    if !differing_macro_contexts(first.span, second.span)
        && !in_macro_or_desugar(first.span)
        && unsugar_if(first).is_some()
        && (is_block(second) || unsugar_if(second).is_some())
    {
        // where the else would be
        let else_span = first.span.between(second.span);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                let (looks_like, next_thing) = if unsugar_if(second).is_some() {
                    ("an `else if`", "the second `if`")
                } else {
                    ("an `else {..}`", "the next block")
                };

                span_note_and_lint(
                    cx,
                    SUSPICIOUS_ELSE_FORMATTING,
                    else_span,
                    &format!("this looks like {} but the `else` is missing", looks_like),
                    else_span,
                    &format!(
                        "to remove this lint, add the missing `else` or add a new line before {}",
                        next_thing,
                    ),
                );
            }
        }
    }
}

fn is_block(expr: &Expr) -> bool {
    if let ExprKind::Block(..) = expr.node {
        true
    } else {
        false
    }
}

/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &Expr) -> Option<(&P<Block>, &Option<P<Expr>>)> {
    match expr.node {
        ExprKind::If(_, ref then, ref else_) => Some((then, else_)),
        _ => None,
    }
}
<commit_msg>Simplify unsugar_if to just an if expr check<commit_after>use crate::utils::{differing_macro_contexts, in_macro_or_desugar, snippet_opt, span_note_and_lint};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax::ast::*;

declare_clippy_lint! {
    /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
    /// operators.
    ///
    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
    /// confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
    /// ```
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    style,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
    /// is followed immediately by a newline or the `else` seems to be missing.
    ///
    /// **Why is this bad?** This is probably some refactoring remnant, even if the
    /// code is correct, it might look confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if foo {
    /// } { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } if bar { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// { // this is the `else` block of the previous `if`, but should it be?
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// if bar { // this is the `else` block of the previous `if`, but should it be?
    /// }
    /// ```
    pub SUSPICIOUS_ELSE_FORMATTING,
    style,
    "suspicious formatting of `else`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for possible missing comma in an array. It lints if
    /// an array element is a binary operator expression and it lies on two lines.
    ///
    /// **Why is this bad?** This could lead to unexpected results.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a = &[
    ///     -1, -2, -3 // <= no comma here
    ///     -4, -5, -6
    /// ];
    /// ```
    pub POSSIBLE_MISSING_COMMA,
    correctness,
    "possible missing comma in array"
}

declare_lint_pass!(Formatting => [
    SUSPICIOUS_ASSIGNMENT_FORMATTING,
    SUSPICIOUS_ELSE_FORMATTING,
    POSSIBLE_MISSING_COMMA
]);

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&StmtKind::Expr(ref first), &StmtKind::Expr(ref second))
                | (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => {
                    check_missing_else(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        check_assign(cx, expr);
        check_else(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
    if let ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro_or_desugar(lhs.span) {
            let eq_span = lhs.span.between(rhs.span);
            if let ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = UnOp::to_string(op);
                    let eqop_span = lhs.span.between(sub_rhs.span);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(
                            cx,
                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
                            eqop_span,
                            &format!(
                                "this looks like you are trying to use `.. {op}= ..`, but you \
                                 really are doing `.. = ({op} ..)`",
                                op = op
                            ),
                            eqop_span,
                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
                        );
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
    if_chain! {
        if let ExprKind::If(_, then, Some(else_)) = &expr.node;
        if is_block(else_) || is_if(else_);
        if !differing_macro_contexts(then.span, else_.span);
        if !in_macro_or_desugar(then.span) && !in_external_macro(cx.sess, expr.span);

        // workaround for rust-lang/rust#43081
        if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;

        // this will be a span from the closing ‘}’ of the “then” block (excluding) to
        // the “if” of the “else if” block (excluding)
        let else_span = then.span.between(else_.span);

        // the snippet should look like " else \n    " with maybe comments anywhere
        // it’s bad when there is a ‘\n’ after the “else”
        if let Some(else_snippet) = snippet_opt(cx, else_span);
        if let Some(else_pos) = else_snippet.find("else");
        if else_snippet[else_pos..].contains('\n');
        let else_desc = if is_if(else_) { "if" } else { "{..}" };

        then {
            span_note_and_lint(
                cx,
                SUSPICIOUS_ELSE_FORMATTING,
                else_span,
                &format!("this is an `else {}` but the formatting might hide it", else_desc),
                else_span,
                &format!(
                    "to remove this lint, remove the `else` or remove the new line between \
                     `else` and `{}`",
                    else_desc,
                ),
            );
        }
    }
}

fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
    // &, *, -
    bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
}

/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
    if let ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ExprKind::Binary(ref op, ref lhs, _) = element.node {
                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span) {
                    let space_span = lhs.span.between(op.span);
                    if let Some(space_snippet) = snippet_opt(cx, space_span) {
                        let lint_span = lhs.span.with_lo(lhs.span.hi());
                        if space_snippet.contains('\n') {
                            span_note_and_lint(
                                cx,
                                POSSIBLE_MISSING_COMMA,
                                lint_span,
                                "possibly missing a comma here",
                                lint_span,
                                "to remove this lint, add a comma or write the expr in a single line",
                            );
                        }
                    }
                }
            }
        }
    }
}

fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
    if !differing_macro_contexts(first.span, second.span)
        && !in_macro_or_desugar(first.span)
        && is_if(first)
        && (is_block(second) || is_if(second))
    {
        // where the else would be
        let else_span = first.span.between(second.span);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                let (looks_like, next_thing) = if is_if(second) {
                    ("an `else if`", "the second `if`")
                } else {
                    ("an `else {..}`", "the next block")
                };

                span_note_and_lint(
                    cx,
                    SUSPICIOUS_ELSE_FORMATTING,
                    else_span,
                    &format!("this looks like {} but the `else` is missing", looks_like),
                    else_span,
                    &format!(
                        "to remove this lint, add the missing `else` or add a new line before {}",
                        next_thing,
                    ),
                );
            }
        }
    }
}

fn is_block(expr: &Expr) -> bool {
    if let ExprKind::Block(..) = expr.node {
        true
    } else {
        false
    }
}

/// Check if the expression is an `if` or `if let`
fn is_if(expr: &Expr) -> bool {
    if let ExprKind::If(..) = expr.node {
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use syntax::codemap::mk_sp;
use syntax::ast;
use utils::{differing_macro_contexts, in_macro, snippet_opt, span_note_and_lint};
use syntax::ptr::P;

/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.
///
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
/// ```
declare_lint! {
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    Warn,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

/// **What it does:** Checks for formatting of `else if`. It lints if the `else`
/// and `if` are not on the same line or the `else` seems to be missing.
///
/// **Why is this bad?** This is probably some refactoring remnant, even if the
/// code is correct, it might look confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// if foo {
/// } if bar { // looks like an `else` is missing here
/// }
///
/// if foo {
/// } else
///
/// if bar { // this is the `else` block of the previous `if`, but should it be?
/// }
/// ```
declare_lint! {
    pub SUSPICIOUS_ELSE_FORMATTING,
    Warn,
    "suspicious formatting of `else if`"
}

/// **What it does:** Checks for possible missing comma in an array. It lints if
/// an array element is a binary op and it lies on two lines.
///
/// **Why is this bad?** This could lead to unexpected results.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let a = &[
///     -1, -2, -3 // <= no coma here
///     -4, -5, -6
/// ];
/// ```
declare_lint! {
    pub POSSIBLE_MISSING_COMMA,
    Warn,
    "possible missing comma in array"
}

#[derive(Copy,Clone)]
pub struct Formatting;

impl LintPass for Formatting {
    fn get_lints(&self) -> LintArray {
        lint_array![SUSPICIOUS_ASSIGNMENT_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, POSSIBLE_MISSING_COMMA]
    }
}

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext, block: &ast::Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second)) |
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
                    check_consecutive_ifs(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
        check_assign(cx, expr);
        check_else_if(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext, expr: &ast::Expr) {
    if let ast::ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro(cx, lhs.span) {
            let eq_span = mk_sp(lhs.span.hi, rhs.span.lo);

            if let ast::ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = ast::UnOp::to_string(op);
                    let eqop_span = mk_sp(lhs.span.hi, sub_rhs.span.lo);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(cx,
                                           SUSPICIOUS_ASSIGNMENT_FORMATTING,
                                           eqop_span,
                                           &format!("this looks like you are trying to use `.. {op}= ..`, but you \
                                                     really are doing `.. = ({op} ..)`",
                                                    op = op),
                                           eqop_span,
                                           &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op));
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else if`.
fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
    if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
        if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(cx, then.span) {
            // this will be a span from the closing ‘}’ of the “then” block (excluding) to the
            // “if” of the “else if” block (excluding)
            let else_span = mk_sp(then.span.hi, else_.span.lo);

            // the snippet should look like " else \n    " with maybe comments anywhere
            // it’s bad when there is a ‘\n’ after the “else”
            if let Some(else_snippet) = snippet_opt(cx, else_span) {
                let else_pos = else_snippet.find("else").expect("there must be a `else` here");

                if else_snippet[else_pos..].contains('\n') {
                    span_note_and_lint(cx,
                                       SUSPICIOUS_ELSE_FORMATTING,
                                       else_span,
                                       "this is an `else if` but the formatting might hide it",
                                       else_span,
                                       "to remove this lint, remove the `else` or remove the new line between `else` \
                                        and `if`");
                }
            }
        }
    }
}

/// Implementation of the `POSSIBLE_MISSING_COMMA_IN_ARRAY_FORMATTING` lint for array
fn check_array(cx: &EarlyContext, expr: &ast::Expr) {
    if let ast::ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ast::ExprKind::Binary(ref op, ref lhs, _) = element.node {
                let space_span = mk_sp(lhs.span.hi, op.span.lo);
                if let Some(space_snippet) = snippet_opt(cx, space_span) {
                    let lint_span = mk_sp(lhs.span.hi, lhs.span.hi);
                    if space_snippet.contains('\n') {
                        span_note_and_lint(cx,
                                           POSSIBLE_MISSING_COMMA_IN_ARRAY_FORMATTING,
                                           lint_span,
                                           "possibly missing a comma here",
                                           lint_span,
                                           "to remove this lint, add a comma or write the expr in a single line");
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Expr) {
    if !differing_macro_contexts(first.span, second.span) && !in_macro(cx, first.span) &&
       unsugar_if(first).is_some() && unsugar_if(second).is_some() {
        // where the else would be
        let else_span = mk_sp(first.span.hi, second.span.lo);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                span_note_and_lint(cx,
                                   SUSPICIOUS_ELSE_FORMATTING,
                                   else_span,
                                   "this looks like an `else if` but the `else` is missing",
                                   else_span,
                                   "to remove this lint, add the missing `else` or add a new line before the second \
                                    `if`");
            }
        }
    }
}

/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
    match expr.node {
        ast::ExprKind::If(_, ref then, ref else_) |
        ast::ExprKind::IfLet(_, _, ref then, ref else_) => Some((then, else_)),
        _ => None,
    }
}
<commit_msg>Fix abbreviations in document<commit_after>use rustc::lint::*;
use syntax::codemap::mk_sp;
use syntax::ast;
use utils::{differing_macro_contexts, in_macro, snippet_opt, span_note_and_lint};
use syntax::ptr::P;

/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.
///
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
/// ```
declare_lint! {
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    Warn,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

/// **What it does:** Checks for formatting of `else if`. It lints if the `else`
/// and `if` are not on the same line or the `else` seems to be missing.
///
/// **Why is this bad?** This is probably some refactoring remnant, even if the
/// code is correct, it might look confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// if foo {
/// } if bar { // looks like an `else` is missing here
/// }
///
/// if foo {
/// } else
///
/// if bar { // this is the `else` block of the previous `if`, but should it be?
/// }
/// ```
declare_lint! {
    pub SUSPICIOUS_ELSE_FORMATTING,
    Warn,
    "suspicious formatting of `else if`"
}

/// **What it does:** Checks for possible missing comma in an array. It lints if
/// an array element is a binary operator expression and it lies on two lines.
///
/// **Why is this bad?** This could lead to unexpected results.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let a = &[
///     -1, -2, -3 // <= no coma here
///     -4, -5, -6
/// ];
/// ```
declare_lint! {
    pub POSSIBLE_MISSING_COMMA,
    Warn,
    "possible missing comma in array"
}

#[derive(Copy,Clone)]
pub struct Formatting;

impl LintPass for Formatting {
    fn get_lints(&self) -> LintArray {
        lint_array![SUSPICIOUS_ASSIGNMENT_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, POSSIBLE_MISSING_COMMA]
    }
}

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext, block: &ast::Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second)) |
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
                    check_consecutive_ifs(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
        check_assign(cx, expr);
        check_else_if(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext, expr: &ast::Expr) {
    if let ast::ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro(cx, lhs.span) {
            let eq_span = mk_sp(lhs.span.hi, rhs.span.lo);

            if let ast::ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = ast::UnOp::to_string(op);
                    let eqop_span = mk_sp(lhs.span.hi, sub_rhs.span.lo);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(cx,
                                           SUSPICIOUS_ASSIGNMENT_FORMATTING,
                                           eqop_span,
                                           &format!("this looks like you are trying to use `.. {op}= ..`, but you \
                                                     really are doing `.. = ({op} ..)`",
                                                    op = op),
                                           eqop_span,
                                           &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op));
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else if`.
fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
    if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
        if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(cx, then.span) {
            // this will be a span from the closing ‘}’ of the “then” block (excluding) to the
            // “if” of the “else if” block (excluding)
            let else_span = mk_sp(then.span.hi, else_.span.lo);

            // the snippet should look like " else \n    " with maybe comments anywhere
            // it’s bad when there is a ‘\n’ after the “else”
            if let Some(else_snippet) = snippet_opt(cx, else_span) {
                let else_pos = else_snippet.find("else").expect("there must be a `else` here");

                if else_snippet[else_pos..].contains('\n') {
                    span_note_and_lint(cx,
                                       SUSPICIOUS_ELSE_FORMATTING,
                                       else_span,
                                       "this is an `else if` but the formatting might hide it",
                                       else_span,
                                       "to remove this lint, remove the `else` or remove the new line between `else` \
                                        and `if`");
                }
            }
        }
    }
}

/// Implementation of the `POSSIBLE_MISSING_COMMA_IN_ARRAY_FORMATTING` lint for array
fn check_array(cx: &EarlyContext, expr: &ast::Expr) {
    if let ast::ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ast::ExprKind::Binary(ref op, ref lhs, _) = element.node {
                let space_span = mk_sp(lhs.span.hi, op.span.lo);
                if let Some(space_snippet) = snippet_opt(cx, space_span) {
                    let lint_span = mk_sp(lhs.span.hi, lhs.span.hi);
                    if space_snippet.contains('\n') {
                        span_note_and_lint(cx,
                                           POSSIBLE_MISSING_COMMA_IN_ARRAY_FORMATTING,
                                           lint_span,
                                           "possibly missing a comma here",
                                           lint_span,
                                           "to remove this lint, add a comma or write the expr in a single line");
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Expr) {
    if !differing_macro_contexts(first.span, second.span) && !in_macro(cx, first.span) &&
       unsugar_if(first).is_some() && unsugar_if(second).is_some() {
        // where the else would be
        let else_span = mk_sp(first.span.hi, second.span.lo);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                span_note_and_lint(cx,
                                   SUSPICIOUS_ELSE_FORMATTING,
                                   else_span,
                                   "this looks like an `else if` but the `else` is missing",
                                   else_span,
                                   "to remove this lint, add the missing `else` or add a new line before the second \
                                    `if`");
            }
        }
    }
}

/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
    match expr.node {
        ast::ExprKind::If(_, ref then, ref else_) |
        ast::ExprKind::IfLet(_, _, ref then, ref else_) => Some((then, else_)),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use serde::{de,
            Deserialize,
            Deserializer,
            Serialize,
            Serializer};
use std::{error::Error,
          str::FromStr};

/// `Serialize` and `Deserialize` a type using the `ToString` and `FromStr` traits.
pub mod string {
    use super::*;

    pub fn serialize<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error>
        where T: ToString,
              S: Serializer
    {
        s.serialize_str(&t.to_string())
    }

    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
        where T: FromStr,
              T::Err: Error,
              D: Deserializer<'de>
    {
        String::deserialize(d)?.parse().map_err(de::Error::custom)
    }
}

/// `Serialize` and `Deserialize` a type using a proxy type that implements `Serialize` and
/// `Deserialize`.
pub mod proxy {
    use super::*;

    pub fn serialize<T, U, S>(t: T, s: S) -> Result<S::Ok, S::Error>
        where T: Into<U>,
              U: Serialize,
              S: Serializer
    {
        t.into().serialize(s)
    }

    pub fn deserialize<'de, T, U, D>(d: D) -> Result<T, D::Error>
        where U: Into<T> + Deserialize<'de>,
              D: Deserializer<'de>
    {
        U::deserialize(d).map(|u| u.into())
    }
}
<commit_msg>Fix clippy<commit_after>use serde::{de,
            Deserialize,
            Deserializer,
            Serialize,
            Serializer};
use std::{error::Error,
          str::FromStr};

/// `Serialize` and `Deserialize` a type using the `ToString` and `FromStr` traits.
pub mod string {
    use super::*;

    pub fn serialize<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error>
        where T: ToString,
              S: Serializer
    {
        s.serialize_str(&t.to_string())
    }

    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
        where T: FromStr,
              T::Err: Error,
              D: Deserializer<'de>
    {
        String::deserialize(d)?.parse().map_err(de::Error::custom)
    }
}

/// `Serialize` and `Deserialize` a type using a proxy type that implements `Serialize` and
/// `Deserialize`.
pub mod proxy {
    use super::*;

    pub fn serialize<T, U, S>(t: T, s: S) -> Result<S::Ok, S::Error>
        where T: Into<U>,
              U: Serialize,
              S: Serializer
    {
        t.into().serialize(s)
    }

    pub fn deserialize<'de, T, U, D>(d: D) -> Result<T, D::Error>
        where U: Into<T> + Deserialize<'de>,
              D: Deserializer<'de>
    {
        U::deserialize(d).map(Into::into)
    }
}
<|endoftext|>