<commit_before>#![feature(libc)]
#![feature(rustc_private)]

extern crate libc;
use libc::{c_int, c_ulong, c_void};

extern crate rustc_back;

use std::io::Error;
use std::result::Result;
use std::os::unix::io::AsRawFd;

#[link(name = "c")]
extern {
    fn ioctl(fd : c_int, request : c_ulong, ...) -> c_int;
}

const IOC_WRITE: i32 = 1;
const IOC_READ:  i32 = 2;

const IOC_NRBITS:   usize = 8;
const IOC_TYPEBITS: usize = 8;
const IOC_SIZEBITS: usize = 14;

const IOC_NRSHIFT:   usize = 0us;
const IOC_TYPESHIFT: usize = IOC_NRSHIFT   + IOC_NRBITS;
const IOC_SIZESHIFT: usize = IOC_TYPESHIFT + IOC_TYPEBITS;
const IOC_DIRSHIFT:  usize = IOC_SIZESHIFT + IOC_SIZEBITS;

#[inline]
pub fn ioc(dir: i32, ty: i32, nr: i32, size: i32) -> c_ulong {
    ((dir  << IOC_DIRSHIFT)  |
    (ty   << IOC_TYPESHIFT) |
    (nr   << IOC_NRSHIFT)   |
    (size << IOC_SIZESHIFT)) as c_ulong
}

fn get_hot_data(fd: c_int) -> Result<hot_data, Error> {
    let mut result = hot_data { live: 1,
                                resv: [0,0,0],
                                temp: 0,
                                avg_delta_reads: 0,
                                avg_delta_writes: 0,
                                last_read_time: 0,
                                last_write_time: 0,
                                num_reads: 0,
                                num_writes: 0,
                                future: [0,0,0,0]};
    unsafe {
        if ioctl(fd, ioc(IOC_READ, 'f' as i32, 17, std::mem::size_of::<hot_data>() as i32), &mut result) == 0
        {
            Ok(result)
        }
        else
        {
            Err(Error::last_os_error())
        }
    }
}

#[cfg(test)]
struct HotDataFixture {
    base_dir : std::path::PathBuf,
    fs_image_file : std::fs::File,
    fs_image_dir : std::path::PathBuf
}

#[cfg(test)]
impl Drop for HotDataFixture {
    fn drop(&mut self) {
        assert!(Command::new("sudo").arg("umount").arg(&self.base_dir.as_os_str()).status().is_ok());
        assert!(std::fs::remove_dir_all(&self.base_dir).is_ok());
        assert!(std::fs::remove_dir_all(&self.fs_image_dir).is_ok());
    }
}

#[cfg(test)]
use std::process::Command;

#[cfg(test)]
impl HotDataFixture {
    fn new(mkfs_command : &str) -> HotDataFixture {
        let tmpdir = rustc_back::tempdir::TempDir::new("hot-data-test").unwrap();
        let image_path = tmpdir.path().join("btrfs-image");
        let mount_dir = tmpdir.path().join("btrfs-mount-point");

        let image_file = std::fs::File::create(&image_path).unwrap();
        assert!(image_file.set_len(1024*1024*100).is_ok());
        assert!(image_file.sync_all().is_ok());

        assert!(std::fs::create_dir(&mount_dir).is_ok());

        assert!(Command::new(mkfs_command).arg(&image_path.as_os_str()).status().is_ok());
        assert!(Command::new("sudo").arg("mount").arg("-o").arg("hot_track").arg(&image_path.as_os_str()).arg(&mount_dir.as_os_str()).status().is_ok());
        assert!(Command::new("sudo").arg("chmod").arg("777").arg(&mount_dir.as_os_str()).status().is_ok());

        HotDataFixture { base_dir : mount_dir,
                         fs_image_file : image_file,
                         fs_image_dir : tmpdir.into_path() }
    }
}

