<commit_before>use std::fs::{self, File};
use std::io::prelude::*;

use support::{project, execs, path2url};
use support::paths::CargoPathExt;
use hamcrest::{assert_that, existing_file};

fn setup() {}

test!(modifying_and_moving {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"
        "#)
        .file("src/main.rs", r#"
            mod a; fn main() {}
        "#)
        .file("src/a.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stdout(""));
    p.root().move_into_the_past().unwrap();
    p.root().join("target").move_into_the_past().unwrap();

    File::create(&p.root().join("src/a.rs")).unwrap()
         .write_all(b"fn main() {}").unwrap();
    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));

    fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap();
    assert_that(p.cargo("build"),
                execs().with_status(101));
});

test!(modify_only_some_files {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"
        "#)
        .file("src/lib.rs", "mod a;")
        .file("src/a.rs", "")
        .file("src/main.rs", r#"
            mod b;
            fn main() {}
        "#)
        .file("src/b.rs", "")
        .file("tests/test.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));
    assert_that(p.cargo("test"),
                execs().with_status(0));
    ::sleep_ms(1000);

    assert_that(&p.bin("foo"), existing_file());

    let lib = p.root().join("src/lib.rs");
    let bin = p.root().join("src/b.rs");

    File::create(&lib).unwrap().write_all(b"invalid rust code").unwrap();
    File::create(&bin).unwrap().write_all(b"fn foo() {}").unwrap();
    lib.move_into_the_past().unwrap();

    // Make sure the binary is rebuilt, not the lib
    assert_that(p.cargo("build")
                 .env("RUST_LOG", "cargo::ops::cargo_rustc::fingerprint"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));
    assert_that(&p.bin("foo"), existing_file());
});

test!(rebuild_sub_package_then_while_package {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"

            [dependencies.a]
            path = "a"
            [dependencies.b]
            path = "b"
        "#)
        .file("src/lib.rs", "extern crate a; extern crate b;")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            authors = []
            version = "0.0.1"
            [dependencies.b]
            path = "../b"
        "#)
        .file("a/src/lib.rs", "extern crate b;")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            authors = []
            version = "0.0.1"
        "#)
        .file("b/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));

    File::create(&p.root().join("b/src/lib.rs")).unwrap().write_all(br#"
        pub fn b() {}
    "#).unwrap();

    assert_that(p.cargo("build").arg("-pb"),
                execs().with_status(0));

    File::create(&p.root().join("src/lib.rs")).unwrap().write_all(br#"
        extern crate a;
        extern crate b;
        pub fn toplevel() {}
    "#).unwrap();

    assert_that(p.cargo("build"),
                execs().with_status(0));
});

test!(changing_features_is_ok {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"

            [features]
            foo = []
        "#)
        .file("src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build").arg("--features").arg("foo"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build"),
                execs().with_status(0)
                       .with_stdout(""));
});

test!(rebuild_tests_if_lib_changes {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file("tests/foo.rs", r#"
            extern crate foo;
            #[test]
            fn test() { foo::foo(); }
        "#);

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("test"),
                execs().with_status(0));

    File::create(&p.root().join("src/lib.rs")).unwrap();
    p.root().move_into_the_past().unwrap();
    p.root().join("target").move_into_the_past().unwrap();

    assert_that(p.cargo("build"),
                execs().with_status(0));
    assert_that(p.cargo("test").arg("-v"),
                execs().with_status(101));
});

test!(no_rebuild_transitive_target_deps {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            a = { path = "a" }
            [dev-dependencies]
            b = { path = "b" }
        "#)
        .file("src/lib.rs", "")
        .file("tests/foo.rs", "")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            version = "0.0.1"
            authors = []

            [target.foo.dependencies]
            c = { path = "../c" }
        "#)
        .file("a/src/lib.rs", "")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            version = "0.0.1"
            authors = []

            [dependencies]
            c = { path = "../c" }
        "#)
        .file("b/src/lib.rs", "")
        .file("c/Cargo.toml", r#"
            [package]
            name = "c"
            version = "0.0.1"
            authors = []
        "#)
        .file("c/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("test").arg("--no-run"),
                execs().with_status(0)
                       .with_stderr("\
[COMPILING] c v0.0.1 ([..])
[COMPILING] b v0.0.1 ([..])
[COMPILING] foo v0.0.1 ([..])
"));
});

test!(rerun_if_changed_in_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            a = { path = "a" }
        "#)
        .file("src/lib.rs", "")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("a/build.rs", r#"
            fn main() {
                println!("cargo:rerun-if-changed=build.rs");
            }
        "#)
        .file("a/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("build"),
                execs().with_status(0).with_stdout(""));
});

test!(same_build_dir_cached_packages {
    let p = project("foo")
        .file("a1/Cargo.toml", r#"
            [package]
            name = "a1"
            version = "0.0.1"
            authors = []
            [dependencies]
            b = { path = "../b" }
        "#)
        .file("a1/src/lib.rs", "")
        .file("a2/Cargo.toml", r#"
            [package]
            name = "a2"
            version = "0.0.1"
            authors = []
            [dependencies]
            b = { path = "../b" }
        "#)
        .file("a2/src/lib.rs", "")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            version = "0.0.1"
            authors = []
            [dependencies]
            c = { path = "../c" }
        "#)
        .file("b/src/lib.rs", "")
        .file("c/Cargo.toml", r#"
            [package]
            name = "c"
            version = "0.0.1"
            authors = []
            [dependencies]
            d = { path = "../d" }
        "#)
        .file("c/src/lib.rs", "")
        .file("d/Cargo.toml", r#"
            [package]
            name = "d"
            version = "0.0.1"
            authors = []
        "#)
        .file("d/src/lib.rs", "")
        .file(".cargo/config", r#"
            [build]
            target-dir = "./target"
        "#);
    p.build();

    assert_that(p.cargo("build").cwd(p.root().join("a1")),
                execs().with_status(0).with_stderr(&format!("\
[COMPILING] d v0.0.1 ({dir}/d)
[COMPILING] c v0.0.1 ({dir}/c)
[COMPILING] b v0.0.1 ({dir}/b)
[COMPILING] a1 v0.0.1 ({dir}/a1)
", dir = p.url())));
    assert_that(p.cargo("build").cwd(p.root().join("a2")),
                execs().with_status(0).with_stderr(&format!("\
[COMPILING] a2 v0.0.1 ({dir}/a2)
", dir = p.url())));
});
<commit_msg>fix test_cargo_freshness<commit_after>use std::fs::{self, File};
use std::io::prelude::*;

use support::{project, execs, path2url};
use support::paths::CargoPathExt;
use hamcrest::{assert_that, existing_file};

fn setup() {}

test!(modifying_and_moving {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"
        "#)
        .file("src/main.rs", r#"
            mod a; fn main() {}
        "#)
        .file("src/a.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stdout(""));
    p.root().move_into_the_past().unwrap();
    p.root().join("target").move_into_the_past().unwrap();

    File::create(&p.root().join("src/a.rs")).unwrap()
         .write_all(b"#[allow(unused)]fn main() {}").unwrap();
    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));

    fs::rename(&p.root().join("src/a.rs"), &p.root().join("src/b.rs")).unwrap();
    assert_that(p.cargo("build"),
                execs().with_status(101));
});

test!(modify_only_some_files {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"
        "#)
        .file("src/lib.rs", "mod a;")
        .file("src/a.rs", "")
        .file("src/main.rs", r#"
            mod b;
            fn main() {}
        "#)
        .file("src/b.rs", "")
        .file("tests/test.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));
    assert_that(p.cargo("test"),
                execs().with_status(0));
    ::sleep_ms(1000);

    assert_that(&p.bin("foo"), existing_file());

    let lib = p.root().join("src/lib.rs");
    let bin = p.root().join("src/b.rs");

    File::create(&lib).unwrap().write_all(b"invalid rust code").unwrap();
    File::create(&bin).unwrap().write_all(b"#[allow(unused)]fn foo() {}").unwrap();
    lib.move_into_the_past().unwrap();

    // Make sure the binary is rebuilt, not the lib
    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr(format!("\
[COMPILING] foo v0.0.1 ({dir})
", dir = path2url(p.root()))));
    assert_that(&p.bin("foo"), existing_file());
});

test!(rebuild_sub_package_then_while_package {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"

            [dependencies.a]
            path = "a"
            [dependencies.b]
            path = "b"
        "#)
        .file("src/lib.rs", "extern crate a; extern crate b;")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            authors = []
            version = "0.0.1"
            [dependencies.b]
            path = "../b"
        "#)
        .file("a/src/lib.rs", "extern crate b;")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            authors = []
            version = "0.0.1"
        "#)
        .file("b/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));

    File::create(&p.root().join("b/src/lib.rs")).unwrap().write_all(br#"
        pub fn b() {}
    "#).unwrap();

    assert_that(p.cargo("build").arg("-pb"),
                execs().with_status(0));

    File::create(&p.root().join("src/lib.rs")).unwrap().write_all(br#"
        extern crate a;
        extern crate b;
        pub fn toplevel() {}
    "#).unwrap();

    assert_that(p.cargo("build"),
                execs().with_status(0));
});

test!(changing_features_is_ok {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            authors = []
            version = "0.0.1"

            [features]
            foo = []
        "#)
        .file("src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build").arg("--features").arg("foo"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build"),
                execs().with_status(0)
                       .with_stderr("\
[..]Compiling foo v0.0.1 ([..])
"));

    assert_that(p.cargo("build"),
                execs().with_status(0)
                       .with_stdout(""));
});

test!(rebuild_tests_if_lib_changes {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file("tests/foo.rs", r#"
            extern crate foo;
            #[test]
            fn test() { foo::foo(); }
        "#);

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("test"),
                execs().with_status(0));

    File::create(&p.root().join("src/lib.rs")).unwrap();
    p.root().move_into_the_past().unwrap();
    p.root().join("target").move_into_the_past().unwrap();

    assert_that(p.cargo("build"),
                execs().with_status(0));
    assert_that(p.cargo("test").arg("-v"),
                execs().with_status(101));
});

test!(no_rebuild_transitive_target_deps {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            a = { path = "a" }
            [dev-dependencies]
            b = { path = "b" }
        "#)
        .file("src/lib.rs", "")
        .file("tests/foo.rs", "")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            version = "0.0.1"
            authors = []

            [target.foo.dependencies]
            c = { path = "../c" }
        "#)
        .file("a/src/lib.rs", "")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            version = "0.0.1"
            authors = []

            [dependencies]
            c = { path = "../c" }
        "#)
        .file("b/src/lib.rs", "")
        .file("c/Cargo.toml", r#"
            [package]
            name = "c"
            version = "0.0.1"
            authors = []
        "#)
        .file("c/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("test").arg("--no-run"),
                execs().with_status(0)
                       .with_stderr("\
[COMPILING] c v0.0.1 ([..])
[COMPILING] b v0.0.1 ([..])
[COMPILING] foo v0.0.1 ([..])
"));
});

test!(rerun_if_changed_in_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            a = { path = "a" }
        "#)
        .file("src/lib.rs", "")
        .file("a/Cargo.toml", r#"
            [package]
            name = "a"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("a/build.rs", r#"
            fn main() {
                println!("cargo:rerun-if-changed=build.rs");
            }
        "#)
        .file("a/src/lib.rs", "");

    assert_that(p.cargo_process("build"),
                execs().with_status(0));
    assert_that(p.cargo("build"),
                execs().with_status(0).with_stdout(""));
});

test!(same_build_dir_cached_packages {
    let p = project("foo")
        .file("a1/Cargo.toml", r#"
            [package]
            name = "a1"
            version = "0.0.1"
            authors = []
            [dependencies]
            b = { path = "../b" }
        "#)
        .file("a1/src/lib.rs", "")
        .file("a2/Cargo.toml", r#"
            [package]
            name = "a2"
            version = "0.0.1"
            authors = []
            [dependencies]
            b = { path = "../b" }
        "#)
        .file("a2/src/lib.rs", "")
        .file("b/Cargo.toml", r#"
            [package]
            name = "b"
            version = "0.0.1"
            authors = []
            [dependencies]
            c = { path = "../c" }
        "#)
        .file("b/src/lib.rs", "")
        .file("c/Cargo.toml", r#"
            [package]
            name = "c"
            version = "0.0.1"
            authors = []
            [dependencies]
            d = { path = "../d" }
        "#)
        .file("c/src/lib.rs", "")
        .file("d/Cargo.toml", r#"
            [package]
            name = "d"
            version = "0.0.1"
            authors = []
        "#)
        .file("d/src/lib.rs", "")
        .file(".cargo/config", r#"
            [build]
            target-dir = "./target"
        "#);
    p.build();

    assert_that(p.cargo("build").cwd(p.root().join("a1")),
                execs().with_status(0).with_stderr(&format!("\
[COMPILING] d v0.0.1 ({dir}/d)
[COMPILING] c v0.0.1 ({dir}/c)
[COMPILING] b v0.0.1 ({dir}/b)
[COMPILING] a1 v0.0.1 ({dir}/a1)
", dir = p.url())));
    assert_that(p.cargo("build").cwd(p.root().join("a2")),
                execs().with_status(0).with_stderr(&format!("\
[COMPILING] a2 v0.0.1 ({dir}/a2)
", dir = p.url())));
});
<|endoftext|>


<commit_before>use super::{PhysicalPage, VirtualPage, FrameRange};
use memory::alloc::{FrameAllocator, BorrowedFrame};
use spin::Mutex;

pub struct TempPage  { page: VirtualPage
                     , alloc: FrameCache
                        }

impl TempPage {
    pub fn new<A>(number: usize, alloc: &A) -> Self
    where A: FrameAllocator {
        TempPage { page: VirtualPage { number: number }
                 , alloc: FrameCache::new(alloc)
                 }
    }
}

pub struct FrameCache(Mutex<[Option<PhysicalPage>; 3]>);

impl FrameCache {

    pub fn new<A>(alloc: &A) -> Self
    where A: FrameAllocator {
        unsafe {
            let frames = [ alloc.allocate()
                         , alloc.allocate()
                         , alloc.allocate() ];
            FrameCache(Mutex::new(frames))
        }
    }
}

impl FrameAllocator for FrameCache {

    unsafe fn allocate(&self) -> Option<PhysicalPage> {
        self.0.lock()
            .iter_mut()
            .find(    |frame| frame.is_some())
            .and_then(|mut frame| frame.take())
    }

    unsafe fn deallocate(&self, frame: PhysicalPage) {
        self.0.lock()
            .iter_mut()
            .find(    |slot| slot.is_none())
            .and_then(|mut slot| { *slot = Some(frame); Some(()) })
            .expect("FrameCache can only hold three frames!");
    }

    unsafe fn allocate_range(&self, _num: usize) -> Option<FrameRange> {
        unimplemented!()
    }

    unsafe fn deallocate_range(&self, _range: FrameRange) {
        unimplemented!()
    }

}
<commit_msg>[kernel] add docs to temp page<commit_after>use super::{PhysicalPage, VirtualPage, FrameRange};
use memory::alloc::{FrameAllocator, BorrowedFrame};
use spin::Mutex;

pub struct TempPage { page: VirtualPage
                    , frames: FrameCache
                    }

impl TempPage {

    /// Create a new `TempPage`.
    ///
    /// # Arguments
    /// + `number`: the page number for the temporary page
    /// + `alloc`: a `FrameAllocator` for allocating the frames to use
    ///            for the temporary page.
    pub fn new<A>(number: usize, alloc: &A) -> Self
    where A: FrameAllocator {
        TempPage { page: VirtualPage { number: number }
                 , frames: FrameCache::new(alloc)
                 }
    }
}

pub struct FrameCache(Mutex<[Option<PhysicalPage>; 3]>);

impl FrameCache {

    pub fn new<A>(alloc: &A) -> Self
    where A: FrameAllocator {
        unsafe {
            let frames = [ alloc.allocate()
                         , alloc.allocate()
                         , alloc.allocate() ];
            FrameCache(Mutex::new(frames))
        }
    }
}

impl FrameAllocator for FrameCache {

    unsafe fn allocate(&self) -> Option<PhysicalPage> {
        self.0.lock()
            .iter_mut()
            .find(    |frame| frame.is_some())
            .and_then(|mut frame| frame.take())
    }

    unsafe fn deallocate(&self, frame: PhysicalPage) {
        self.0.lock()
            .iter_mut()
            .find(    |slot| slot.is_none())
            .and_then(|mut slot| { *slot = Some(frame); Some(()) })
            .expect("FrameCache can only hold three frames!");
    }

    unsafe fn allocate_range(&self, _num: usize) -> Option<FrameRange> {
        unimplemented!()
    }

    unsafe fn deallocate_range(&self, _range: FrameRange) {
        unimplemented!()
    }

}
<|endoftext|>


<commit_before>extern crate image;

use vector::*;

pub type Color = Vec3;

pub enum Object {
    Sphere { center: Point, radius: f64 }
}

pub struct Material {
    pub ambient: Color,
    pub diffuse: Color,
    pub specular: Color,
    pub specular_exponent: f64
}

pub type MaterialObject = (Object, Material);

pub struct Light {
    pub position: Point,
    pub color: Color,
    pub intensity: f64
}

pub struct Camera {
    pub position: Point,
    pub direction: Vec3,
    pub up: Vec3
}

pub struct Scene {
    pub camera: Camera,
    pub lights: Vec<Light>,
    pub objects: Vec<MaterialObject>,
}

impl Color {
    pub fn to_rgb(&self) -> image::Rgb<u8> {
        // TODO: implement tonemapping (by parameterizing color on a trait?)
        image::Rgb { data: [(self.x.min(1.0) * 255.0) as u8,
                            (self.y.min(1.0) * 255.0) as u8,
                            (self.z.min(1.0) * 255.0) as u8] }
    }
}

impl Scene {
    pub fn intersects(&self, ray: &Ray) -> Option<(&MaterialObject, f64)> {
        self.objects.iter().fold(None, |result, materialobject| {
            let (ref object, _) = *materialobject;
            match intersects(&ray, object) {
                Some(t) => {
                    match result {
                        Some((_, t_prev)) => {
                            if t < t_prev {
                                Some((&materialobject, t))
                            }
                            else {
                                result
                            }
                        }
                        None => {
                            Some((&materialobject, t))
                        }
                    }
                }
                None => {
                    result
                }
            }
        })
    }
}

const T_THRESHOLD: f64 = 0.00001;

pub fn intersects(ray: &Ray, object: &Object) -> Option<f64> {
    let Ray { origin: e, direction: d } = *ray;
    match *object {
        Object::Sphere { center: c, radius: r } => {
            let discrim = d.dot(e - c).powi(2) -
                (d.dot(d)) * ((e - c).dot(e-c) - r.powi(2));
            if discrim > T_THRESHOLD {
                let t = ((-d).dot(e - c) + discrim.sqrt()) / (d.dot(d));
                if t > T_THRESHOLD {
                    Some(t)
                }
                else {
                    None
                }
            }
            else {
                None
            }
        }
    }
}
<commit_msg>Fix time value from ray-sphere intersection<commit_after>extern crate image;

use vector::*;

pub type Color = Vec3;

pub enum Object {
    Sphere { center: Point, radius: f64 }
}

pub struct Material {
    pub ambient: Color,
    pub diffuse: Color,
    pub specular: Color,
    pub specular_exponent: f64
}

pub type MaterialObject = (Object, Material);

pub struct Light {
    pub position: Point,
    pub color: Color,
    pub intensity: f64
}

pub struct Camera {
    pub position: Point,
    pub direction: Vec3,
    pub up: Vec3
}

pub struct Scene {
    pub camera: Camera,
    pub lights: Vec<Light>,
    pub objects: Vec<MaterialObject>,
}

impl Color {
    pub fn to_rgb(&self) -> image::Rgb<u8> {
        // TODO: implement tonemapping (by parameterizing color on a trait?)
        image::Rgb { data: [(self.x.min(1.0) * 255.0) as u8,
                            (self.y.min(1.0) * 255.0) as u8,
                            (self.z.min(1.0) * 255.0) as u8] }
    }
}

impl Scene {
    pub fn intersects(&self, ray: &Ray) -> Option<(&MaterialObject, f64)> {
        self.objects.iter().fold(None, |result, materialobject| {
            let (ref object, _) = *materialobject;
            match intersects(&ray, object) {
                Some(t) => {
                    match result {
                        Some((_, t_prev)) => {
                            if t < t_prev {
                                Some((&materialobject, t))
                            }
                            else {
                                result
                            }
                        }
                        None => {
                            Some((&materialobject, t))
                        }
                    }
                }
                None => {
                    result
                }
            }
        })
    }
}

const T_THRESHOLD: f64 = 0.00001;

pub fn intersects(ray: &Ray, object: &Object) -> Option<f64> {
    let Ray { origin: e, direction: d } = *ray;
    match *object {
        Object::Sphere { center: c, radius: r } => {
            let discrim = d.dot(e - c).powi(2) -
                (d.dot(d)) * ((e - c).dot(e-c) - r.powi(2));
            if discrim > T_THRESHOLD {
                let sqrtd = discrim.sqrt();
                let t1 = ((-d).dot(e - c) - sqrtd) / (d.dot(d));
                let t2 = ((-d).dot(e - c) + sqrtd) / (d.dot(d));
                match (t1 >= T_THRESHOLD, t2 >= T_THRESHOLD, t1 >= t2) {
                    (true, false, true) |
                    (true, true, false) |
                    (true, false, false) => {
                        Some(t1)
                    }
                    (false, true, true) |
                    (false, true, false) |
                    (true, true, true) => {
                        Some(t2)
                    }
                    _ => {
                        None
                    }
                }
            }
            else {
                None
            }
        }
    }
}
<|endoftext|>


<commit_before>use ansi_term::Style;

use crate::fs::File;
use crate::info::filetype::FileExtensions;
use crate::output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{}  ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai"        => '\u{e7b4}',
                "android"   => '\u{e70e}',
                "apple"     => '\u{f179}',
                "avro"      => '\u{e60b}',
                "clj"       => '\u{e768}',
                "coffee"    => '\u{f0f4}',
                "cpp"       => '\u{e61d}',
                "hpp"       => '\u{e61d}',
                "c"         => '\u{e61e}',
                "h"         => '\u{e61e}',
                "cs"        => '\u{f81a}',
                "css"       => '\u{e749}',
                "d"         => '\u{e7af}',
                "dart"      => '\u{e798}',
                "db"        => '\u{f1c0}',
                "diff"      => '\u{f440}',
                "patch"     => '\u{f440}',
                "rtf"       => '\u{f1c2}',
                "doc"       => '\u{f1c2}',
                "docx"      => '\u{f1c2}',
                "odt"       => '\u{f1c2}',
                "ebook"     => '\u{e28b}',
                "env"       => '\u{f462}',
                "epub"      => '\u{e28a}',
                "erl"       => '\u{e7b1}',
                "font"      => '\u{f031}',
                "gform"     => '\u{f298}',
                "git"       => '\u{f1d3}',
                "go"        => '\u{e626}',
                "hs"        => '\u{e777}',
                "htm"       => '\u{f13b}',
                "html"      => '\u{f13b}',
                "xhtml"     => '\u{f13b}',
                "iml"       => '\u{e7b5}',
                "java"      => '\u{e204}',
                "js"        => '\u{e74e}',
                "mjs"       => '\u{e74e}',
                "json"      => '\u{e60b}',
                "jsx"       => '\u{e7ba}',
                "vue"       => '\u{fd42}',
                "node"      => '\u{f898}',
                "less"      => '\u{e758}',
                "log"       => '\u{f18d}',
                "lua"       => '\u{e620}',
                "md"        => '\u{f48a}',
                "markdown"  => '\u{f48a}',
                "mustache"  => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf"       => '\u{f1c1}',
                "djvu"      => '\u{f02d}',
                "mobi"      => '\u{f02d}',
                "php"       => '\u{e73d}',
                "pl"        => '\u{e769}',
                "ppt"       => '\u{f1c4}',
                "pptx"      => '\u{f1c4}',
                "odp"       => '\u{f1c4}',
                "psd"       => '\u{e7b8}',
                "py"        => '\u{e606}',
                "r"         => '\u{f25d}',
                "rb"        => '\u{e21e}',
                "ru"        => '\u{e21e}',
                "erb"       => '\u{e21e}',
                "gem"       => '\u{e21e}',
                "rdb"       => '\u{e76d}',
                "rs"        => '\u{e7a8}',
                "rss"       => '\u{f09e}',
                "rubydoc"   => '\u{e73b}',
                "sass"      => '\u{e74b}',
                "stylus"    => '\u{e759}',
                "scala"     => '\u{e737}',
                "shell"     => '\u{f489}',
                "sqlite3"   => '\u{e7c4}',
                "styl"      => '\u{e600}',
                "latex"     => '\u{e600}',
                "tex"       => '\u{e600}',
                "ts"        => '\u{e628}',
                "tsx"       => '\u{e628}',
                "twig"      => '\u{e61c}',
                "txt"       => '\u{f15c}',
                "video"     => '\u{f03d}',
                "vim"       => '\u{e62b}',
                "xml"       => '\u{e619}',
                "yml"       => '\u{f481}',
                "yaml"      => '\u{f481}',
                "rar"       => '\u{f410}',
                "zip"       => '\u{f410}',
                "bz"        => '\u{f410}',
                "bz2"       => '\u{f410}',
                "xz"        => '\u{f410}',
                "taz"       => '\u{f410}',
                "tbz"       => '\u{f410}',
                "tbz2"      => '\u{f410}',
                "tz"        => '\u{f410}',
                "tar"       => '\u{f410}',
                "tzo"       => '\u{f410}',
                "lz"        => '\u{f410}',
                "lzh"       => '\u{f410}',
                "lzma"      => '\u{f410}',
                "lzo"       => '\u{f410}',
                "gz"        => '\u{f410}',
                "deb"       => '\u{e77d}',
                "rpm"       => '\u{e7bb}',
                "exe"       => '\u{e70f}',
                "msi"       => '\u{e70f}',
                "dll"       => '\u{e70f}',
                "cab"       => '\u{e70f}',
                "bat"       => '\u{e70f}',
                "cmd"       => '\u{e70f}',
                "sh"        => '\u{e795}',
                "bash"      => '\u{e795}',
                "zsh"       => '\u{e795}',
                "fish"      => '\u{e795}',
                "csh"       => '\u{e795}',
                "ini"       => '\u{e615}',
                "toml"      => '\u{e615}',
                "cfg"       => '\u{e615}',
                "conf"      => '\u{e615}',
                "apk"       => '\u{e70e}',
                "ttf"       => '\u{f031}',
                "woff"      => '\u{f031}',
                "woff2"     => '\u{f031}',
                "otf"       => '\u{f031}',
                "csv"       => '\u{f1c3}',
                "tsv"       => '\u{f1c3}',
                "xls"       => '\u{f1c3}',
                "xlsx"      => '\u{f1c3}',
                "ods"       => '\u{f1c3}',
                "so"        => '\u{f17c}',
                "sql"       => '\u{f1c0}',
                "jar"       => '\u{e256}',
                "jad"       => '\u{e256}',
                "class"     => '\u{e256}',
                "war"       => '\u{e256}',
                "groovy"    => '\u{e775}',
                "iso"       => '\u{e271}',
                "lock"      => '\u{f023}',
                _           => '\u{f15b}'
            }
        } else {
            '\u{f15b}'
        }
    }
}
<commit_msg>Add Swift Programming Language icon<commit_after>use ansi_term::Style;

use crate::fs::File;
use crate::info::filetype::FileExtensions;
use crate::output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{}  ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai"        => '\u{e7b4}',
                "android"   => '\u{e70e}',
                "apple"     => '\u{f179}',
                "avro"      => '\u{e60b}',
                "clj"       => '\u{e768}',
                "coffee"    => '\u{f0f4}',
                "cpp"       => '\u{e61d}',
                "hpp"       => '\u{e61d}',
                "c"         => '\u{e61e}',
                "h"         => '\u{e61e}',
                "cs"        => '\u{f81a}',
                "css"       => '\u{e749}',
                "d"         => '\u{e7af}',
                "dart"      => '\u{e798}',
                "db"        => '\u{f1c0}',
                "diff"      => '\u{f440}',
                "patch"     => '\u{f440}',
                "rtf"       => '\u{f1c2}',
                "doc"       => '\u{f1c2}',
                "docx"      => '\u{f1c2}',
                "odt"       => '\u{f1c2}',
                "ebook"     => '\u{e28b}',
                "env"       => '\u{f462}',
                "epub"      => '\u{e28a}',
                "erl"       => '\u{e7b1}',
                "font"      => '\u{f031}',
                "gform"     => '\u{f298}',
                "git"       => '\u{f1d3}',
                "go"        => '\u{e626}',
                "hs"        => '\u{e777}',
                "htm"       => '\u{f13b}',
                "html"      => '\u{f13b}',
                "xhtml"     => '\u{f13b}',
                "iml"       => '\u{e7b5}',
                "java"      => '\u{e204}',
                "js"        => '\u{e74e}',
                "mjs"       => '\u{e74e}',
                "json"      => '\u{e60b}',
                "jsx"       => '\u{e7ba}',
                "vue"       => '\u{fd42}',
                "node"      => '\u{f898}',
                "less"      => '\u{e758}',
                "log"       => '\u{f18d}',
                "lua"       => '\u{e620}',
                "md"        => '\u{f48a}',
                "markdown"  => '\u{f48a}',
                "mustache"  => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf"       => '\u{f1c1}',
                "djvu"      => '\u{f02d}',
                "mobi"      => '\u{f02d}',
                "php"       => '\u{e73d}',
                "pl"        => '\u{e769}',
                "ppt"       => '\u{f1c4}',
                "pptx"      => '\u{f1c4}',
                "odp"       => '\u{f1c4}',
                "psd"       => '\u{e7b8}',
                "py"        => '\u{e606}',
                "r"         => '\u{f25d}',
                "rb"        => '\u{e21e}',
                "ru"        => '\u{e21e}',
                "erb"       => '\u{e21e}',
                "gem"       => '\u{e21e}',
                "rdb"       => '\u{e76d}',
                "rs"        => '\u{e7a8}',
                "rss"       => '\u{f09e}',
                "rubydoc"   => '\u{e73b}',
                "sass"      => '\u{e74b}',
                "stylus"    => '\u{e759}',
                "scala"     => '\u{e737}',
                "shell"     => '\u{f489}',
                "sqlite3"   => '\u{e7c4}',
                "styl"      => '\u{e600}',
                "latex"     => '\u{e600}',
                "tex"       => '\u{e600}',
                "ts"        => '\u{e628}',
                "tsx"       => '\u{e628}',
                "twig"      => '\u{e61c}',
                "txt"       => '\u{f15c}',
                "video"     => '\u{f03d}',
                "vim"       => '\u{e62b}',
                "xml"       => '\u{e619}',
                "yml"       => '\u{f481}',
                "yaml"      => '\u{f481}',
                "rar"       => '\u{f410}',
                "zip"       => '\u{f410}',
                "bz"        => '\u{f410}',
                "bz2"       => '\u{f410}',
                "xz"        => '\u{f410}',
                "taz"       => '\u{f410}',
                "tbz"       => '\u{f410}',
                "tbz2"      => '\u{f410}',
                "tz"        => '\u{f410}',
                "tar"       => '\u{f410}',
                "tzo"       => '\u{f410}',
                "lz"        => '\u{f410}',
                "lzh"       => '\u{f410}',
                "lzma"      => '\u{f410}',
                "lzo"       => '\u{f410}',
                "gz"        => '\u{f410}',
                "deb"       => '\u{e77d}',
                "rpm"       => '\u{e7bb}',
                "exe"       => '\u{e70f}',
                "msi"       => '\u{e70f}',
                "dll"       => '\u{e70f}',
                "cab"       => '\u{e70f}',
                "bat"       => '\u{e70f}',
                "cmd"       => '\u{e70f}',
                "sh"        => '\u{e795}',
                "bash"      => '\u{e795}',
                "zsh"       => '\u{e795}',
                "fish"      => '\u{e795}',
                "csh"       => '\u{e795}',
                "ini"       => '\u{e615}',
                "toml"      => '\u{e615}',
                "cfg"       => '\u{e615}',
                "conf"      => '\u{e615}',
                "apk"       => '\u{e70e}',
                "ttf"       => '\u{f031}',
                "woff"      => '\u{f031}',
                "woff2"     => '\u{f031}',
                "otf"       => '\u{f031}',
                "csv"       => '\u{f1c3}',
                "tsv"       => '\u{f1c3}',
                "xls"       => '\u{f1c3}',
                "xlsx"      => '\u{f1c3}',
                "ods"       => '\u{f1c3}',
                "so"        => '\u{f17c}',
                "sql"       => '\u{f1c0}',
                "jar"       => '\u{e256}',
                "jad"       => '\u{e256}',
                "class"     => '\u{e256}',
                "war"       => '\u{e256}',
                "groovy"    => '\u{e775}',
                "iso"       => '\u{e271}',
                "lock"      => '\u{f023}',
                "swift"     => '\u{e755}',
                _           => '\u{f15b}'
            }
        } else {
            '\u{f15b}'
        }
    }
}
<|endoftext|>


<commit_before>use parser::combinators::{TypeWithLifetime, At, Str, Unit, Parser, StringParser, string, character};

pub enum TokenAt<'a> {
    LParen,
    RParen,
    Whitespace,
    Identifier(&'a str),
}

pub struct Token;

impl<'a> TypeWithLifetime<'a> for TokenAt<'a> {
    type Type = TokenAt<'a>;
}

fn foo() {
    let LPAREN = string("(");
    let RPAREN = string(")");
    let WHITESPACE = character(char::is_whitespace).plus();
    let IDENTIFIER = character(char::is_alphabetic).and_then(character(char::is_alphanumeric).star());
}

<commit_msg>First draft lexer().<commit_after>use parser::combinators::{TypeWithLifetime, Str, Unit, Function, Parser, StrParser, ParserConsumer, string, character};
use self::TokenAt::{LParen, RParen, Whitespace, Identifier};

#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub enum TokenAt<'a> {
    LParen,
    RParen,
    Whitespace,
    Identifier(&'a str),
}

pub struct Token;

impl<'a> TypeWithLifetime<'a> for Token {
    type Type = TokenAt<'a>;
}

impl Function<Unit,Token> for TokenAt<'static> {
    fn apply<'a>(&self, _: ()) -> TokenAt<'a> {
        self.clone()
    }
}

struct MkIdentifier;

impl Function<Str,Token> for MkIdentifier {
    fn apply<'a>(&self, name: &'a str) -> TokenAt<'a> {
        Identifier(name)
    }
}

#[allow(non_snake_case)]
pub fn lexer<C>(consumer: &mut C) where C: ParserConsumer<Str,Token> {
    let LPAREN = string("(").map(LParen);
    let RPAREN = string(")").map(RParen);
    let WHITESPACE = character(char::is_whitespace).plus().map(Whitespace);
    let IDENTIFIER = character(char::is_alphabetic).and_then(character(char::is_alphanumeric).star()).buffer().map(MkIdentifier);
    let TOKEN = LPAREN.or_else(RPAREN).or_else(WHITESPACE).or_else(IDENTIFIER);
    consumer.accept(TOKEN.star())
}

<|endoftext|>


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

use libc::c_int;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};

use error::Result;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

pub fn wait_for_exit(pid: u32, status: *mut c_int) -> u32 {
    unimplemented!();
}

pub fn send_signal(pid: u32, sig: u32) -> u32 {
    unimplemented!();
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if sucessful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}
<commit_msg>provide noop windows implementation for wait_for_exit<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libc::c_int;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};

use error::Result;

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

//TODO: REALLY wait for exit
pub fn wait_for_exit(pid: u32, status: *mut c_int) -> u32 {
    0
}

pub fn send_signal(pid: u32, sig: u32) -> u32 {
    unimplemented!();
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if sucessful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = try!(Command::new(command).args(&args).status());
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}
<|endoftext|>


<commit_before>use std::task::TaskBuilder;
use std::io::IoError;
use std::collections::hashmap::{
    HashMap,
    Vacant,
    Occupied,
};

use serialize::json;
use serialize::json::DecoderError;
use time::{get_time, Timespec};

use url::Url;
use url::form_urlencoded::serialize_owned;

use http::client::RequestWriter;
use http::method::Get;

use state::{
    KnownUser,
    KnownChannel,
    BotUserId,
    BotChannelId
};
use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator,
    Format,
    StringValue
};
use irc::message::{
    IrcMessage
};

static DEER: &'static str = concat!(
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@\u000300,00@@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@\u000300,00@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@");

static BASE_URL: &'static str = "http://deer.satf.se/deerlist.php";

#[deriving(Decodable, Encodable, Clone)]
struct DeerApiResponse {
    irccode: String
}

#[deriving(Show)]
enum DeerApiFailure {
    NoResponseError(IoError),
    ResponseReadError(IoError),
    ResponseDecodeError,
    ResponseDeserializeError(DecoderError)
}

fn get_deer_nocache(deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let mut url = match Url::parse(BASE_URL) {
        Ok(url) => url,
        Err(_err) => unreachable!()
    };
    url.query = Some(serialize_owned(vec![
        (String::from_str("deer"), String::from_str(deer_name)),
    ][]));

    let request: RequestWriter = RequestWriter::new(Get, url).unwrap();

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_, io_error)) => return Err(NoResponseError(io_error))
    };
    let body = match response.read_to_end() {
        Ok(body) => body,
        Err(io_error) => return Err(ResponseReadError(io_error))
    };
    let body = match String::from_utf8(body) {
        Ok(body) => body,
        Err(_err) => return Err(ResponseDecodeError)
    };

    match json::decode::<DeerApiResponse>(body[]) {
        Ok(result) => Ok(result),
        Err(error) => Err(ResponseDeserializeError(error))
    }
}

fn get_deer(state: &mut DeerInternalState, deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let deer_name_alloc = String::from_str(deer_name);

    match state.cache.find(&deer_name_alloc) {
        Some(result) => return Ok(result.clone()),
        None => ()
    }

    match get_deer_nocache(deer_name) {
        Ok(response) => {
            state.cache.insert(
                String::from_str(deer_name),
                response.clone());
            Ok(response)
        },
        Err(err) => Err(err)
    }
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMessage)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }
}

struct DeerInternalState {
    lines_sent: u64,
    last_request: Option<Timespec>,
    cache: HashMap<String, DeerApiResponse>,
    throttle_map: HashMap<(BotUserId, BotChannelId), Timespec>,
}

impl DeerInternalState {
    fn new() -> DeerInternalState {
        DeerInternalState {
            lines_sent: 0,
            last_request: None,
            cache: HashMap::new(),
            throttle_map: HashMap::new(),
        }
    }

    fn throttle_ok(&mut self, uid: &BotUserId, cid: &BotChannelId) -> bool {
        let now = get_time();

        let key = (uid.clone(), cid.clone());

        match self.throttle_map.entry(key.clone()) {
            Vacant(entry) => {
                info!("deer-throttle vacant: {}", key);
                entry.set(now);
                true
            },
            Occupied(mut entry) => {
                info!("deer-throttle occupied: {}", key);
                let duration = now - *entry.get();
                entry.set(now);
                info!("deer-throttle delta: {}", duration.num_seconds());
                60 < duration.num_seconds()
            }
        }
    }

    fn handle_command<'a>(&mut self, m: &CommandMapperDispatch, cmd: &'a DeerCommandType) {
        match *cmd {
            Deer(Some(ref deer_name)) => {
                match get_deer(self, deer_name[]) {
                    Ok(deer_data) => {
                        for deer_line in deer_data.irccode[].split('\n') {
                            m.reply(String::from_str(deer_line));
                            self.lines_sent += 1;
                        }
                    },
                    Err(err) => {
                        m.reply(format!("error: {}", err));
                    }
                }
            },
            Deer(None) => {
                for deer_line in DEER.split('\n') {
                    m.reply(String::from_str(deer_line));
                    self.lines_sent += 1;
                }
            },
            DeerStats => {
                m.reply(format!("lines sent: {}", self.lines_sent));
            }
        };
    }

    fn start(&mut self, rx: Receiver<(CommandMapperDispatch, IrcMessage)>) {
        for (m, _) in rx.iter() {
            match (m.source.clone(), m.target.clone(), parse_command(&m)) {
                (Some(KnownUser(source)), Some(KnownChannel(target)), Some(ref command)) => {
                    if self.throttle_ok(&source, &target) {
                        self.handle_command(&m, command);
                    } else {
                        m.reply(String::from_str("2deer4plus"))
                    }
                },
                _ => ()
            }
        }
    }
}

enum DeerCommandType {
    Deer(Option<String>),
    DeerStats
}

fn parse_command<'a>(m: &CommandMapperDispatch) -> Option<DeerCommandType> {
    let command_phrase = match m.command() {
        Some(command_phrase) => command_phrase,
        None => return None
    };
    match command_phrase.command[] {
        "deer" => Some(Deer(match command_phrase.args.find(&"deername".to_string()) {
            Some(&StringValue(ref rest)) => {
                if rest.as_slice() == "" {
                    None
                } else {
                    Some(rest.clone())
                }
            }
            Some(_) => None,
            None => return None
        })),
        "deerstats" => Some(DeerStats),
        _ => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map_format(Format::from_str("deer {*deername}").unwrap());
        conf.map_format(Format::from_str("deer").unwrap());
        conf.map_format(Format::from_str("deerstats").unwrap());
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        TaskBuilder::new().named("plugin-deer").spawn(proc() {
            let mut deer_internal_state = DeerInternalState::new();
            deer_internal_state.start(rx);
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        match self.sender {
            Some(ref sender) => sender.send((m.clone(), message.clone())),
            None => ()
        };
    }
}
<commit_msg>Don't throttle stats<commit_after>use std::task::TaskBuilder;
use std::io::IoError;
use std::collections::HashMap;

use serialize::json;
use serialize::json::DecoderError;
use time::{get_time, Timespec};

use url::Url;
use url::form_urlencoded::serialize_owned;

use http::client::RequestWriter;
use http::method::Get;

use state::{
    KnownUser,
    KnownChannel,
    BotUserId,
    BotChannelId
};
use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator,
    Format,
    StringValue
};
use irc::message::{
    IrcMessage
};

static DEER: &'static str = concat!(
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@\u000300,00@@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@\u000300,00@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@");

static BASE_URL: &'static str = "http://deer.satf.se/deerlist.php";

#[deriving(Decodable, Encodable, Clone)]
struct DeerApiResponse {
    irccode: String
}

#[deriving(Show)]
enum DeerApiFailure {
    NoResponseError(IoError),
    ResponseReadError(IoError),
    ResponseDecodeError,
    ResponseDeserializeError(DecoderError)
}

fn get_deer_nocache(deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let mut url = match Url::parse(BASE_URL) {
        Ok(url) => url,
        Err(_err) => unreachable!()
    };
    url.query = Some(serialize_owned(vec![
        (String::from_str("deer"), String::from_str(deer_name)),
    ][]));

    let request: RequestWriter = RequestWriter::new(Get, url).unwrap();

    let mut response = match request.read_response() {
        Ok(response) => response,
        Err((_, io_error)) => return Err(NoResponseError(io_error))
    };
    let body = match response.read_to_end() {
        Ok(body) => body,
        Err(io_error) => return Err(ResponseReadError(io_error))
    };
    let body = match String::from_utf8(body) {
        Ok(body) => body,
        Err(_err) => return Err(ResponseDecodeError)
    };

    match json::decode::<DeerApiResponse>(body[]) {
        Ok(result) => Ok(result),
        Err(error) => Err(ResponseDeserializeError(error))
    }
}

fn get_deer(state: &mut DeerInternalState, deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let deer_name_alloc = String::from_str(deer_name);

    match state.cache.find(&deer_name_alloc) {
        Some(result) => return Ok(result.clone()),
        None => ()
    }

    match get_deer_nocache(deer_name) {
        Ok(response) => {
            state.cache.insert(
                String::from_str(deer_name),
                response.clone());
            Ok(response)
        },
        Err(err) => Err(err)
    }
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMessage)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }
}

struct DeerInternalState {
    lines_sent: u64,
    last_request: Option<Timespec>,
    cache: HashMap<String, DeerApiResponse>,
    throttle_map: HashMap<(BotUserId, BotChannelId), Timespec>,
}

impl DeerInternalState {
    fn new() -> DeerInternalState {
        DeerInternalState {
            lines_sent: 0,
            last_request: None,
            cache: HashMap::new(),
            throttle_map: HashMap::new(),
        }
    }

    fn throttle_ok(&mut self, uid: BotUserId, cid: BotChannelId) -> bool {
        match self.throttle_map.find(&(uid, cid)) {
            Some(entry) => 60 < (get_time() - *entry).num_seconds(),
            None => true
        }
    }

    fn throttle_bump(&mut self, uid: BotUserId, cid: BotChannelId) {
        self.throttle_map.insert((uid, cid), get_time());
    }

    fn handle_command<'a>(&mut self, m: &CommandMapperDispatch, cmd: &'a DeerCommandType) {
        let (source, target) = match (m.source.clone(), m.target.clone()) {
            (Some(KnownUser(source)), Some(KnownChannel(target))) => (source, target),
            _ => return
        };

        if let Deer(_) = *cmd {
            if !self.throttle_ok(source, target) {
                m.reply(String::from_str("2deer4plus"));
                return;
            }
        }
        match *cmd {
            Deer(Some(ref deer_name)) => {
                match get_deer(self, deer_name[]) {
                    Ok(deer_data) => {
                        for deer_line in deer_data.irccode[].split('\n') {
                            m.reply(String::from_str(deer_line));
                            self.lines_sent += 1;
                        }
                        self.throttle_bump(source, target);
                    },
                    Err(err) => {
                        m.reply(format!("error: {}", err));
                    }
                }
            },
            Deer(None) => {
                for deer_line in DEER.split('\n') {
                    m.reply(String::from_str(deer_line));
                    self.lines_sent += 1;
                }
                self.throttle_bump(source, target);
            },
            DeerStats => {
                m.reply(format!("lines sent: {}", self.lines_sent));
            }
        };
    }

    fn start(&mut self, rx: Receiver<(CommandMapperDispatch, IrcMessage)>) {
        for (m, _) in rx.iter() {
            match parse_command(&m) {
                Some(ref command) => self.handle_command(&m, command),
                None => ()
            }
        }
    }
}

enum DeerCommandType {
    Deer(Option<String>),
    DeerStats
}

fn parse_command<'a>(m: &CommandMapperDispatch) -> Option<DeerCommandType> {
    let command_phrase = match m.command() {
        Some(command_phrase) => command_phrase,
        None => return None
    };
    match command_phrase.command[] {
        "deer" => Some(Deer(match command_phrase.args.find(&"deername".to_string()) {
            Some(&StringValue(ref rest)) => {
                if rest.as_slice() == "" {
                    None
                } else {
                    Some(rest.clone())
                }
            }
            Some(_) => None,
            None => return None
        })),
        "deerstats" => Some(DeerStats),
        _ => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map_format(Format::from_str("deer {*deername}").unwrap());
        conf.map_format(Format::from_str("deer").unwrap());
        conf.map_format(Format::from_str("deerstats").unwrap());
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        TaskBuilder::new().named("plugin-deer").spawn(proc() {
            let mut deer_internal_state = DeerInternalState::new();
            deer_internal_state.start(rx);
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        match self.sender {
            Some(ref sender) => sender.send((m.clone(), message.clone())),
            None => ()
        };
    }
}
<|endoftext|>


<commit_before>//! Generic WebSocket protocol implementation

mod frame;
mod message;

pub use self::message::Message;

use std::collections::VecDeque;
use std::io::{Read, Write, Error as IoError, ErrorKind as IoErrorKind};
use std::mem::replace;
use std::result::Result as StdResult;

use error::{Error, Result};
use self::message::{IncompleteMessage, IncompleteMessageType};
use self::frame::{Frame, FrameSocket};
use self::frame::coding::{OpCode, Data as OpData, Control as OpCtl, CloseCode};

/// Indicates a Client or Server role of the websocket
#[derive(Debug, Clone, Copy)]
pub enum Role {
    /// This socket is a server
    Server,
    /// This socket is a client
    Client,
}

/// WebSocket input-output stream
pub struct WebSocket<Stream> {
    /// Server or client?
    role: Role,
    /// The underlying socket.
    socket: FrameSocket<Stream>,
    /// The state of processing, either "active" or "closing".
    state: WebSocketState,
    /// Receive: an incomplete message being processed.
    incomplete: Option<IncompleteMessage>,
    /// Send: a data send queue.
    send_queue: VecDeque<Frame>,
    /// Send: an OOB pong message.
    pong: Option<Frame>,
}

impl<Stream> WebSocket<Stream>
    where Stream: Read + Write
{

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_raw_socket(stream: Stream, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::new(stream), role)
    }

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_partially_read(stream: Stream, part: Vec<u8>, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::from_partially_read(stream, part), role)
    }

    /// Read a message from stream, if possible.
    pub fn read_message(&mut self) -> Result<Message> {
        loop {
            let write_blocks = self.send_pending().no_block()?.is_none();
            let read = self.read_message_frame();
            let frame = if write_blocks {
                Some(read?)
            } else {
                read.no_block()?
            };
            if let Some(Some(message)) = frame {
                debug!("Received message {}", message);
                return Ok(message)
            }
        }
    }

    /// Send a message to stream, if possible.
    pub fn write_message(&mut self, message: Message) -> Result<()> {
        let frame = {
            let opcode = match message {
                Message::Text(_) => OpData::Text,
                Message::Binary(_) => OpData::Binary,
            };
            Frame::message(message.into_data(), OpCode::Data(opcode), true)
        };
        self.send_queue.push_back(frame);
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Close the connection.
    pub fn close(&mut self) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByUs;
                let frame = Frame::close(None);
                self.send_queue.push_back(frame);
            }
            _ => {
                // already closed, nothing to do
            }
        }
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Convert a frame socket into a WebSocket.
    fn from_frame_socket(socket: FrameSocket<Stream>, role: Role) -> Self {
        WebSocket {
            role: role,
            socket: socket,
            state: WebSocketState::Active,
            incomplete: None,
            send_queue: VecDeque::new(),
            pong: None,
        }
    }

    /// Try to decode one message frame. May return None.
    fn read_message_frame(&mut self) -> Result<Option<Message>> {
        if let Some(mut frame) = self.socket.read_frame()? {

            // MUST be 0 unless an extension is negotiated that defines meanings
            // for non-zero values.  If a nonzero value is received and none of
            // the negotiated extensions defines the meaning of such a nonzero
            // value, the receiving endpoint MUST _Fail the WebSocket
            // Connection_.
            if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
                return Err(Error::Protocol("Reserved bits are non-zero".into()))
            }

            match self.role {
                Role::Server => {
                    if frame.is_masked() {
                        // A server MUST remove masking for data frames received from a client
                        // as described in Section 5.3. (RFC 6455)
                        frame.remove_mask()
                    } else {
                        // The server MUST close the connection upon receiving a
                        // frame that is not masked. (RFC 6455)
                        return Err(Error::Protocol("Received an unmasked frame from client".into()))
                    }
                }
                Role::Client => {
                    if frame.is_masked() {
                        // A client MUST close a connection if it detects a masked frame. (RFC 6455)
                        return Err(Error::Protocol("Received a masked frame from server".into()))
                    }
                }
            }

            match frame.opcode() {

                OpCode::Control(ctl) => {
                    (match ctl {
                        // All control frames MUST have a payload length of 125 bytes or less
                        // and MUST NOT be fragmented. (RFC 6455)
                        _ if !frame.is_final() => {
                            Err(Error::Protocol("Fragmented control frame".into()))
                        }
                        _ if frame.payload().len() > 125 => {
                            Err(Error::Protocol("Control frame too big".into()))
                        }
                        OpCtl::Close => {
                            self.do_close(frame.into_close()?)
                        }
                        OpCtl::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown control frame type {}", i).into()))
                        }
                        OpCtl::Ping | OpCtl::Pong if !self.state.is_active() => {
                            // No ping processing while closing.
                            Ok(())
                        }
                        OpCtl::Ping => {
                            self.do_ping(frame.into_data())
                        }
                        OpCtl::Pong => {
                            self.do_pong(frame.into_data())
                        }
                    }).map(|_| None)
                }

                OpCode::Data(_) if !self.state.is_active() => {
                    // No data processing while closing.
                    Ok(None)
                }

                OpCode::Data(data) => {
                    let fin = frame.is_final();
                    match data {
                        OpData::Continue => {
                            if let Some(ref mut msg) = self.incomplete {
                                // TODO if msg too big
                                msg.extend(frame.into_data())?;
                            } else {
                                return Err(Error::Protocol("Continue frame but nothing to continue".into()))
                            }
                            if fin {
                                Ok(Some(replace(&mut self.incomplete, None).unwrap().complete()?))
                            } else {
                                Ok(None)
                            }
                        }
                        c if self.incomplete.is_some() => {
                            Err(Error::Protocol(
                                format!("Received {} while waiting for more fragments", c).into()
                            ))
                        }
                        OpData::Text | OpData::Binary => {
                            let msg = {
                                let message_type = match data {
                                    OpData::Text => IncompleteMessageType::Text,
                                    OpData::Binary => IncompleteMessageType::Binary,
                                    _ => panic!("Bug: message is not text nor binary"),
                                };
                                let mut m = IncompleteMessage::new(message_type);
                                m.extend(frame.into_data())?;
                                m
                            };
                            if fin {
                                Ok(Some(msg.complete()?))
                            } else {
                                self.incomplete = Some(msg);
                                Ok(None)
                            }
                        }
                        OpData::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown data frame type {}", i).into()))
                        }
                    }
                }

            } // match opcode

        } else {
            Err(Error::Protocol("Connection reset without closing handshake".into()))
        }
    }

    /// Received a close frame.
    fn do_close(&mut self, close: Option<(CloseCode, String)>) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByPeer;
                let reply = if let Some((code, _)) = close {
                    if code.is_allowed() {
                        Frame::close(Some((CloseCode::Normal, "")))
                    } else {
                        Frame::close(Some((CloseCode::Protocol, "Protocol violation")))
                    }
                } else {
                    Frame::close(None)
                };
                self.send_queue.push_back(reply);
                Ok(())
            }
            WebSocketState::ClosedByPeer => {
                // It is already closed, just ignore.
                Ok(())
            }
            WebSocketState::ClosedByUs => {
                // We received a reply.
                match self.role {
                    Role::Client => {
                        // Client waits for the server to close the connection.
                        Ok(())
                    }
                    Role::Server => {
                        // Server closes the connection.
                        Err(Error::ConnectionClosed)
                    }
                }
            }
        }
    }

    /// Received a ping frame.
    fn do_ping(&mut self, ping: Vec<u8>) -> Result<()> {
        // If an endpoint receives a Ping frame and has not yet sent Pong
        // frame(s) in response to previous Ping frame(s), the endpoint MAY
        // elect to send a Pong frame for only the most recently processed Ping
        // frame. (RFC 6455)
        // We do exactly that, keeping a "queue" from one and only Pong frame.
        self.pong = Some(Frame::pong(ping));
        Ok(())
    }

    /// Received a pong frame.
    fn do_pong(&mut self, _: Vec<u8>) -> Result<()> {
        // A Pong frame MAY be sent unsolicited.  This serves as a
        // unidirectional heartbeat.  A response to an unsolicited Pong frame is
        // not expected. (RFC 6455)
        // Due to this, we just don't check pongs right now.
        // TODO: check if there was a reply to our ping at all...
        Ok(())
    }

    /// Flush the pending send queue.
    fn send_pending(&mut self) -> Result<()> {
        // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
        // response, unless it already received a Close frame. It SHOULD
        // respond with Pong frame as soon as is practical. (RFC 6455)
        if let Some(pong) = replace(&mut self.pong, None) {
            self.send_one_frame(pong)?;
        }
        // If we have any unsent frames, send them.
        while let Some(data) = self.send_queue.pop_front() {
            self.send_one_frame(data)?;
        }

        // If we're closing and there is nothing to send anymore, we should close the connection.
        match self.state {
            WebSocketState::ClosedByPeer if self.send_queue.is_empty() => {
                // The underlying TCP connection, in most normal cases, SHOULD be closed
                // first by the server, so that it holds the TIME_WAIT state and not the
                // client (as this would prevent it from re-opening the connection for 2
                // maximum segment lifetimes (2MSL), while there is no corresponding
                // server impact as a TIME_WAIT connection is immediately reopened upon
                // a new SYN with a higher seq number). (RFC 6455)
                match self.role {
                    Role::Client => Ok(()),
                    Role::Server => Err(Error::ConnectionClosed),
                }
            }
            _ => Ok(()),
        }
    }

    /// Send a single pending frame.
    fn send_one_frame(&mut self, mut frame: Frame) -> Result<()> {
        match self.role {
            Role::Server => {
            }
            Role::Client => {
                // 5.  If the data is being sent by the client, the frame(s) MUST be
                // masked as defined in Section 5.3. (RFC 6455)
                frame.set_mask();
            }
        }
        self.socket.write_frame(frame)?;
        Ok(())
    }

}

/// The current connection state.
enum WebSocketState {
    Active,
    ClosedByUs,
    ClosedByPeer,
}

impl WebSocketState {
    /// Tell if we're allowed to process normal messages.
    fn is_active(&self) -> bool {
        match *self {
            WebSocketState::Active => true,
            _ => false,
        }
    }
}

/// Non-blocking IO handling.
trait NonBlockingError: Sized {
    fn into_non_blocking(self) -> Option<Self>;
}

impl NonBlockingError for IoError {
    fn into_non_blocking(self) -> Option<Self> {
        match self.kind() {
            IoErrorKind::Interrupted => None,
            _ => Some(self),
        }
    }
}

impl NonBlockingError for Error {
    fn into_non_blocking(self) -> Option<Self> {
        match self {
            Error::Io(e) => e.into_non_blocking().map(|e| e.into()),
            x => Some(x),
        }
    }
}

/// Non-blocking IO wrapper.
trait NonBlockingResult {
    type Result;
    fn no_block(self) -> Self::Result;
}

impl<T, E> NonBlockingResult for StdResult<T, E>
where E : NonBlockingError
{
    type Result = StdResult<Option<T>, E>;
    fn no_block(self) -> Self::Result {
        match self {
            Ok(x) => Ok(Some(x)),
            Err(e) => match e.into_non_blocking() {
                Some(e) => Err(e),
                None => Ok(None),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{WebSocket, Role, Message};

    use std::io;
    use std::io::Cursor;

    struct WriteMoc<Stream>(Stream);

    impl<Stream> io::Write for WriteMoc<Stream> {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            Ok(buf.len())
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    impl<Stream: io::Read> io::Read for WriteMoc<Stream> {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            self.0.read(buf)
        }
    }

    #[test]
    fn receive_messages() {
        let incoming = Cursor::new(vec![
            0x01, 0x07,
            0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
            0x80, 0x06,
            0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
            0x82, 0x03,
            0x01, 0x02, 0x03,
        ]);
        let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client);
        assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into()));
        assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
    }

}
<commit_msg>protocol: fix bug with async handling<commit_after>//! Generic WebSocket protocol implementation

mod frame;
mod message;

pub use self::message::Message;

use std::collections::VecDeque;
use std::io::{Read, Write, Error as IoError, ErrorKind as IoErrorKind};
use std::mem::replace;
use std::result::Result as StdResult;

use error::{Error, Result};
use self::message::{IncompleteMessage, IncompleteMessageType};
use self::frame::{Frame, FrameSocket};
use self::frame::coding::{OpCode, Data as OpData, Control as OpCtl, CloseCode};

/// Indicates a Client or Server role of the websocket
#[derive(Debug, Clone, Copy)]
pub enum Role {
    /// This socket is a server
    Server,
    /// This socket is a client
    Client,
}

/// WebSocket input-output stream
pub struct WebSocket<Stream> {
    /// Server or client?
    role: Role,
    /// The underlying socket.
    socket: FrameSocket<Stream>,
    /// The state of processing, either "active" or "closing".
    state: WebSocketState,
    /// Receive: an incomplete message being processed.
    incomplete: Option<IncompleteMessage>,
    /// Send: a data send queue.
    send_queue: VecDeque<Frame>,
    /// Send: an OOB pong message.
    pong: Option<Frame>,
}

impl<Stream> WebSocket<Stream>
    where Stream: Read + Write
{

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_raw_socket(stream: Stream, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::new(stream), role)
    }

    /// Convert a raw socket into a WebSocket without performing a handshake.
    pub fn from_partially_read(stream: Stream, part: Vec<u8>, role: Role) -> Self {
        WebSocket::from_frame_socket(FrameSocket::from_partially_read(stream, part), role)
    }

    /// Read a message from stream, if possible.
    pub fn read_message(&mut self) -> Result<Message> {
        loop {
            let write_blocks = self.send_pending().no_block()?.is_none();
            let read = self.read_message_frame();
            let frame = if write_blocks {
                Some(read?)
            } else {
                read.no_block()?
            };
            if let Some(Some(message)) = frame {
                debug!("Received message {}", message);
                return Ok(message)
            }
        }
    }

    /// Send a message to stream, if possible.
    pub fn write_message(&mut self, message: Message) -> Result<()> {
        let frame = {
            let opcode = match message {
                Message::Text(_) => OpData::Text,
                Message::Binary(_) => OpData::Binary,
            };
            Frame::message(message.into_data(), OpCode::Data(opcode), true)
        };
        self.send_queue.push_back(frame);
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Close the connection.
    pub fn close(&mut self) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByUs;
                let frame = Frame::close(None);
                self.send_queue.push_back(frame);
            }
            _ => {
                // already closed, nothing to do
            }
        }
        self.send_pending().no_block()?;
        Ok(())
    }

    /// Convert a frame socket into a WebSocket.
    fn from_frame_socket(socket: FrameSocket<Stream>, role: Role) -> Self {
        WebSocket {
            role: role,
            socket: socket,
            state: WebSocketState::Active,
            incomplete: None,
            send_queue: VecDeque::new(),
            pong: None,
        }
    }

    /// Try to decode one message frame. May return None.
    fn read_message_frame(&mut self) -> Result<Option<Message>> {
        if let Some(mut frame) = self.socket.read_frame()? {

            // MUST be 0 unless an extension is negotiated that defines meanings
            // for non-zero values.  If a nonzero value is received and none of
            // the negotiated extensions defines the meaning of such a nonzero
            // value, the receiving endpoint MUST _Fail the WebSocket
            // Connection_.
            if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
                return Err(Error::Protocol("Reserved bits are non-zero".into()))
            }

            match self.role {
                Role::Server => {
                    if frame.is_masked() {
                        // A server MUST remove masking for data frames received from a client
                        // as described in Section 5.3. (RFC 6455)
                        frame.remove_mask()
                    } else {
                        // The server MUST close the connection upon receiving a
                        // frame that is not masked. (RFC 6455)
                        return Err(Error::Protocol("Received an unmasked frame from client".into()))
                    }
                }
                Role::Client => {
                    if frame.is_masked() {
                        // A client MUST close a connection if it detects a masked frame. (RFC 6455)
                        return Err(Error::Protocol("Received a masked frame from server".into()))
                    }
                }
            }

            match frame.opcode() {

                OpCode::Control(ctl) => {
                    (match ctl {
                        // All control frames MUST have a payload length of 125 bytes or less
                        // and MUST NOT be fragmented. (RFC 6455)
                        _ if !frame.is_final() => {
                            Err(Error::Protocol("Fragmented control frame".into()))
                        }
                        _ if frame.payload().len() > 125 => {
                            Err(Error::Protocol("Control frame too big".into()))
                        }
                        OpCtl::Close => {
                            self.do_close(frame.into_close()?)
                        }
                        OpCtl::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown control frame type {}", i).into()))
                        }
                        OpCtl::Ping | OpCtl::Pong if !self.state.is_active() => {
                            // No ping processing while closing.
                            Ok(())
                        }
                        OpCtl::Ping => {
                            self.do_ping(frame.into_data())
                        }
                        OpCtl::Pong => {
                            self.do_pong(frame.into_data())
                        }
                    }).map(|_| None)
                }

                OpCode::Data(_) if !self.state.is_active() => {
                    // No data processing while closing.
                    Ok(None)
                }

                OpCode::Data(data) => {
                    let fin = frame.is_final();
                    match data {
                        OpData::Continue => {
                            if let Some(ref mut msg) = self.incomplete {
                                // TODO if msg too big
                                msg.extend(frame.into_data())?;
                            } else {
                                return Err(Error::Protocol("Continue frame but nothing to continue".into()))
                            }
                            if fin {
                                Ok(Some(replace(&mut self.incomplete, None).unwrap().complete()?))
                            } else {
                                Ok(None)
                            }
                        }
                        c if self.incomplete.is_some() => {
                            Err(Error::Protocol(
                                format!("Received {} while waiting for more fragments", c).into()
                            ))
                        }
                        OpData::Text | OpData::Binary => {
                            let msg = {
                                let message_type = match data {
                                    OpData::Text => IncompleteMessageType::Text,
                                    OpData::Binary => IncompleteMessageType::Binary,
                                    _ => panic!("Bug: message is not text nor binary"),
                                };
                                let mut m = IncompleteMessage::new(message_type);
                                m.extend(frame.into_data())?;
                                m
                            };
                            if fin {
                                Ok(Some(msg.complete()?))
                            } else {
                                self.incomplete = Some(msg);
                                Ok(None)
                            }
                        }
                        OpData::Reserved(i) => {
                            Err(Error::Protocol(format!("Unknown data frame type {}", i).into()))
                        }
                    }
                }

            } // match opcode

        } else {
            Err(Error::Protocol("Connection reset without closing handshake".into()))
        }
    }

    /// Received a close frame.
    fn do_close(&mut self, close: Option<(CloseCode, String)>) -> Result<()> {
        match self.state {
            WebSocketState::Active => {
                self.state = WebSocketState::ClosedByPeer;
                let reply = if let Some((code, _)) = close {
                    if code.is_allowed() {
                        Frame::close(Some((CloseCode::Normal, "")))
                    } else {
                        Frame::close(Some((CloseCode::Protocol, "Protocol violation")))
                    }
                } else {
                    Frame::close(None)
                };
                self.send_queue.push_back(reply);
                Ok(())
            }
            WebSocketState::ClosedByPeer => {
                // It is already closed, just ignore.
                Ok(())
            }
            WebSocketState::ClosedByUs => {
                // We received a reply.
                match self.role {
                    Role::Client => {
                        // Client waits for the server to close the connection.
                        Ok(())
                    }
                    Role::Server => {
                        // Server closes the connection.
                        Err(Error::ConnectionClosed)
                    }
                }
            }
        }
    }

    /// Received a ping frame.
    fn do_ping(&mut self, ping: Vec<u8>) -> Result<()> {
        // If an endpoint receives a Ping frame and has not yet sent Pong
        // frame(s) in response to previous Ping frame(s), the endpoint MAY
        // elect to send a Pong frame for only the most recently processed Ping
        // frame. (RFC 6455)
        // We do exactly that, keeping a "queue" from one and only Pong frame.
        self.pong = Some(Frame::pong(ping));
        Ok(())
    }

    /// Received a pong frame.
    fn do_pong(&mut self, _: Vec<u8>) -> Result<()> {
        // A Pong frame MAY be sent unsolicited.  This serves as a
        // unidirectional heartbeat.  A response to an unsolicited Pong frame is
        // not expected. (RFC 6455)
        // Due to this, we just don't check pongs right now.
        // TODO: check if there was a reply to our ping at all...
        Ok(())
    }

    /// Flush the pending send queue.
    fn send_pending(&mut self) -> Result<()> {
        // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
        // response, unless it already received a Close frame. It SHOULD
        // respond with Pong frame as soon as is practical. (RFC 6455)
        if let Some(pong) = replace(&mut self.pong, None) {
            self.send_one_frame(pong)?;
        }
        // If we have any unsent frames, send them.
        while let Some(data) = self.send_queue.pop_front() {
            self.send_one_frame(data)?;
        }

        // If we're closing and there is nothing to send anymore, we should close the connection.
        match self.state {
            WebSocketState::ClosedByPeer if self.send_queue.is_empty() => {
                // The underlying TCP connection, in most normal cases, SHOULD be closed
                // first by the server, so that it holds the TIME_WAIT state and not the
                // client (as this would prevent it from re-opening the connection for 2
                // maximum segment lifetimes (2MSL), while there is no corresponding
                // server impact as a TIME_WAIT connection is immediately reopened upon
                // a new SYN with a higher seq number). (RFC 6455)
                match self.role {
                    Role::Client => Ok(()),
                    Role::Server => Err(Error::ConnectionClosed),
                }
            }
            _ => Ok(()),
        }
    }

    /// Send a single pending frame.
    fn send_one_frame(&mut self, mut frame: Frame) -> Result<()> {
        match self.role {
            Role::Server => {
            }
            Role::Client => {
                // 5.  If the data is being sent by the client, the frame(s) MUST be
                // masked as defined in Section 5.3. (RFC 6455)
                frame.set_mask();
            }
        }
        self.socket.write_frame(frame)?;
        Ok(())
    }

}

/// The current connection state.
enum WebSocketState {
    Active,
    ClosedByUs,
    ClosedByPeer,
}

impl WebSocketState {
    /// Tell if we're allowed to process normal messages.
    fn is_active(&self) -> bool {
        match *self {
            WebSocketState::Active => true,
            _ => false,
        }
    }
}

/// Non-blocking IO handling.
trait NonBlockingError: Sized {
    fn into_non_blocking(self) -> Option<Self>;
}

impl NonBlockingError for IoError {
    fn into_non_blocking(self) -> Option<Self> {
        match self.kind() {
            IoErrorKind::WouldBlock => None,
            _ => Some(self),
        }
    }
}

impl NonBlockingError for Error {
    fn into_non_blocking(self) -> Option<Self> {
        match self {
            Error::Io(e) => e.into_non_blocking().map(|e| e.into()),
            x => Some(x),
        }
    }
}

/// Non-blocking IO wrapper.
trait NonBlockingResult {
    type Result;
    fn no_block(self) -> Self::Result;
}

impl<T, E> NonBlockingResult for StdResult<T, E>
where E : NonBlockingError
{
    type Result = StdResult<Option<T>, E>;
    fn no_block(self) -> Self::Result {
        match self {
            Ok(x) => Ok(Some(x)),
            Err(e) => match e.into_non_blocking() {
                Some(e) => Err(e),
                None => Ok(None),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{WebSocket, Role, Message};

    use std::io;
    use std::io::Cursor;

    struct WriteMoc<Stream>(Stream);

    impl<Stream> io::Write for WriteMoc<Stream> {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            Ok(buf.len())
        }
        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    impl<Stream: io::Read> io::Read for WriteMoc<Stream> {
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            self.0.read(buf)
        }
    }

    #[test]
    fn receive_messages() {
        let incoming = Cursor::new(vec![
            0x01, 0x07,
            0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
            0x80, 0x06,
            0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
            0x82, 0x03,
            0x01, 0x02, 0x03,
        ]);
        let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client);
        assert_eq!(socket.read_message().unwrap(), Message::Text("Hello, World!".into()));
        assert_eq!(socket.read_message().unwrap(), Message::Binary(vec![0x01, 0x02, 0x03]));
    }

}
<|endoftext|>