#[test]
fn ioctl_binding_returns_error() {
    match get_hot_data(33) {
        Ok(_) => panic!("ioctl unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 9) // EBADF == 9
    }
}

#[test]
fn get_hot_data_returns_unsupported() {
    let test_file = std::fs::File::open("/home/chris/.zshrc").ok().unwrap();
    match get_hot_data(test_file.as_raw_fd()) {
        Ok(_) => panic!("Call unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 95) // ENOTSUP == 95
    }
}

#[test]
fn get_hot_data_returns_no_data() {
    let fixture = HotDataFixture::new("mkfs.btrfs");

    let test_file = match std::fs::File::create(&fixture.base_dir.join("test")) {
        Ok(file) => file,
        Err(e) => panic!("Failed to create {:?}: {}", &fixture.base_dir.join("test"), e)
    };
    match get_hot_data(test_file.as_raw_fd()) {
        Ok(_) => panic!("Call unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 61) // ENODATA == 61
    }
}

#[test]
fn get_hot_data_returns_fault() {
    let fixture = HotDataFixture::new("mkfs.btrfs");

    let test_file = match std::fs::File::create(&fixture.base_dir.join("test")) {
        Ok(file) => file,
        Err(e) => panic!("Failed to create {:?}: {}", &fixture.base_dir.join("test"), e)
    };

    unsafe {
        if ioctl(test_file.as_raw_fd(), ioc(IOC_READ, 'f' as i32, 17, std::mem::size_of::<hot_data>() as i32), 0) == 0
        {
            panic!("Call unexpectedly succeeded");
        }
        else
        {
            assert_eq!(Error::last_os_error().raw_os_error().unwrap(), 14) // EFAULT == 14
        }
    }
}

#[cfg(test)]
use std::io::Write;
#[cfg(test)]
use std::io::Read;

#[test]
fn get_hot_data_has_correct_read_write_count() {
    let fixture = HotDataFixture::new("mkfs.xfs");
    let test_file_name = fixture.base_dir.join("test_file");

    {
        let mut test_file = match std::fs::File::create(&test_file_name) {
            Ok(file) => file,
            Err(e) => panic!("Failed to create {:?}: {}", &test_file_name, e)
        };
        match test_file.write_all(b"Hello, I'm a string") {
            Err(why) => panic!("Failed to write: {}", why),
            Ok(_) => {}
        }
        test_file.sync_all();
    }

    let mut hello = match std::fs::File::open(&test_file_name) {
        Ok(file) => file,
        Err(e) => panic!("Failed to open {:?}: {}", &test_file_name, e)
    };

    let mut data = String::new();
    match hello.read_to_string(&mut data) {
        Ok(_) => assert_eq!(data, "Hello, I'm a string"),
        Err(e) => panic!("Failed to read from {:?}: {}", &test_file_name, e)
    }

    let info = match get_hot_data(hello.as_raw_fd()) {
        Ok(x) => x,
        Err(why) => panic!("get_hot_data failed: {}", why)
    };

    assert_eq!(info.num_reads, 1);
    assert_eq!(info.num_writes, 1);
}

#[repr(C)]
struct hot_data {
    live : u8,
    resv : [u8; 3],
    temp : u32,
    avg_delta_reads : u64,
    avg_delta_writes : u64,
    last_read_time : u64,
    last_write_time : u64,
    num_reads : u32,
    num_writes : u32,
    future : [u64; 4]
}
<commit_msg>Test read/write count for both btrfs and xfs<commit_after>#![feature(libc)]
#![feature(rustc_private)]

extern crate libc;
use libc::{c_int, c_ulong, c_void};

extern crate rustc_back;

use std::io::Error;
use std::result::Result;
use std::os::unix::io::AsRawFd;

#[link(name = "c")]
extern {
    fn ioctl(fd : c_int, request : c_ulong, ...) -> c_int;
}

const IOC_WRITE: i32 = 1;
const IOC_READ:  i32 = 2;

const IOC_NRBITS:   usize = 8;
const IOC_TYPEBITS: usize = 8;
const IOC_SIZEBITS: usize = 14;

const IOC_NRSHIFT:   usize = 0us;
const IOC_TYPESHIFT: usize = IOC_NRSHIFT   + IOC_NRBITS;
const IOC_SIZESHIFT: usize = IOC_TYPESHIFT + IOC_TYPEBITS;
const IOC_DIRSHIFT:  usize = IOC_SIZESHIFT + IOC_SIZEBITS;

#[inline]
pub fn ioc(dir: i32, ty: i32, nr: i32, size: i32) -> c_ulong {
    ((dir  << IOC_DIRSHIFT)  |
    (ty   << IOC_TYPESHIFT) |
    (nr   << IOC_NRSHIFT)   |
    (size << IOC_SIZESHIFT)) as c_ulong
}

fn get_hot_data(fd: c_int) -> Result<hot_data, Error> {
    let mut result = hot_data { live: 1,
                                resv: [0,0,0],
                                temp: 0,
                                avg_delta_reads: 0,
                                avg_delta_writes: 0,
                                last_read_time: 0,
                                last_write_time: 0,
                                num_reads: 0,
                                num_writes: 0,
                                future: [0,0,0,0]};
    unsafe {
        if ioctl(fd, ioc(IOC_READ, 'f' as i32, 17, std::mem::size_of::<hot_data>() as i32), &mut result) == 0
        {
            Ok(result)
        }
        else
        {
            Err(Error::last_os_error())
        }
    }
}

#[cfg(test)]
struct HotDataFixture {
    base_dir : std::path::PathBuf,
    fs_image_file : std::fs::File,
    fs_image_dir : std::path::PathBuf
}

#[cfg(test)]
impl Drop for HotDataFixture {
    fn drop(&mut self) {
        assert!(Command::new("sudo").arg("umount").arg(&self.base_dir.as_os_str()).status().is_ok());
        assert!(std::fs::remove_dir_all(&self.base_dir).is_ok());
        assert!(std::fs::remove_dir_all(&self.fs_image_dir).is_ok());
    }
}

#[cfg(test)]
use std::process::Command;

#[cfg(test)]
impl HotDataFixture {
    fn new(mkfs_command : &str) -> HotDataFixture {
        let tmpdir = rustc_back::tempdir::TempDir::new("hot-data-test").unwrap();
        let image_path = tmpdir.path().join("btrfs-image");
        let mount_dir = tmpdir.path().join("btrfs-mount-point");

        let image_file = std::fs::File::create(&image_path).unwrap();
        assert!(image_file.set_len(1024*1024*100).is_ok());
        assert!(image_file.sync_all().is_ok());

        assert!(std::fs::create_dir(&mount_dir).is_ok());

        assert!(Command::new(mkfs_command).arg(&image_path.as_os_str()).status().is_ok());
        assert!(Command::new("sudo").arg("mount").arg("-o").arg("hot_track").arg(&image_path.as_os_str()).arg(&mount_dir.as_os_str()).status().is_ok());
        assert!(Command::new("sudo").arg("chmod").arg("777").arg(&mount_dir.as_os_str()).status().is_ok());

        HotDataFixture { base_dir : mount_dir,
                         fs_image_file : image_file,
                         fs_image_dir : tmpdir.into_path() }
    }
}

#[test]
fn ioctl_binding_returns_error() {
    match get_hot_data(33) {
        Ok(_) => panic!("ioctl unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 9) // EBADF == 9
    }
}

#[test]
fn get_hot_data_returns_unsupported() {
    let test_file = std::fs::File::open("/home/chris/.zshrc").ok().unwrap();
    match get_hot_data(test_file.as_raw_fd()) {
        Ok(_) => panic!("Call unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 95) // ENOTSUP == 95
    }
}

#[test]
fn get_hot_data_returns_no_data() {
    let fixture = HotDataFixture::new("mkfs.btrfs");

    let test_file = match std::fs::File::create(&fixture.base_dir.join("test")) {
        Ok(file) => file,
        Err(e) => panic!("Failed to create {:?}: {}", &fixture.base_dir.join("test"), e)
    };
    match get_hot_data(test_file.as_raw_fd()) {
        Ok(_) => panic!("Call unexpectedly succeeded"),
        Err(e) => assert_eq!(e.raw_os_error().unwrap(), 61) // ENODATA == 61
    }
}

#[test]
fn get_hot_data_returns_fault() {
    let fixture = HotDataFixture::new("mkfs.btrfs");

    let test_file = match std::fs::File::create(&fixture.base_dir.join("test")) {
        Ok(file) => file,
        Err(e) => panic!("Failed to create {:?}: {}", &fixture.base_dir.join("test"), e)
    };

    unsafe {
        if ioctl(test_file.as_raw_fd(), ioc(IOC_READ, 'f' as i32, 17, std::mem::size_of::<hot_data>() as i32), 0) == 0
        {
            panic!("Call unexpectedly succeeded");
        }
        else
        {
            assert_eq!(Error::last_os_error().raw_os_error().unwrap(), 14) // EFAULT == 14
        }
    }
}

#[cfg(test)]
use std::io::Write;
#[cfg(test)]
use std::io::Read;

#[cfg(test)]
fn get_hot_data_has_correct_read_write_count(fixture : &HotDataFixture) {
    let test_file_name = fixture.base_dir.join("test_file");

    {
        let mut test_file = match std::fs::File::create(&test_file_name) {
            Ok(file) => file,
            Err(e) => panic!("Failed to create {:?}: {}", &test_file_name, e)
        };
        match test_file.write_all(b"Hello, I'm a string") {
            Err(why) => panic!("Failed to write: {}", why),
            Ok(_) => {}
        }
        test_file.sync_all();
    }

    let mut hello = match std::fs::File::open(&test_file_name) {
        Ok(file) => file,
        Err(e) => panic!("Failed to open {:?}: {}", &test_file_name, e)
    };

    let mut data = String::new();
    match hello.read_to_string(&mut data) {
        Ok(_) => assert_eq!(data, "Hello, I'm a string"),
        Err(e) => panic!("Failed to read from {:?}: {}", &test_file_name, e)
    }

    let info = match get_hot_data(hello.as_raw_fd()) {
        Ok(x) => x,
        Err(why) => panic!("get_hot_data failed: {}", why)
    };

    assert_eq!(info.num_reads, 1);
    assert_eq!(info.num_writes, 1);
}

#[test]
fn btrfs_has_correct_read_write_count() {
    get_hot_data_has_correct_read_write_count(&HotDataFixture::new("mkfs.btrfs"));
}

#[test]
fn xfs_has_correct_read_write_count() {
    get_hot_data_has_correct_read_write_count(&HotDataFixture::new("mkfs.xfs"));
}

#[repr(C)]
struct hot_data {
    live : u8,
    resv : [u8; 3],
    temp : u32,
    avg_delta_reads : u64,
    avg_delta_writes : u64,
    last_read_time : u64,
    last_write_time : u64,
    num_reads : u32,
    num_writes : u32,
    future : [u64; 4]
}
<|endoftext|>


<commit_before>extern crate metal_sys;
extern crate cocoa;

use cocoa::foundation::NSUInteger;

mod device;

pub mod sys {
    pub use metal_sys::*;
}

pub use sys::MTLClearColor as ClearColor;
pub use sys::MTLOrigin as Origin;
pub use sys::MTLRegion as Region;
pub use sys::MTLScissorRect as ScissorRect;
pub use sys::MTLSize as Size;
pub use sys::MTLViewport as Viewport;
pub use sys::MTLDrawPrimitivesIndirectArguments as DrawPrimitivesIndirectArguments;
pub use sys::MTLDrawIndexedPrimitivesIndirectArguments as DrawIndexedPrimitivesIndirectArguments;
pub use sys::MTLDispatchThreadgroupsIndirectArguments as DispatchThreadgroupsIndirectArguments;

pub use device::Device;

pub fn clear_color(red: f64, green: f64, blue: f64, alpha: f64) -> ClearColor {
    sys::MTLClearColorMake(red, green, blue, alpha)
}

pub fn origin(x: usize, y: usize, z: usize) -> Origin {
    sys::MTLOriginMake(x as NSUInteger, y as NSUInteger, z as NSUInteger)
}

pub fn region_1d(x: usize, width: usize) -> Region {
    sys::MTLRegionMake1D(x as NSUInteger, width as NSUInteger)
}

pub fn region_2d(x: usize, y: usize, width: usize, height: usize) -> Region {
    sys::MTLRegionMake2D(x as NSUInteger, y as NSUInteger, width as NSUInteger, height as NSUInteger)
}

pub fn region_3d(x: usize, y: usize, z: usize, width: usize, height: usize, depth: usize) -> Region {
    sys::MTLRegionMake3D(x as NSUInteger,
                         y as NSUInteger,
                         z  as NSUInteger,
                         width as NSUInteger,
                         height as NSUInteger,
                         depth as NSUInteger)
}

 pub fn size(width: usize, height: usize, depth: usize) -> Size {
     sys::MTLSizeMake(width as NSUInteger, height as NSUInteger, depth as NSUInteger)
 }
<commit_msg>Need to add feature(cstring_into, cstr_memory2) for Device::name<commit_after>#![feature(cstring_into, cstr_memory2)]

extern crate metal_sys;
extern crate cocoa;

use cocoa::foundation::NSUInteger;

mod device;

pub mod sys {
    pub use metal_sys::*;
}

pub use sys::MTLClearColor as ClearColor;
pub use sys::MTLOrigin as Origin;
pub use sys::MTLRegion as Region;
pub use sys::MTLScissorRect as ScissorRect;
pub use sys::MTLSize as Size;
pub use sys::MTLViewport as Viewport;
pub use sys::MTLDrawPrimitivesIndirectArguments as DrawPrimitivesIndirectArguments;
pub use sys::MTLDrawIndexedPrimitivesIndirectArguments as DrawIndexedPrimitivesIndirectArguments;
pub use sys::MTLDispatchThreadgroupsIndirectArguments as DispatchThreadgroupsIndirectArguments;

pub use device::Device;

pub fn clear_color(red: f64, green: f64, blue: f64, alpha: f64) -> ClearColor {
    sys::MTLClearColorMake(red, green, blue, alpha)
}

pub fn origin(x: usize, y: usize, z: usize) -> Origin {
    sys::MTLOriginMake(x as NSUInteger, y as NSUInteger, z as NSUInteger)
}

pub fn region_1d(x: usize, width: usize) -> Region {
    sys::MTLRegionMake1D(x as NSUInteger, width as NSUInteger)
}

pub fn region_2d(x: usize, y: usize, width: usize, height: usize) -> Region {
    sys::MTLRegionMake2D(x as NSUInteger, y as NSUInteger, width as NSUInteger, height as NSUInteger)
}

pub fn region_3d(x: usize, y: usize, z: usize, width: usize, height: usize, depth: usize) -> Region {
    sys::MTLRegionMake3D(x as NSUInteger,
                         y as NSUInteger,
                         z  as NSUInteger,
                         width as NSUInteger,
                         height as NSUInteger,
                         depth as NSUInteger)
}

 pub fn size(width: usize, height: usize, depth: usize) -> Size {
     sys::MTLSizeMake(width as NSUInteger, height as NSUInteger, depth as NSUInteger)
 }
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    match words.next() {
                        Some("branch.oid") => {
                            match words.next() {
                                Some(oid) => s.oid = String::from(oid),
                                None => return Err("Bad input"),
                            }
                        },
                        Some("branch.head") => {
                            match words.next() {
                                Some(branch) => s.branch = String::from(branch),
                                None => return Err("Bad input"),
                            }
                        }
                        Some("branch.upstream") => {
                            match words.next() {
                                Some(upstream) => s.upstream = Some(String::from(upstream)),
                                None => return Err("Bad input"),
                            }
                        },
                        Some("branch.ab") => {
                            match words.next() {
                                Some("+0") => {},
                                Some(_) => s.ahead = true,
                                None => return Err("Bad input"),
                            }
                            match words.next() {
                                Some("-0") => {},
                                Some(_) => s.behind = true,
                                None => return Err("Bad input"),
                            }
                        },
                        _ => return Err("Bad input"),
                    }
                },
                Some("1") | Some("2") => {
                    if let Some(changes) = words.next() {
                        let mut changes = changes.chars();
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.staged = true,
                            None => return Err("Bad input"),
                        }
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.unstaged = true,
                            None => return Err("Bad input"),
                        }
                    }
                },
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err("Bad input"),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let mut line = format!("[{} {}", self.branch, &self.oid[..7]);
        if self.ahead || self.behind || self.staged || self.unstaged || self.untracked || self.ignored {
            line.push(' ');
        }
        if self.ahead {
            line.push('A');
        }
        if self.behind {
            line.push('B');
        }
        if self.staged {
            line.push('+');
        }
        if self.unstaged {
            line.push('*');
        }
        if self.untracked {
            line.push('?');
        }
        if self.ignored {
            line.push('!');
        }
        line.push_str("] ");
        line
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a] "));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*?!] "));
    }
}
<commit_msg>Don't abbreviate oid if initial<commit_after>#[derive(Debug, PartialEq, Eq)]
pub struct GitStatus {
    oid: String,
    branch: String,
    upstream: Option<String>,
    ahead: bool,
    behind: bool,
    staged: bool,
    unstaged: bool,
    untracked: bool,
    ignored: bool,
}

impl GitStatus {
    pub fn new(status_txt: &str) -> Result<GitStatus, &'static str> {
        let mut s = GitStatus {
            oid: String::new(),
            branch: String::new(),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        for line in status_txt.lines() {
            let mut words = line.split(' ');
            match words.next() {
                Some("#") => {
                    match words.next() {
                        Some("branch.oid") => {
                            match words.next() {
                                Some(oid) => s.oid = String::from(oid),
                                None => return Err("Bad input"),
                            }
                        },
                        Some("branch.head") => {
                            match words.next() {
                                Some(branch) => s.branch = String::from(branch),
                                None => return Err("Bad input"),
                            }
                        }
                        Some("branch.upstream") => {
                            match words.next() {
                                Some(upstream) => s.upstream = Some(String::from(upstream)),
                                None => return Err("Bad input"),
                            }
                        },
                        Some("branch.ab") => {
                            match words.next() {
                                Some("+0") => {},
                                Some(_) => s.ahead = true,
                                None => return Err("Bad input"),
                            }
                            match words.next() {
                                Some("-0") => {},
                                Some(_) => s.behind = true,
                                None => return Err("Bad input"),
                            }
                        },
                        _ => return Err("Bad input"),
                    }
                },
                Some("1") | Some("2") => {
                    if let Some(changes) = words.next() {
                        let mut changes = changes.chars();
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.staged = true,
                            None => return Err("Bad input"),
                        }
                        match changes.next() {
                            Some('.') => {},
                            Some(_) => s.unstaged = true,
                            None => return Err("Bad input"),
                        }
                    }
                },
                Some("?") => s.untracked = true,
                Some("!") => s.ignored = true,
                Some("") | None => {},
                _ => return Err("Bad input"),
            }
        }
        Ok(s)
    }

    pub fn to_line(&self) -> String {
        let short_oid = if self.oid == "(initial)" {
            &self.oid
        } else {
            &self.oid[..7]
        };
        let mut line = format!("[{} {}", self.branch, short_oid);
        if self.ahead || self.behind || self.staged || self.unstaged || self.untracked || self.ignored {
            line.push(' ');
        }
        if self.ahead {
            line.push('A');
        }
        if self.behind {
            line.push('B');
        }
        if self.staged {
            line.push('+');
        }
        if self.unstaged {
            line.push('*');
        }
        if self.untracked {
            line.push('?');
        }
        if self.ignored {
            line.push('!');
        }
        line.push_str("] ");
        line
    }
}

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

    #[test]
    fn parse_clean() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: None,
                ahead: false,
                behind: false,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_clean_upstream() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: false,
                unstaged: false,
                untracked: false,
                ignored: false,
            }
        );
    }

    #[test]
    fn parse_staged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 M. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_unstaged_only() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 .M N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(!s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_both_staged_and_unstaged() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 MM N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 311c77295c5b6056f4599c2b8d0a019d4c76746a README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(s.unstaged);
    }

    #[test]
    fn parse_deleted() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_renamed() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
2 R. N... 100644 100644 100644 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 R100 README	README.md
";
        let s = GitStatus::new(test_status).unwrap();
        assert!(s.staged);
        assert!(!s.unstaged);
    }

    #[test]
    fn parse_untracked() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
? foo.txt
";
        assert!(GitStatus::new(test_status).unwrap().untracked);
    }

    #[test]
    fn parse_ignored() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
! node_modules/
";
        assert!(GitStatus::new(test_status).unwrap().ignored);
    }

    #[test]
    fn parse_very_dirty() {
        let test_status = "\
# branch.oid 3845e7a3c3aadaaebb2d1b261bf07a9357d35a79
# branch.head master
# branch.upstream origin/master
# branch.ab +1 -1
1 D. N... 100644 000000 000000 1290f45e7ad7575848a436d8febbd6c4ba07f1f3 0000000000000000000000000000000000000000 README.md
1 .M N... 100644 100644 100644 5e8a8090976077ddf16252a560460a20dbbdd6a5 5e8a8090976077ddf16252a560460a20dbbdd6a5 gh-pages.sh
? foo.txt
! ignored.txt
";
        assert_eq!(
            GitStatus::new(test_status).unwrap(),
            GitStatus {
                oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
                branch: String::from("master"),
                upstream: Some(String::from("origin/master")),
                ahead: true,
                behind: true,
                staged: true,
                unstaged: true,
                untracked: true,
                ignored: true,
            }
        );
    }

    #[test]
    fn parse_bad_input() {
        assert_eq!(GitStatus::new("foo"), Err("Bad input"));
    }

    #[test]
    fn test_to_line_clean() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a] "));
    }

    #[test]
    fn test_to_line_dirty() {
        let s = GitStatus {
            oid: String::from("3845e7a3c3aadaaebb2d1b261bf07a9357d35a79"),
            branch: String::from("master"),
            upstream: Some(String::from("origin/master")),
            ahead: true,
            behind: true,
            staged: true,
            unstaged: true,
            untracked: true,
            ignored: true,
        };
        assert_eq!(s.to_line(), String::from("[master 3845e7a AB+*?!] "));
    }

    #[test]
    fn test_to_line_initial_commit() {
        let s = GitStatus {
            oid: String::from("(initial)"),
            branch: String::from("master"),
            upstream: None,
            ahead: false,
            behind: false,
            staged: false,
            unstaged: false,
            untracked: false,
            ignored: false,
        };
        assert_eq!(s.to_line(), String::from("[master (initial)] "));
    }
}
<|endoftext|>


<commit_before>extern crate nom_lua53;

use nom_lua53::{parse_all, ParseResult, Statement, Exp};
use nom_lua53::op::BinOp;

#[derive(PartialEq,Debug)]
pub enum LuaValue {
    Nil,
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

#[derive(PartialEq,Eq,Debug)]
pub enum LuaError {
    TypeError(String),
    ArithmeticError(String),
}

use LuaError::*;

type Result<T> = std::result::Result<T, LuaError>;

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_arithmetic(left_op: &Box<Exp>, right_op: &Box<Exp>,
                   integer: fn(isize, isize) -> Result<LuaValue>,
                   float: fn(f64, f64) -> Result<LuaValue>) -> Result<LuaValue> {

    let left_op = eval_expr(&left_op)?;
    let right_op = eval_expr(&right_op)?;

    match left_op {
        LuaValue::Integer(i) => match right_op {
            LuaValue::Integer(j) => integer(i, j),
            LuaValue::Float(j) => float(i as f64, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        LuaValue::Float(i) => match right_op {
            LuaValue::Integer(j) => float(i, j as f64),
            LuaValue::Float(j) => float(i, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
    }
}

fn eval_binary_expr(left_op: &Box<Exp>, right_op: &Box<Exp>, operator: &BinOp) -> Result<LuaValue> {
    // We cannot yet evaluate the operands as some binary operators are used to shortcut
    // evaluation.
    match *operator {
        BinOp::Plus => eval_arithmetic(left_op, right_op,
                                       |i, j| Ok(LuaValue::Integer(i+j)),
                                       |i, j| Ok(LuaValue::Float(i+j))),
        BinOp::Minus => eval_arithmetic(left_op, right_op,
                                        |i, j| Ok(LuaValue::Integer(i-j)),
                                        |i, j| Ok(LuaValue::Float(i-j))),
        BinOp::Mul => eval_arithmetic(left_op, right_op,
                                      |i, j| Ok(LuaValue::Integer(i*j)),
                                      |i, j| Ok(LuaValue::Float(i*j))),
        BinOp::Div => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float((i as f64)/(j as f64)))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i/j))
                                      }),
        BinOp::Mod => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Integer(i % j))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i - (i/j).floor()*j))
                                      }),
        BinOp::IntDiv => eval_arithmetic(left_op, right_op,
                                         |i, j| if j == 0 {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer(i/j))
                                         },
                                         |i, j| if j == 0. {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer((i/j).floor() as isize))
                                         }),
        BinOp::Pow => eval_arithmetic(left_op, right_op,
                                         |i, j| Ok(LuaValue::Float((i as f64).powf(j as f64))),
                                         |i, j| Ok(LuaValue::Float(i.powf(j)))),
        _ => Ok(LuaValue::Nil)
    }
}

fn eval_expr(expr: &Exp) -> Result<LuaValue> {
    match *expr {
        Exp::Nil => Ok(LuaValue::Nil),
        Exp::Bool(val) => Ok(LuaValue::Boolean(val)),
        Exp::Num(val) => Ok(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::BinExp(ref left, ref op, ref right) => eval_binary_expr(left, right, &op),
        Exp::Str(ref s) => Ok(LuaValue::Str(lit_to_string(s))),
        _ => Ok(LuaValue::Nil),
    }
}

pub fn eval_file(input: &[u8]) {
    match parse_all(input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {
                            let computed_value = eval_expr(val);
                            println!("Assigning {:?} to {:?}", computed_value, var);
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}

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

    use nom_lua53::{parse_all, ParseResult, Statement, Exp};
    use nom_lua53::num::Numeral;
    use nom_lua53::op::BinOp;

    #[test]
    fn test_addition() {
        // 1. + -1. == 0.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.0))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).unwrap();
        assert_eq!(res, LuaValue::Float(0.));

        // 1 + -1. == 0.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).unwrap();
        assert_eq!( res, LuaValue::Float(0.));
        // 1 + 3 == 4
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Plus).unwrap();
        assert_eq!(res, LuaValue::Integer(4));
    }

    #[test]
    fn test_arithmetic_types() {
        eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).expect("Type error!");

        eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.))), &Box::new(Exp::Num(Numeral::Int(-1))), &BinOp::Plus).expect("Type error!");

        let res = eval_binary_expr(&Box::new(Exp::Bool(true)), &Box::new(Exp::Num(Numeral::Int(-1))), &BinOp::Plus).expect_err("No type error, there should be one!");
        assert!(match res { TypeError(_) => true, _ => false })
    }

    #[test]
    fn test_substraction() {
        // 1. - -1. == 2.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.0))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Float(2.));

        // 1 - -1. == 2.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Float(2.));

        // 1 - 3 == -2
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Integer(-2));
    }

    #[test]
    fn test_multiplication() {
        // 1.5 * 2.5. == 3.75.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.5))), &Box::new(Exp::Num(Numeral::Float(2.5))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Float(3.75));

        // -2 * 1.25 == -2.5.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(-2))), &Box::new(Exp::Num(Numeral::Float(1.25))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Float(-2.5));

        // 10 * 3 == 30
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(10))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Integer(30));
    }

    #[test]
    fn test_division() {
        // 1.5 / 0.5. == 3.0
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.5))), &Box::new(Exp::Num(Numeral::Float(0.5))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(3.));

        // 3 / -2. == -1.5
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(3))), &Box::new(Exp::Num(Numeral::Float(-2.0))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(-1.5));

        // 3 / 2 == 1.5
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(3))), &Box::new(Exp::Num(Numeral::Int(2))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(1.5));
    }
}
<commit_msg>More expect -> unwrap conversions<commit_after>extern crate nom_lua53;

use nom_lua53::{parse_all, ParseResult, Statement, Exp};
use nom_lua53::op::BinOp;

#[derive(PartialEq,Debug)]
pub enum LuaValue {
    Nil,
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

#[derive(PartialEq,Eq,Debug)]
pub enum LuaError {
    TypeError(String),
    ArithmeticError(String),
}

use LuaError::*;

type Result<T> = std::result::Result<T, LuaError>;

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_arithmetic(left_op: &Box<Exp>, right_op: &Box<Exp>,
                   integer: fn(isize, isize) -> Result<LuaValue>,
                   float: fn(f64, f64) -> Result<LuaValue>) -> Result<LuaValue> {

    let left_op = eval_expr(&left_op)?;
    let right_op = eval_expr(&right_op)?;

    match left_op {
        LuaValue::Integer(i) => match right_op {
            LuaValue::Integer(j) => integer(i, j),
            LuaValue::Float(j) => float(i as f64, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        LuaValue::Float(i) => match right_op {
            LuaValue::Integer(j) => float(i, j as f64),
            LuaValue::Float(j) => float(i, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
    }
}

fn eval_binary_expr(left_op: &Box<Exp>, right_op: &Box<Exp>, operator: &BinOp) -> Result<LuaValue> {
    // We cannot yet evaluate the operands as some binary operators are used to shortcut
    // evaluation.
    match *operator {
        BinOp::Plus => eval_arithmetic(left_op, right_op,
                                       |i, j| Ok(LuaValue::Integer(i+j)),
                                       |i, j| Ok(LuaValue::Float(i+j))),
        BinOp::Minus => eval_arithmetic(left_op, right_op,
                                        |i, j| Ok(LuaValue::Integer(i-j)),
                                        |i, j| Ok(LuaValue::Float(i-j))),
        BinOp::Mul => eval_arithmetic(left_op, right_op,
                                      |i, j| Ok(LuaValue::Integer(i*j)),
                                      |i, j| Ok(LuaValue::Float(i*j))),
        BinOp::Div => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float((i as f64)/(j as f64)))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i/j))
                                      }),
        BinOp::Mod => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Integer(i % j))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i - (i/j).floor()*j))
                                      }),
        BinOp::IntDiv => eval_arithmetic(left_op, right_op,
                                         |i, j| if j == 0 {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer(i/j))
                                         },
                                         |i, j| if j == 0. {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer((i/j).floor() as isize))
                                         }),
        BinOp::Pow => eval_arithmetic(left_op, right_op,
                                         |i, j| Ok(LuaValue::Float((i as f64).powf(j as f64))),
                                         |i, j| Ok(LuaValue::Float(i.powf(j)))),
        _ => Ok(LuaValue::Nil)
    }
}

fn eval_expr(expr: &Exp) -> Result<LuaValue> {
    match *expr {
        Exp::Nil => Ok(LuaValue::Nil),
        Exp::Bool(val) => Ok(LuaValue::Boolean(val)),
        Exp::Num(val) => Ok(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::BinExp(ref left, ref op, ref right) => eval_binary_expr(left, right, &op),
        Exp::Str(ref s) => Ok(LuaValue::Str(lit_to_string(s))),
        _ => Ok(LuaValue::Nil),
    }
}

pub fn eval_file(input: &[u8]) {
    match parse_all(input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {
                            let computed_value = eval_expr(val);
                            println!("Assigning {:?} to {:?}", computed_value, var);
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}

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

    use nom_lua53::{parse_all, ParseResult, Statement, Exp};
    use nom_lua53::num::Numeral;
    use nom_lua53::op::BinOp;

    #[test]
    fn test_addition() {
        // 1. + -1. == 0.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.0))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).unwrap();
        assert_eq!(res, LuaValue::Float(0.));

        // 1 + -1. == 0.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).unwrap();
        assert_eq!( res, LuaValue::Float(0.));
        // 1 + 3 == 4
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Plus).unwrap();
        assert_eq!(res, LuaValue::Integer(4));
    }

    #[test]
    fn test_arithmetic_types() {
        eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Plus).unwrap();

        eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.))), &Box::new(Exp::Num(Numeral::Int(-1))), &BinOp::Plus).unwrap();

        let res = eval_binary_expr(&Box::new(Exp::Bool(true)), &Box::new(Exp::Num(Numeral::Int(-1))), &BinOp::Plus).unwrap_err();
        assert!(match res { TypeError(_) => true, _ => false })
    }

    #[test]
    fn test_substraction() {
        // 1. - -1. == 2.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.0))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Float(2.));

        // 1 - -1. == 2.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Float(-1.0))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Float(2.));

        // 1 - 3 == -2
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(1))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Minus).unwrap();
        assert_eq!(res, LuaValue::Integer(-2));
    }

    #[test]
    fn test_multiplication() {
        // 1.5 * 2.5. == 3.75.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.5))), &Box::new(Exp::Num(Numeral::Float(2.5))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Float(3.75));

        // -2 * 1.25 == -2.5.
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(-2))), &Box::new(Exp::Num(Numeral::Float(1.25))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Float(-2.5));

        // 10 * 3 == 30
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(10))), &Box::new(Exp::Num(Numeral::Int(3))), &BinOp::Mul).unwrap();
        assert_eq!(res, LuaValue::Integer(30));
    }

    #[test]
    fn test_division() {
        // 1.5 / 0.5. == 3.0
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Float(1.5))), &Box::new(Exp::Num(Numeral::Float(0.5))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(3.));

        // 3 / -2. == -1.5
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(3))), &Box::new(Exp::Num(Numeral::Float(-2.0))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(-1.5));

        // 3 / 2 == 1.5
        let res = eval_binary_expr(&Box::new(Exp::Num(Numeral::Int(3))), &Box::new(Exp::Num(Numeral::Int(2))), &BinOp::Div).unwrap();
        assert_eq!(res, LuaValue::Float(1.5));
    }
}
<|endoftext|>


<commit_before>//! The enum `Either`.

use std::io::{self, Write, Read, BufRead};
use std::convert::{AsRef, AsMut};
use std::ops::Deref;
use std::ops::DerefMut;

pub use Either::{Left, Right};

/// `Either` represents an alternative holding one value out of
/// either of the two possible values.
///
/// `Either` is a general purpose sum type of two parts. For representing
/// success or error, use the regular `Result<T, E>` instead.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Either<L, R> {
    /// A value of type `L`.
    Left(L),
    /// A value of type `R`.
    Right(R),
}

macro_rules! either {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref $inner) => $result,
            Either::Right(ref $inner) => $result,
        }
    )
}

macro_rules! either_mut {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref mut $inner) => $result,
            Either::Right(ref mut $inner) => $result,
        }
    )
}

impl<L, R> Either<L, R> {
    pub fn is_left(&self) -> bool {
        match *self {
            Left(_) => true,
            Right(_) => false,
        }
    }

    pub fn is_right(&self) -> bool {
        !self.is_left()
    }

    pub fn left(self) -> Option<L> {
        match self {
            Left(l) => Some(l),
            Right(_) => None,
        }
    }

    pub fn right(self) -> Option<R> {
        match self {
            Left(_) => None,
            Right(r) => Some(r),
        }
    }

    pub fn as_ref(&self) -> Either<&L, &R> {
        match *self {
            Left(ref inner) => Left(inner),
            Right(ref inner) => Right(inner),
        }
    }

    pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
        match *self {
            Left(ref mut inner) => Left(inner),
            Right(ref mut inner) => Right(inner),
        }
    }

    pub fn flip(self) -> Either<R, L> {
        match self {
            Left(l) => Right(l),
            Right(r) => Left(r),
        }
    }
}

/// Convert from `Result` to `Either` with `Ok => Right` and `Err => Left`.
impl<L, R> From<Result<R, L>> for Either<L, R> {
    fn from(r: Result<R, L>) -> Self {
        match r {
            Err(e) => Left(e),
            Ok(o) => Right(o),
        }
    }
}

/// Convert from `Either` to `Result` with `Right => Ok` and `Left => Err`.
impl<L, R> Into<Result<R, L>> for Either<L, R> {
    fn into(self) -> Result<R, L> {
        match self {
            Left(l) => Err(l),
            Right(r) => Ok(r),
        }
    }
}

/// `Either<L, R>` is an iterator if both `L` and `R` are iterators.
impl<L, R> Iterator for Either<L, R>
    where L: Iterator, R: Iterator<Item=L::Item>
{
    type Item = L::Item;

    fn next(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        either!(*self, inner => inner.size_hint())
    }
}

impl<L, R> DoubleEndedIterator for Either<L, R>
    where L: DoubleEndedIterator, R: DoubleEndedIterator<Item=L::Item>
{
    fn next_back(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next_back())
    }
}

impl<L, R> ExactSizeIterator for Either<L, R>
    where L: ExactSizeIterator, R: ExactSizeIterator<Item=L::Item>
{
}

/// `Either<L, R>` implements `Read` if both `L` and `R` do.
impl<L, R> Read for Either<L, R>
    where L: Read, R: Read
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read_to_end(buf))
    }
}

impl<L, R> BufRead for Either<L, R>
    where L: BufRead, R: BufRead
{
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        either_mut!(*self, inner => inner.fill_buf())
    }

    fn consume(&mut self, amt: usize) {
        either_mut!(*self, inner => inner.consume(amt))
    }
}

/// `Either<L, R>` implements `Write` if both `L` and `R` do.
impl<L, R> Write for Either<L, R>
    where L: Write, R: Write
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        either_mut!(*self, inner => inner.flush())
    }
}

impl<L, R, Target> AsRef<Target> for Either<L, R>
    where L: AsRef<Target>, R: AsRef<Target>
{
    fn as_ref(&self) -> &Target {
        either!(*self, inner => inner.as_ref())
    }
}

impl<L, R, Target> AsMut<Target> for Either<L, R>
    where L: AsMut<Target>, R: AsMut<Target>
{
    fn as_mut(&mut self) -> &mut Target {
        either_mut!(*self, inner => inner.as_mut())
    }
}

impl<L, R> Deref for Either<L, R>
    where L: Deref, R: Deref<Target=L::Target>
{
    type Target = L::Target;

    fn deref(&self) -> &Self::Target {
        either!(*self, inner => &*inner)
    }
}

impl<L, R> DerefMut for Either<L, R>
    where L: DerefMut, R: DerefMut<Target=L::Target>
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        either_mut!(*self, inner => &mut *inner)
    }
}

#[test]
fn basic() {
    let mut e = Left(2);
    let r = Right(2);
    assert_eq!(e, Left(2));
    e = r;
    assert_eq!(e, Right(2));
    assert_eq!(e.left(), None);
    assert_eq!(e.right(), Some(2));
    assert_eq!(e.as_ref().right(), Some(&2));
    assert_eq!(e.as_mut().right(), Some(&mut 2));
}

#[test]
fn deref() {
    fn is_str(_: &str) {}
    let value: Either<String, &str> = Left(String::from("test"));
    is_str(&*value);
}

#[test]
fn iter() {
    let x = 3;
    let mut iter = match x {
        1...3 => Left(0..10),
        _ => Right(17..),
    };

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.count(), 9);
}

#[test]
fn read_write() {
    use std::io;

    let use_stdio = false;
    let mockdata = [0xff; 256];

    let mut reader = if use_stdio {
        Left(io::stdin())
    } else {
        Right(&mockdata[..])
    };

    let mut buf = [0u8; 16];
    assert_eq!(reader.read(&mut buf).unwrap(), buf.len());
    assert_eq!(&buf, &mockdata[..buf.len()]);

    let mut mockbuf = [0u8; 256];
    let mut writer = if use_stdio {
        Left(io::stdout())
    } else {
        Right(&mut mockbuf[..])
    };

    let buf = [1u8; 16];
    assert_eq!(writer.write(&buf).unwrap(), buf.len());
}
<commit_msg>Add unwrapping macros that might fail early<commit_after>//! The enum `Either`.

use std::io::{self, Write, Read, BufRead};
use std::convert::{AsRef, AsMut};
use std::ops::Deref;
use std::ops::DerefMut;

pub use Either::{Left, Right};

/// `Either` represents an alternative holding one value out of
/// either of the two possible values.
///
/// `Either` is a general purpose sum type of two parts. For representing
/// success or error, use the regular `Result<T, E>` instead.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Either<L, R> {
    /// A value of type `L`.
    Left(L),
    /// A value of type `R`.
    Right(R),
}

macro_rules! either {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref $inner) => $result,
            Either::Right(ref $inner) => $result,
        }
    )
}

macro_rules! either_mut {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref mut $inner) => $result,
            Either::Right(ref mut $inner) => $result,
        }
    )
}

/// Helper macro for unwrapping the left side of an `Either` while returning
/// early with the right side if there is no left one. Can only be used in
/// functions that return `Either` because of the early return of `Right` that
/// it provides.
#[macro_export]
macro_rules! left {
    ($expr:expr) => (
        match $expr {
            Either::Left(val) => val,
            Either::Right(err) => return Either::Right(From::from(err))
        }
    )
}

/// Helper macro for unwrapping the right side of an `Either` while returning
/// early with the left side if there is no right one. Can only be used in
/// functions that return `Either` because of the early return of `Left` that
/// it provides.
#[macro_export]
macro_rules! right {
    ($expr:expr) => (
        match $expr {
            Either::Left(err) => return Either::Left(From::from(err)),
            Either::Right(val) => val
        }
    )
}

impl<L, R> Either<L, R> {
    pub fn is_left(&self) -> bool {
        match *self {
            Left(_) => true,
            Right(_) => false,
        }
    }

    pub fn is_right(&self) -> bool {
        !self.is_left()
    }

    pub fn left(self) -> Option<L> {
        match self {
            Left(l) => Some(l),
            Right(_) => None,
        }
    }

    pub fn right(self) -> Option<R> {
        match self {
            Left(_) => None,
            Right(r) => Some(r),
        }
    }

    pub fn as_ref(&self) -> Either<&L, &R> {
        match *self {
            Left(ref inner) => Left(inner),
            Right(ref inner) => Right(inner),
        }
    }

    pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
        match *self {
            Left(ref mut inner) => Left(inner),
            Right(ref mut inner) => Right(inner),
        }
    }

    pub fn flip(self) -> Either<R, L> {
        match self {
            Left(l) => Right(l),
            Right(r) => Left(r),
        }
    }
}

/// Convert from `Result` to `Either` with `Ok => Right` and `Err => Left`.
impl<L, R> From<Result<R, L>> for Either<L, R> {
    fn from(r: Result<R, L>) -> Self {
        match r {
            Err(e) => Left(e),
            Ok(o) => Right(o),
        }
    }
}

/// Convert from `Either` to `Result` with `Right => Ok` and `Left => Err`.
impl<L, R> Into<Result<R, L>> for Either<L, R> {
    fn into(self) -> Result<R, L> {
        match self {
            Left(l) => Err(l),
            Right(r) => Ok(r),
        }
    }
}

/// `Either<L, R>` is an iterator if both `L` and `R` are iterators.
impl<L, R> Iterator for Either<L, R>
    where L: Iterator, R: Iterator<Item=L::Item>
{
    type Item = L::Item;

    fn next(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        either!(*self, inner => inner.size_hint())
    }
}

impl<L, R> DoubleEndedIterator for Either<L, R>
    where L: DoubleEndedIterator, R: DoubleEndedIterator<Item=L::Item>
{
    fn next_back(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next_back())
    }
}

impl<L, R> ExactSizeIterator for Either<L, R>
    where L: ExactSizeIterator, R: ExactSizeIterator<Item=L::Item>
{
}

/// `Either<L, R>` implements `Read` if both `L` and `R` do.
impl<L, R> Read for Either<L, R>
    where L: Read, R: Read
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read_to_end(buf))
    }
}

impl<L, R> BufRead for Either<L, R>
    where L: BufRead, R: BufRead
{
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        either_mut!(*self, inner => inner.fill_buf())
    }

    fn consume(&mut self, amt: usize) {
        either_mut!(*self, inner => inner.consume(amt))
    }
}

/// `Either<L, R>` implements `Write` if both `L` and `R` do.
impl<L, R> Write for Either<L, R>
    where L: Write, R: Write
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        either_mut!(*self, inner => inner.flush())
    }
}

impl<L, R, Target> AsRef<Target> for Either<L, R>
    where L: AsRef<Target>, R: AsRef<Target>
{
    fn as_ref(&self) -> &Target {
        either!(*self, inner => inner.as_ref())
    }
}

impl<L, R, Target> AsMut<Target> for Either<L, R>
    where L: AsMut<Target>, R: AsMut<Target>
{
    fn as_mut(&mut self) -> &mut Target {
        either_mut!(*self, inner => inner.as_mut())
    }
}

impl<L, R> Deref for Either<L, R>
    where L: Deref, R: Deref<Target=L::Target>
{
    type Target = L::Target;

    fn deref(&self) -> &Self::Target {
        either!(*self, inner => &*inner)
    }
}

impl<L, R> DerefMut for Either<L, R>
    where L: DerefMut, R: DerefMut<Target=L::Target>
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        either_mut!(*self, inner => &mut *inner)
    }
}

#[test]
fn basic() {
    let mut e = Left(2);
    let r = Right(2);
    assert_eq!(e, Left(2));
    e = r;
    assert_eq!(e, Right(2));
    assert_eq!(e.left(), None);
    assert_eq!(e.right(), Some(2));
    assert_eq!(e.as_ref().right(), Some(&2));
    assert_eq!(e.as_mut().right(), Some(&mut 2));
}

#[test]
fn macros() {
    fn a() -> Either<u32, u32> {
        let x = left!(Right::<u32, u32>(1337));
        Left(x * 2)
    }
    assert_eq!(a(), Right(1337));

    fn b() -> Either<String, &'static str> {
        Right(right!(Left::<&str, &str>("foo bar")))
    }
    assert_eq!(b(), Left(String::from("foo bar")));
}

#[test]
fn deref() {
    fn is_str(_: &str) {}
    let value: Either<String, &str> = Left(String::from("test"));
    is_str(&*value);
}

#[test]
fn iter() {
    let x = 3;
    let mut iter = match x {
        1...3 => Left(0..10),
        _ => Right(17..),
    };

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.count(), 9);
}

#[test]
fn read_write() {
    use std::io;

    let use_stdio = false;
    let mockdata = [0xff; 256];

    let mut reader = if use_stdio {
        Left(io::stdin())
    } else {
        Right(&mockdata[..])
    };

    let mut buf = [0u8; 16];
    assert_eq!(reader.read(&mut buf).unwrap(), buf.len());
    assert_eq!(&buf, &mockdata[..buf.len()]);

    let mut mockbuf = [0u8; 256];
    let mut writer = if use_stdio {
        Left(io::stdout())
    } else {
        Right(&mut mockbuf[..])
    };

    let buf = [1u8; 16];
    assert_eq!(writer.write(&buf).unwrap(), buf.len());
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate regex;

use std::collections::HashMap;

use hyper::method::Method;
use hyper::server::{Handler, Request, Response};
use hyper::status::StatusCode;
use regex::{Regex, RegexSet};

pub use error::RouterError;

pub type Captures = Option<Vec<String>>;
pub type NotFoundFn = fn(Request, Response);

#[derive(PartialEq, Eq, Hash)]
pub struct RouteInfo {
    route: String,
    verb: Method,
}

/// The Router struct contains the information for your app to route requests
/// properly based on their HTTP method and matching route. It allows the use
/// of a custom 404 handler if desired but provides a default as well.
///
/// Under the hood a Router uses a RegexSet to match URI's that come in to the
/// instance of the hyper server. Because of this, it has the potential to match
/// multiple patterns that you provide. It will call the first handler that it
/// matches against so the order in which you add routes matters.
pub struct Router {
    /// A custom 404 handler that you can provide.
    pub not_found: Option<NotFoundFn>,

    routes: Option<RegexSet>,
    route_list: Vec<String>,
    compiled_list: Vec<Regex>,
    route_map: HashMap<RouteInfo, Box<Fn(Request, Response, Captures)>>,
}

// These are needed to satisfy the Handler trait while using Fn to accept closures.
unsafe impl Send for Router {}
unsafe impl Sync for Router {}

impl Handler for Router {
    // The handle method for the router simply tries to match the URI against
    // the first pattern that it can which in turn calls its associated handle
    // function passing the hyper Request and Response structures.
    fn handle(&self, req: Request, res: Response) {
        let uri = format!("{}", req.uri);
        let matches = self.routes.as_ref().unwrap().matches(&uri);
        let index = matches.iter().next();
        match index {
            Some(i) => {
                let route = &self.route_list[i];
                let route_info = RouteInfo {
                    route: route.clone(),
                    verb: req.method.clone(),
                };
                let handler = self.route_map.get(&route_info);
                match handler {
                    Some(h) => {
                        let compiled_pattern = &self.compiled_list[i];
                        let captures = get_captures(compiled_pattern, &uri);
                        h(req, res, captures);
                    }
                    None => {
                        not_allowed(req, res);
                    }
                }
            }
            None => self.not_found.unwrap()(req, res),
        }
    }
}

impl Router {
    /// Construct a new Router to maintain the routes and their handler
    /// functions.
    pub fn new() -> Router {
        Router {
            not_found: None,
            routes: None,
            route_list: Vec::new(),
            compiled_list: Vec::new(),
            route_map: HashMap::new(),
        }
    }

    /// Add a route to the router and give it a function to call when the route
    /// is matched against. You can call this explicitly or use the convenience
    /// methods defined below.
    pub fn add_route<H>(&mut self, verb: Method, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        // This is added to provide stricter matching for routes that could have more than one
        // match.
        let anchored_pattern = [r"\A", route].join("");
        let route_info = RouteInfo {
            route: anchored_pattern.clone(),
            verb: verb,
        };
        let pattern = Regex::new(&anchored_pattern);
        match pattern {
            Ok(p) => {
                self.compiled_list.push(p);
            }
            Err(e) => {
                println!("Not adding this route due to error: {}", e);
            }
        }
        self.route_list.push(anchored_pattern.clone());
        self.route_map.insert(route_info, Box::new(handler));
    }

    /// A convenience method for OPTIONS requests.
    pub fn options<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Options, route, handler);
    }

    /// A convenience method for GET requests.
    pub fn get<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Get, route, handler);
    }

    /// A convenience method for POST requests.
    pub fn post<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Post, route, handler);
    }

    /// A convenience method for PUT requests.
    pub fn put<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Put, route, handler);
    }

    /// A convenience method for DELETE requests.
    pub fn delete<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Delete, route, handler);
    }

    /// A convenience method for PATCH requests.
    pub fn patch<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Patch, route, handler);
    }

    /// This function ensures that a valid `RegexSet` could be made from the route
    /// vector that was built while using the functions that add routes. It
    /// requires that there exist at least one route so that the RegexSet can be
    /// successfully constructed.
    ///
    /// It will also ensure that there is a handler for routes that do not match
    /// any available in the set.
    pub fn finalize(&mut self) -> Result<(), RouterError> {
        if self.route_list.len() == 0 {
            return Err(RouterError::TooFewRoutes);
        }

        // Check if the user added a 404 handler, else use the default.
        match self.not_found {
            Some(_) => {}
            None => {
                self.not_found = Some(default_not_found);
            }
        }

        let re_routes = RegexSet::new(self.route_list.iter());
        match re_routes {
            Ok(r) => {
                self.routes = Some(r);
                Ok(())
            }
            Err(_) => Err(RouterError::BadSet),
        }
    }

    /// Add a function to handle routes that get no matches.
    pub fn add_not_found(&mut self, not_found: NotFoundFn) {
        self.not_found = Some(not_found)
    }
}

// The default 404 handler.
fn default_not_found(req: Request, mut res: Response) {
    let message = format!("No route handler found for {}", req.uri);
    *res.status_mut() = StatusCode::NotFound;
    res.send(message.as_bytes()).unwrap();
}

// This handler will get fired when a URI matches a route but contains the wrong method.
fn not_allowed(_: Request, mut res: Response) {
    *res.status_mut() = StatusCode::MethodNotAllowed;
    let res = res.start().unwrap();
    res.end().unwrap();
}

// Return that captures from a pattern that was matched.
fn get_captures(pattern: &Regex, uri: &str) -> Captures {
    // We know this compiles because it was part of the set.
    let caps = pattern.captures(uri);
    match caps {
        Some(caps) => {
            let mut v = vec![];
            for c in caps.iter() {
                v.push(c.unwrap().to_owned());
            }
            Some(v)
        }
        None => None,
    }
}

mod error;

#[test]
fn no_routes() {
    let mut router = Router::new();
    let e = router.finalize();
    assert_eq!(e.err(), Some(RouterError::TooFewRoutes));
}

#[test]
fn bad_regular_expression() {
    fn test_handler(_: Request, _: Response, _: Captures) {}
    let mut router = Router::new();
    router.add_route(Method::Get, r"/[", test_handler);
    let e = router.finalize();
    assert_eq!(e.err(), Some(RouterError::BadSet));
}
<commit_msg>minor changes after running lints<commit_after>extern crate hyper;
extern crate regex;

use std::collections::HashMap;

use hyper::method::Method;
use hyper::server::{Handler, Request, Response};
use hyper::status::StatusCode;
use regex::{Regex, RegexSet};

pub use error::RouterError;

pub type Captures = Option<Vec<String>>;
pub type NotFoundFn = fn(Request, Response);

#[derive(PartialEq, Eq, Hash)]
pub struct RouteInfo {
    route: String,
    verb: Method,
}

/// The Router struct contains the information for your app to route requests
/// properly based on their HTTP method and matching route. It allows the use
/// of a custom 404 handler if desired but provides a default as well.
///
/// Under the hood a Router uses a `RegexSet` to match URI's that come in to the
/// instance of the hyper server. Because of this, it has the potential to match
/// multiple patterns that you provide. It will call the first handler that it
/// matches against so the order in which you add routes matters.
#[derive(Default)]
pub struct Router {
    /// A custom 404 handler that you can provide.
    pub not_found: Option<NotFoundFn>,

    routes: Option<RegexSet>,
    route_list: Vec<String>,
    compiled_list: Vec<Regex>,
    route_map: HashMap<RouteInfo, Box<Fn(Request, Response, Captures)>>,
}

// These are needed to satisfy the Handler trait while using Fn to accept closures.
unsafe impl Send for Router {}
unsafe impl Sync for Router {}

impl Handler for Router {
    // The handle method for the router simply tries to match the URI against
    // the first pattern that it can which in turn calls its associated handle
    // function passing the hyper Request and Response structures.
    fn handle(&self, req: Request, res: Response) {
        let uri = format!("{}", req.uri);
        let matches = self.routes.as_ref().unwrap().matches(&uri);
        let index = matches.iter().next();
        match index {
            Some(i) => {
                let route = &self.route_list[i];
                let route_info = RouteInfo {
                    route: route.clone(),
                    verb: req.method.clone(),
                };
                let handler = self.route_map.get(&route_info);
                match handler {
                    Some(h) => {
                        let compiled_pattern = &self.compiled_list[i];
                        let captures = get_captures(compiled_pattern, &uri);
                        h(req, res, captures);
                    }
                    None => {
                        not_allowed(req, res);
                    }
                }
            }
            None => self.not_found.unwrap()(req, res),
        }
    }
}

impl Router {
    /// Construct a new Router to maintain the routes and their handler
    /// functions.
    pub fn new() -> Router {
        Router {
            not_found: None,
            routes: None,
            route_list: Vec::new(),
            compiled_list: Vec::new(),
            route_map: HashMap::new(),
        }
    }

    /// Add a route to the router and give it a function to call when the route
    /// is matched against. You can call this explicitly or use the convenience
    /// methods defined below.
    pub fn add_route<H>(&mut self, verb: Method, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        // This is added to provide stricter matching for routes that could have more than one
        // match.
        let anchored_pattern = [r"\A", route].join("");
        let route_info = RouteInfo {
            route: anchored_pattern.clone(),
            verb: verb,
        };
        let pattern = Regex::new(&anchored_pattern);
        match pattern {
            Ok(p) => {
                self.compiled_list.push(p);
            }
            Err(e) => {
                println!("Not adding this route due to error: {}", e);
            }
        }
        self.route_list.push(anchored_pattern.clone());
        self.route_map.insert(route_info, Box::new(handler));
    }

    /// A convenience method for OPTIONS requests.
    pub fn options<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Options, route, handler);
    }

    /// A convenience method for GET requests.
    pub fn get<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Get, route, handler);
    }

    /// A convenience method for POST requests.
    pub fn post<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Post, route, handler);
    }

    /// A convenience method for PUT requests.
    pub fn put<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Put, route, handler);
    }

    /// A convenience method for DELETE requests.
    pub fn delete<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Delete, route, handler);
    }

    /// A convenience method for PATCH requests.
    pub fn patch<H>(&mut self, route: &str, handler: H)
        where H: Fn(Request, Response, Captures) + Send + Sync + 'static
    {
        self.add_route(Method::Patch, route, handler);
    }

    /// This function ensures that a valid `RegexSet` could be made from the route
    /// vector that was built while using the functions that add routes. It
    /// requires that there exist at least one route so that the RegexSet can be
    /// successfully constructed.
    ///
    /// It will also ensure that there is a handler for routes that do not match
    /// any available in the set.
    pub fn finalize(&mut self) -> Result<(), RouterError> {
        if self.route_list.is_empty() {
            return Err(RouterError::TooFewRoutes);
        }

        // Check if the user added a 404 handler, else use the default.
        match self.not_found {
            Some(_) => {}
            None => {
                self.not_found = Some(default_not_found);
            }
        }

        let re_routes = RegexSet::new(self.route_list.iter());
        match re_routes {
            Ok(r) => {
                self.routes = Some(r);
                Ok(())
            }
            Err(_) => Err(RouterError::BadSet),
        }
    }

    /// Add a function to handle routes that get no matches.
    pub fn add_not_found(&mut self, not_found: NotFoundFn) {
        self.not_found = Some(not_found)
    }
}

// The default 404 handler.
fn default_not_found(req: Request, mut res: Response) {
    let message = format!("No route handler found for {}", req.uri);
    *res.status_mut() = StatusCode::NotFound;
    res.send(message.as_bytes()).unwrap();
}

// This handler will get fired when a URI matches a route but contains the wrong method.
fn not_allowed(_: Request, mut res: Response) {
    *res.status_mut() = StatusCode::MethodNotAllowed;
    let res = res.start().unwrap();
    res.end().unwrap();
}

// Return that captures from a pattern that was matched.
fn get_captures(pattern: &Regex, uri: &str) -> Captures {
    // We know this compiles because it was part of the set.
    let caps = pattern.captures(uri);
    match caps {
        Some(caps) => {
            let mut v = vec![];
            for c in caps.iter() {
                v.push(c.unwrap().to_owned());
            }
            Some(v)
        }
        None => None,
    }
}

mod error;

#[test]
fn no_routes() {
    let mut router = Router::new();
    let e = router.finalize();
    assert_eq!(e.err(), Some(RouterError::TooFewRoutes));
}

#[test]
fn bad_regular_expression() {
    fn test_handler(_: Request, _: Response, _: Captures) {}
    let mut router = Router::new();
    router.add_route(Method::Get, r"/[", test_handler);
    let e = router.finalize();
    assert_eq!(e.err(), Some(RouterError::BadSet));
}
<|endoftext|>


<commit_before>//
// Copyright 2015 Joshua R. Rodgers
//
// 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.
//

#[macro_export]
macro_rules! context_match {
    ($value:expr, $context:pat => $result:expr) => {
        match $value {
            $context => $result,
            _ => panic!("Unexpected pattern context.")
        }
    }
}

#[macro_export]
macro_rules! query {
    ($($tokens:tt)*) => {
        query_from!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_from {
    (from $context:pat => $source:expr, $($remainder:tt)+) => {
        query_from!(match_none, $source, $context => $($remainder)+)
    };

    (match_none, $source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            let $context = value;
            $newSource
        });

        query_from!(match_none, source, $newContext => $($remainder)+)
    }};

    //NOTE: In order to prevent "refutable pattern" errors potentially caused by where let,
    // I've added a Some(()) into the context to work around this issue.
    (match_context, $source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            context_match!(value, $context => $newSource.map(|value| { (value, Some(())) }))
        });

        query_from!(match_context, source, ($newContext, Some(())) => $($remainder)+)
    }};

    ($($tokens:tt)*) => {
        query_operator!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_where {
    (match_none, $source:expr, $context:pat => $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&$context| { $filter });
            query!(match_none, source, $context => $($remainder)+)
        }
    };

    (match_context, $source:expr, $context:pat => $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&value| { context_match!(value, $context => $filter) });
            query!(match_context, source, $context => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_let {
    (match_none, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                let $context = value;
                (value, $letValue)
            });

            query!(match_none, source, ($context, $newContext) => $($remainder)+)
        }
    };

    (match_context, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                context_match!(value, $context => (value, $letValue))
            });

            query!(match_context, source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_where_let {
    (match_none, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter_map(|&value| {
                let $context = value;
                if let $newContext = $letValue {
                    Some((value, $letValue))
                }
                else {
                    None
                }
            });

            query!(match_context, source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[macro_export]
macro_rules! query_operator {
    ($match_context:ident, $source:expr, $context:pat => where let $($remainder:tt)+) => {
        query_where_let!($match_context, $source, $context => $($remainder)+)
    };

    ($match_context:ident, $source:expr, $context:pat => where $($remainder:tt)+) => {
        {
            query_where!($match_context, $source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => let $($remainder:tt)+) => {
        {
            query_let!($match_context, $source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => do $action:stmt, $($remainder:tt)+) => {
        {
            let source = $source.inspect(|$context| { $action; });
            query!($match_context, source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => from $($remainder:tt)+) => {
        {
            query_from!($match_context, $source, $context => from $($remainder)+)
        }
    };

    ($($tokens:tt)*) => {
        query_end!($($tokens)*)
    };
}

#[macro_export]
macro_rules! query_end {
    (match_context, $source:expr, $context:pat => select $selector:expr) => {
        $source.map(|value| { context_match!(value, $context => $selector) })
    };

    (match_none, $source:expr, $context:pat => select $selector:expr) => {
        $source.map(|$context| { $selector })
    };
}<commit_msg>Added some hidden tags to macros that don't need to be documented.  Renamed some of the macros.<commit_after>//
// Copyright 2015 Joshua R. Rodgers
//
// 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.
//

#[doc(hidden)]
#[macro_export]
macro_rules! expand_context {
    ($value:expr, $context:pat => $result:expr) => {
        match $value {
            $context => $result,
            _ => panic!("Unexpected pattern context.")
        }
    };
}

#[macro_export]
macro_rules! query {
    (from $context:pat => $source:expr, $($remainder:tt)+) => {
        query_from!(match_none, $source, $context => $($remainder)+)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_from {
    (match_none, $source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            let $context = value;
            $newSource
        });

        query_from!(match_none, source, $newContext => $($remainder)+)
    }};

    //NOTE: In order to prevent "refutable pattern" errors potentially caused by where let,
    // I've added a Some(()) into the context to work around this issue.
    (match_context, $source:expr, $context:pat => from $newContext:pat => $newSource:expr, $($remainder:tt)+) => {{
        let source = $source.flat_map(|value| {
            expand_context!(value, $context => $newSource.map(|value| { (value, Some(())) }))
        });

        query_from!(match_context, source, ($newContext, Some(())) => $($remainder)+)
    }};

    ($($tokens:tt)*) => {
        query_clause!($($tokens)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_where {
    (match_none, $source:expr, $context:pat => $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&$context| { $filter });
            query_clause!(match_none, source, $context => $($remainder)+)
        }
    };

    (match_context, $source:expr, $context:pat => $filter:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter(|&value| { expand_context!(value, $context => $filter) });
            query_clause!(match_context, source, $context => $($remainder)+)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_let {
    (match_none, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                let $context = value;
                (value, $letValue)
            });

            query_clause!(match_none, source, ($context, $newContext) => $($remainder)+)
        }
    };

    (match_context, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.map(|value| {
                expand_context!(value, $context => (value, $letValue))
            });

            query_clause!(match_context, source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_where_let {
    (match_none, $source:expr, $context:pat => $newContext:pat = $letValue:expr, $($remainder:tt)+) => {
        {
            let source = $source.filter_map(|&value| {
                let $context = value;
                if let $newContext = $letValue {
                    Some((value, $letValue))
                }
                else {
                    None
                }
            });

            query_clause!(match_context, source, ($context, $newContext) => $($remainder)+)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_clause {
    ($match_context:ident, $source:expr, $context:pat => where let $($remainder:tt)+) => {
        query_where_let!($match_context, $source, $context => $($remainder)+)
    };

    ($match_context:ident, $source:expr, $context:pat => where $($remainder:tt)+) => {
        {
            query_where!($match_context, $source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => let $($remainder:tt)+) => {
        {
            query_let!($match_context, $source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => do $action:stmt, $($remainder:tt)+) => {
        {
            let source = $source.inspect(|$context| { $action; });
            query_clause!($match_context, source, $context => $($remainder)+)
        }
    };

    ($match_context:ident, $source:expr, $context:pat => from $($remainder:tt)+) => {
        {
            query_from!($match_context, $source, $context => from $($remainder)+)
        }
    };

    ($($tokens:tt)*) => {
        query_end!($($tokens)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! query_end {
    (match_context, $source:expr, $context:pat => select $selector:expr) => {
        $source.map(|value| { expand_context!(value, $context => $selector) })
    };

    (match_none, $source:expr, $context:pat => select $selector:expr) => {
        $source.map(|$context| { $selector })
    };
}<|endoftext|>