<commit_before>use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::signal;
use super::property::Property;
use super::class::{self, Class};
use rustwlc::types::KeyMod;
use xcb::ffi::xproto::xcb_button_t;

#[derive(Clone, Debug)]
pub struct ButtonState {
    button: xcb_button_t,
    modifiers: KeyMod
}

#[derive(Clone, Debug)]
pub struct Button<'lua>(Table<'lua>);

impl Display for ButtonState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Button: {:p}", self)
    }
}

impl Default for ButtonState {
    fn default() -> Self {
        ButtonState {
            button: xcb_button_t::default(),
            modifiers: KeyMod::empty()
        }
    }
}

impl UserData for ButtonState {}

impl <'lua> Button<'lua> {
    pub fn button(&self) -> rlua::Result<Value<'lua>> {
        let button = self.state()?;
        Ok(Value::Integer(button.button as _))
    }

    pub fn set_button(&self, new_val: xcb_button_t) -> rlua::Result<()> {
        let mut button = self.state()?;
        button.button = new_val;
        self.0.set("data", button)?;
        Ok(())
    }

    pub fn modifiers(&self) -> rlua::Result<KeyMod> {
        let button = self.state()?;
        Ok(button.modifiers)
    }

    pub fn set_modifiers(&self, mods: Table<'lua>) -> rlua::Result<()> {
        use ::lua::mods_to_rust;
        let mut button = self.state()?;
        button.modifiers = mods_to_rust(mods)?;
        self.0.set("data", button)?;
        Ok(())
    }
}

impl <'lua> ToLua<'lua> for Button<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl_objectable!(Button, ButtonState);

/// Makes a new button stored in a table beside its signals
pub fn allocator(lua: &Lua) -> rlua::Result<Object> {
    let meta = lua.create_table();
    let class = class::button_class(lua)?;
    Ok(Button::new(lua, class)?
       .add_to_meta(meta)?
       .build())
}

pub fn new<'lua>(lua: &'lua Lua, _table: Table<'lua>)
                 -> rlua::Result<Object<'lua>> {
    allocator(lua)
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::new(lua, Some(Rc::new(allocator)), None, None)?
        .method("__call".into(), lua.create_function(new))?
        .property(Property::new("button".into(),
                                Some(lua.create_function(set_button)),
                                Some(lua.create_function(get_button)),
                                Some(lua.create_function(set_button))))?
        .property(Property::new("modifiers".into(),
                                Some(lua.create_function(set_modifiers)),
                                Some(lua.create_function(get_modifiers)),
                                Some(lua.create_function(set_modifiers))))?
        .save_class("button")?
        .build()
}

fn set_button<'lua>(lua: &'lua Lua, (table, val): (Table, Value))
                    -> rlua::Result<Value<'lua>> {
    use rlua::Value::*;
    let button = Button::cast(table.clone().into())?;
    match val {
        Number(num) => button.set_button(num as _)?,
        Integer(num) => button.set_button(num as _)?,
        _ => button.set_button(xcb_button_t::default())?
    }
    signal::emit_signal(lua,
                        table.into(),
                        "property::button".into(),
                        val)?;
    Ok(Value::Nil)
}

fn get_button<'lua>(_: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    Button::cast(table.into())?.button()
}

fn set_modifiers<'lua>(lua: &'lua Lua, (table, modifiers): (Table, Table))
                       -> rlua::Result<Value<'lua>> {
    let button = Button::cast(table.clone().into())?;
    button.set_modifiers(modifiers.clone())?;
    signal::emit_signal(lua,
                        table.into(),
                        "property::modifiers".into(),
                        modifiers)?;
    Ok(Value::Nil)
}

fn get_modifiers<'lua>(lua: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    use ::lua::mods_to_lua;
    mods_to_lua(lua, Button::cast(table.into())?.modifiers()?).map(Value::Table)
}

#[cfg(test)]
mod test {
    use rlua::{Table, Lua};
    use super::super::button;
    use super::super::object;

    #[test]
    fn button_object_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("button0", button::allocator(&lua).unwrap())
            .unwrap();
        lua.globals().set("button1", button::allocator(&lua).unwrap())
            .unwrap();
        lua.eval(r#"
assert(button0.button == 0)
assert(button1.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button1.button == 0)
assert(button0.button == 3)
"#, None).unwrap()
    }

    #[test]
    fn button_class_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.connect_signal("test", function(button) button.button = 2 end)
a_button.emit_signal("test")
assert(a_button.button == 2)
"#, None).unwrap()
    }

    #[test]
    fn button_property_test() {
        let lua = Lua::new();
        let button_class = button::init(&lua).unwrap();
        assert_eq!(button_class.properties().unwrap().len().unwrap(), 2);
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.button = 5
assert(a_button.button == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_remove_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
button0 = button()
assert(button0.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button0.button == 3)
button0.button = 0
button0.disconnect_signal("test")
button0.emit_signal("test")
assert(button0.button == 0)
"#, None).unwrap()
    }

    #[test]
    fn button_emit_signal_multiple_args() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap())
            .unwrap();
        lua.eval(r#"
 assert(a_button.button == 0)
 a_button.connect_signal("test", function(button, num) button.button = num end)
 a_button.emit_signal("test", 5)
 assert(a_button.button == 5)
 a_button.emit_signal("test", 0)
 assert(a_button.button == 0)
 "#, None).unwrap()
    }

    #[test]
    fn button_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap())
            .unwrap();
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS);
    }

    #[test]
    fn button_multiple_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap())
            .unwrap();
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps", "Mod2" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS | MOD_MOD2);
    }

    #[test]
    /// Tests that setting the button index property updates the
    /// callback for all instances of button
    fn button_index_property() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", button::allocator(&lua).unwrap())
            .unwrap();
        lua.eval::<()>(r#"
hit = false
button.set_index_miss_handler(function(button)
    hit = true
    return 5
end)
assert(not hit)
a = button.button
assert(a ~= 5)
assert(not hit)
a = a_button.aoeu
assert(hit)
assert(a == 5)
a = nil
hit = false
a = button().aoeu
assert(hit)
assert(a == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_modifiers_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
hit = false
a_button.connect_signal("property::modifiers", function(button) hit = true end)
a_button.emit_signal("property::modifiers")
assert(hit)
hit = false
assert(not hit)
button.button = nil
assert(not hit)
a_button.modifiers = { "Caps", "Mod2" }
assert(hit)
"#, None).unwrap()
    }

    #[test]
    fn button_button_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
hit = false
a_button.connect_signal("property::button", function(button) hit = true end)
a_button.emit_signal("property::button")
assert(hit)
hit = false
assert(not hit)
a_button.button = nil
assert(hit)
"#, None).unwrap()
    }

    #[test]
    fn button_test_valid() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
assert(a_button.valid)
getmetatable(a_button).__class = nil
assert(not a_button.valid)
"#, None).unwrap();
    }
}

<commit_msg>Moved allocator to Button::allocate<commit_after>use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::signal;
use super::property::Property;
use super::class::{self, Class};
use rustwlc::types::KeyMod;
use xcb::ffi::xproto::xcb_button_t;

#[derive(Clone, Debug)]
pub struct ButtonState {
    button: xcb_button_t,
    modifiers: KeyMod
}

#[derive(Clone, Debug)]
pub struct Button<'lua>(Table<'lua>);

impl Display for ButtonState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Button: {:p}", self)
    }
}

impl Default for ButtonState {
    fn default() -> Self {
        ButtonState {
            button: xcb_button_t::default(),
            modifiers: KeyMod::empty()
        }
    }
}

impl UserData for ButtonState {}

impl <'lua> Button<'lua> {
    fn allocate(lua: &Lua) -> rlua::Result<Object> {
        let meta = lua.create_table();
        let class = class::button_class(lua)?;
        Ok(Button::new(lua, class)?
           .add_to_meta(meta)?
           .build())
    }

    pub fn button(&self) -> rlua::Result<Value<'lua>> {
        let button = self.state()?;
        Ok(Value::Integer(button.button as _))
    }

    pub fn set_button(&self, new_val: xcb_button_t) -> rlua::Result<()> {
        let mut button = self.state()?;
        button.button = new_val;
        self.0.set("data", button)?;
        Ok(())
    }

    pub fn modifiers(&self) -> rlua::Result<KeyMod> {
        let button = self.state()?;
        Ok(button.modifiers)
    }

    pub fn set_modifiers(&self, mods: Table<'lua>) -> rlua::Result<()> {
        use ::lua::mods_to_rust;
        let mut button = self.state()?;
        button.modifiers = mods_to_rust(mods)?;
        self.0.set("data", button)?;
        Ok(())
    }
}

impl <'lua> ToLua<'lua> for Button<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl_objectable!(Button, ButtonState);

pub fn new<'lua>(lua: &'lua Lua, _table: Table<'lua>)
                 -> rlua::Result<Object<'lua>> {
    Button::allocate(lua)
}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::new(lua, Some(Rc::new(Button::allocate)), None, None)?
        .method("__call".into(), lua.create_function(new))?
        .property(Property::new("button".into(),
                                Some(lua.create_function(set_button)),
                                Some(lua.create_function(get_button)),
                                Some(lua.create_function(set_button))))?
        .property(Property::new("modifiers".into(),
                                Some(lua.create_function(set_modifiers)),
                                Some(lua.create_function(get_modifiers)),
                                Some(lua.create_function(set_modifiers))))?
        .save_class("button")?
        .build()
}

fn set_button<'lua>(lua: &'lua Lua, (table, val): (Table, Value))
                    -> rlua::Result<Value<'lua>> {
    use rlua::Value::*;
    let button = Button::cast(table.clone().into())?;
    match val {
        Number(num) => button.set_button(num as _)?,
        Integer(num) => button.set_button(num as _)?,
        _ => button.set_button(xcb_button_t::default())?
    }
    signal::emit_signal(lua,
                        table.into(),
                        "property::button".into(),
                        val)?;
    Ok(Value::Nil)
}

fn get_button<'lua>(_: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    Button::cast(table.into())?.button()
}

fn set_modifiers<'lua>(lua: &'lua Lua, (table, modifiers): (Table, Table))
                       -> rlua::Result<Value<'lua>> {
    let button = Button::cast(table.clone().into())?;
    button.set_modifiers(modifiers.clone())?;
    signal::emit_signal(lua,
                        table.into(),
                        "property::modifiers".into(),
                        modifiers)?;
    Ok(Value::Nil)
}

fn get_modifiers<'lua>(lua: &'lua Lua, table: Table<'lua>)
                    -> rlua::Result<Value<'lua>> {
    use ::lua::mods_to_lua;
    mods_to_lua(lua, Button::cast(table.into())?.modifiers()?).map(Value::Table)
}

#[cfg(test)]
mod test {
    use rlua::{Table, Lua};
    use super::super::button::{self, Button};
    use super::super::object;

    #[test]
    fn button_object_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("button0", Button::allocate(&lua).unwrap())
            .unwrap();
        lua.globals().set("button1", Button::allocate(&lua).unwrap())
            .unwrap();
        lua.eval(r#"
assert(button0.button == 0)
assert(button1.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button1.button == 0)
assert(button0.button == 3)
"#, None).unwrap()
    }

    #[test]
    fn button_class_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.connect_signal("test", function(button) button.button = 2 end)
a_button.emit_signal("test")
assert(a_button.button == 2)
"#, None).unwrap()
    }

    #[test]
    fn button_property_test() {
        let lua = Lua::new();
        let button_class = button::init(&lua).unwrap();
        assert_eq!(button_class.properties().unwrap().len().unwrap(), 2);
        lua.eval(r#"
a_button = button()
assert(a_button.button == 0)
a_button.button = 5
assert(a_button.button == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_remove_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval(r#"
button0 = button()
assert(button0.button == 0)
button0.connect_signal("test", function(button) button.button = 3 end)
button0.emit_signal("test")
assert(button0.button == 3)
button0.button = 0
button0.disconnect_signal("test")
button0.emit_signal("test")
assert(button0.button == 0)
"#, None).unwrap()
    }

    #[test]
    fn button_emit_signal_multiple_args() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", Button::allocate(&lua).unwrap())
            .unwrap();
        lua.eval(r#"
 assert(a_button.button == 0)
 a_button.connect_signal("test", function(button, num) button.button = num end)
 a_button.emit_signal("test", 5)
 assert(a_button.button == 5)
 a_button.emit_signal("test", 0)
 assert(a_button.button == 0)
 "#, None).unwrap()
    }

    #[test]
    fn button_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", Button::allocate(&lua).unwrap())
            .unwrap();
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS);
    }

    #[test]
    fn button_multiple_modifiers_test() {
        use rustwlc::*;
        use self::button::Button;
        use self::object::Objectable;
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", Button::allocate(&lua).unwrap())
            .unwrap();
        let button = Button::cast(lua.globals().get::<_, Table>("a_button")
                                  .unwrap().into()).unwrap();
        assert_eq!(button.modifiers().unwrap(), KeyMod::empty());
        lua.eval::<()>(r#"
a_button.modifiers = { "Caps", "Mod2" }
"#, None).unwrap();
        assert_eq!(button.modifiers().unwrap(), MOD_CAPS | MOD_MOD2);
    }

    #[test]
    /// Tests that setting the button index property updates the
    /// callback for all instances of button
    fn button_index_property() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.globals().set("a_button", Button::allocate(&lua).unwrap())
            .unwrap();
        lua.eval::<()>(r#"
hit = false
button.set_index_miss_handler(function(button)
    hit = true
    return 5
end)
assert(not hit)
a = button.button
assert(a ~= 5)
assert(not hit)
a = a_button.aoeu
assert(hit)
assert(a == 5)
a = nil
hit = false
a = button().aoeu
assert(hit)
assert(a == 5)
"#, None).unwrap()
    }

    #[test]
    fn button_modifiers_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
hit = false
a_button.connect_signal("property::modifiers", function(button) hit = true end)
a_button.emit_signal("property::modifiers")
assert(hit)
hit = false
assert(not hit)
button.button = nil
assert(not hit)
a_button.modifiers = { "Caps", "Mod2" }
assert(hit)
"#, None).unwrap()
    }

    #[test]
    fn button_button_signal_test() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
hit = false
a_button.connect_signal("property::button", function(button) hit = true end)
a_button.emit_signal("property::button")
assert(hit)
hit = false
assert(not hit)
a_button.button = nil
assert(hit)
"#, None).unwrap()
    }

    #[test]
    fn button_test_valid() {
        let lua = Lua::new();
        button::init(&lua).unwrap();
        lua.eval::<()>(r#"
a_button = button()
assert(a_button.valid)
getmetatable(a_button).__class = nil
assert(not a_button.valid)
"#, None).unwrap();
    }
}

<|endoftext|>


<commit_before>// Copyright 2014 The Rustdown Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::iter::FromIterator;
use std::string::String;
use std::slice::Items;
use std::slice::Slice;

#[deriving(Show)]
pub struct Blocks(Vec<Vec<String>>);

impl Blocks {
    pub fn len(&self) -> uint {
        match self { &Blocks(ref blocks) => blocks.len() }
    }

    #[inline]
    pub fn iter<'a>(&'a self) -> Items<'a,Vec<String>> {
        match self { &Blocks(ref blocks) => blocks.as_slice().iter() }
    }
}

impl Index<uint, Vec<String>> for Blocks {
    fn index<'a>(&'a self, index: &uint) -> &'a Vec<String> {
        let block = match self { &Blocks(ref blocks) => blocks.get(*index) };
        block
    }
}

impl Slice<Vec<String>> for Blocks {
    #[inline]
    fn as_slice<'a>(&'a self) -> &'a [Vec<String>] {
        let blocks = match self { &Blocks(ref blocks) => blocks.as_slice() };
        blocks
    }
}

impl FromIterator<String> for Blocks {
    fn from_iter<T: Iterator<String>>(mut iterator: T) -> Blocks {
        let mut blockbuf: Vec<String> = vec![];
        let mut blocks = iterator.fold(vec![], |mut vec, line| {
            if is_block_separator(line.as_slice()) {
                vec.push(blockbuf.clone());
                blockbuf = vec![];
            }
            else {
                blockbuf.push(line.as_slice().trim_right_chars('\n').to_string());
            }
            vec
        });
        blocks.push(blockbuf);
        Blocks(blocks)
    }
}

pub fn is_block_separator(s: &str) -> bool {
    let s = s.trim_right_chars('\n').trim_left_chars(' ').trim_left_chars('\t');
    s == ""
}

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

    #[test]
    fn test_determining_block_separators() {
        assert!(is_block_separator(""));
        assert!(is_block_separator("    "));
        assert!(is_block_separator("\t"));
        assert!(!is_block_separator(" ## t"));
    }
}
<commit_msg>add more block separator tests<commit_after>// Copyright 2014 The Rustdown Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::iter::FromIterator;
use std::string::String;
use std::slice::Items;
use std::slice::Slice;

#[deriving(Show)]
pub struct Blocks(Vec<Vec<String>>);

impl Blocks {
    pub fn len(&self) -> uint {
        match self { &Blocks(ref blocks) => blocks.len() }
    }

    #[inline]
    pub fn iter<'a>(&'a self) -> Items<'a,Vec<String>> {
        match self { &Blocks(ref blocks) => blocks.as_slice().iter() }
    }
}

impl Index<uint, Vec<String>> for Blocks {
    fn index<'a>(&'a self, index: &uint) -> &'a Vec<String> {
        let block = match self { &Blocks(ref blocks) => blocks.get(*index) };
        block
    }
}

impl Slice<Vec<String>> for Blocks {
    #[inline]
    fn as_slice<'a>(&'a self) -> &'a [Vec<String>] {
        let blocks = match self { &Blocks(ref blocks) => blocks.as_slice() };
        blocks
    }
}

impl FromIterator<String> for Blocks {
    fn from_iter<T: Iterator<String>>(mut iterator: T) -> Blocks {
        let mut blockbuf: Vec<String> = vec![];
        let mut blocks = iterator.fold(vec![], |mut vec, line| {
            if is_block_separator(line.as_slice()) {
                vec.push(blockbuf.clone());
                blockbuf = vec![];
            }
            else {
                blockbuf.push(line.as_slice().trim_right_chars('\n').to_string());
            }
            vec
        });
        blocks.push(blockbuf);
        Blocks(blocks)
    }
}

pub fn is_block_separator(s: &str) -> bool {
    let s = s.trim_right_chars('\n').trim_left_chars(' ').trim_left_chars('\t');
    s == ""
}

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

    #[test]
    fn test_determining_block_separators() {
        // Block separators
        assert!(is_block_separator(""));
        assert!(is_block_separator("    "));
        assert!(is_block_separator("\t"));
        // Not block separators
        assert!(!is_block_separator(" ## t"));
        assert!(!is_block_separator("A line with text"));
        assert!(!is_block_separator("- a bullet"));
        assert!(!is_block_separator("1. A numeric bullet"));
    }
}
<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::io;
use std::time::Duration;

use types::*;

use super::device::*;
use super::ffi::*;

pub struct CompletionPort<H: DeviceHandler> {
    handle: HANDLE,
    handlers: HashMap<DeviceId, H>,
}

impl<H: DeviceHandler> CompletionPort<H> {

    pub fn new() -> io::Result<CompletionPort<H>> {
        let handle = checked_handle! { not_null =>
            CreateIoCompletionPort(
                INVALID_HANDLE_VALUE,
                0 as HANDLE,
                0 as ULONG_PTR,
                0)
        };
        Ok(CompletionPort {
            handle: handle,
            handlers: HashMap::new(),
        })
    }

    pub fn handler(&mut self, dev: &DeviceId) -> Option<&mut H> {
        self.handlers.get_mut(dev)
    }

    pub fn attach(&mut self, mut handler: H) -> io::Result<DeviceId> {
        let handle = handler.device().handle();
        let id = handler.device().id();
        unsafe {
            let rc = CreateIoCompletionPort(
                handle,
                self.handle,
                id as ULONG_PTR,
                0);
            if rc == 0 as HANDLE {
                return Err(io::Error::last_os_error());
            }
        }
        try!(handler.process_event(Event::Ready));
        self.handlers.insert(id, handler);
        Ok(id)
    }

    pub fn process_event(&mut self, timeout: &Duration) -> io::Result<DeviceId> {
        let mut nbytes: DWORD = 0;
        let mut key: ULONG_PTR = 0 as ULONG_PTR;
        let mut overlapped: LPOVERLAPPED = 0 as LPOVERLAPPED;
        let timeout_millis =
        	(timeout.as_secs() as DWORD * 1000) +
        	(timeout.subsec_nanos() as DWORD / 1000000);
        unsafe {
            let rc = GetQueuedCompletionStatus(
        		self.handle,
             	&mut nbytes as LPDWORD,
             	&mut key as PULONG_PTR,
             	&mut overlapped as *mut LPOVERLAPPED,
          	 	timeout_millis);
            if rc == 0 {
                return Err(io::Error::last_os_error());
            }
        };
        let id = key as DeviceId;
        let was_closed = {
            let handler = self.handlers.get_mut(&id).unwrap();
            if let Some(event) = handler.device().process_event() {
            	if let Err(e) = handler.process_event(event) {
            	    error!("unexpected error while processing IO event: {:?}", e);
            	    info!("closing connection to handle {} due to IO errors", id);
            	    try!(handler.device().close());
            	}
            }
          	handler.device().is_closed()
        };
        if was_closed {
            self.handlers.remove(&id);
        }
        Ok(id)
    }

    pub fn is_timeout_error(&self, error: &io::Error) -> bool {
        error.raw_os_error() == Some(258)
    }
}

#[cfg(test)]
mod test {

    use std::fs::File;
    use std::io;
	use std::io::{Read, Write};
	use std::path::Path;
	use std::time::Duration;

    use tempdir::TempDir;

	use io::device::*;

    use super::*;

	#[test]
	fn should_read_device() {
	    with_file_content("should_read_device", "This is a file with some content", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(FileReader::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
        });
	}

	#[test]
	fn should_write_device() {
	    with_file_content("should_write_device", "", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(FileWriter::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    assert_file_contains(path, "This is a file with some content");
        });
	}

	#[test]
	fn should_write_device_concurrently() {
	    with_file_content("should_write_device_concurrently", "", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(ParallelFileWriter::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    // Since no offset is given when writing, both writes will start at the begining
		    // of the file. This is pointless for serial and network ports.
		    assert_file_contains(path, "This is Sparta with some content");
        });
	}

	fn with_file_content<F: FnOnce(&Path)>(name: &str, content: &str, f: F) {
	    let tmp_dir = TempDir::new("fv").expect("create temp dir");
	    let file_path = tmp_dir.path().join(name);
	    {
	    	let mut file = File::create(&file_path).expect("create temp file");
	    	write!(file, "{}", content).unwrap();
	    }
	    f(&file_path);
	}

	fn assert_file_contains(path: &Path, content: &str) {
	    let mut file = File::open(path).expect("open file");
	    let mut line = String::new();
	    file.read_to_string(&mut line).expect("read file");
	    assert_eq!(line, content);
	}

	struct FileReader { dev: Device }

	impl FileReader {
	    fn new(dev: Device) -> FileReader {
	        FileReader { dev: dev }
	    }
	}

	impl DeviceHandler for FileReader {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => self.dev.request_read(),
    	        Event::BytesRead(n) => {
    	            assert_eq!(n, 32);
    	            assert_eq!(self.dev.recv_bytes(), b"This is a file with some content");
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}

	struct FileWriter { dev: Device }

	impl FileWriter {
	    fn new(dev: Device) -> FileWriter {
	        FileWriter { dev: dev }
	    }
	}

	impl DeviceHandler for FileWriter {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => self.dev.request_write(b"This is a file with some content"),
    	        Event::BytesWritten(n) => {
    	            assert_eq!(n, 32);
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}

	struct ParallelFileWriter { dev: Device, written: usize }

	impl ParallelFileWriter {
	    fn new(dev: Device) -> ParallelFileWriter {
	        ParallelFileWriter { dev: dev, written: 0 }
	    }
	}

	impl DeviceHandler for ParallelFileWriter {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => {
    	            try!(self.dev.request_write(b"This is a file with some content"));
    	            self.dev.request_write(b"This is Sparta")
    	        }
    	        Event::BytesWritten(n) if self.written == 0 => {
    	            assert_eq!(n, 32);
    	            self.written += n;
    	            Ok(())
    	        }
    	        Event::BytesWritten(n) if self.written > 0 => {
    	            assert_eq!(n, 14);
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}
}<commit_msg>Include device name in some logging events<commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::io;
use std::time::Duration;

use types::*;

use super::device::*;
use super::ffi::*;

pub struct CompletionPort<H: DeviceHandler> {
    handle: HANDLE,
    handlers: HashMap<DeviceId, H>,
}

impl<H: DeviceHandler> CompletionPort<H> {

    pub fn new() -> io::Result<CompletionPort<H>> {
        let handle = checked_handle! { not_null =>
            CreateIoCompletionPort(
                INVALID_HANDLE_VALUE,
                0 as HANDLE,
                0 as ULONG_PTR,
                0)
        };
        Ok(CompletionPort {
            handle: handle,
            handlers: HashMap::new(),
        })
    }

    pub fn handler(&mut self, dev: &DeviceId) -> Option<&mut H> {
        self.handlers.get_mut(dev)
    }

    pub fn attach(&mut self, mut handler: H) -> io::Result<DeviceId> {
        let handle = handler.device().handle();
        let id = handler.device().id();
        unsafe {
            let rc = CreateIoCompletionPort(
                handle,
                self.handle,
                id as ULONG_PTR,
                0);
            if rc == 0 as HANDLE {
                return Err(io::Error::last_os_error());
            }
        }
        try!(handler.process_event(Event::Ready));
        self.handlers.insert(id, handler);
        Ok(id)
    }

    pub fn process_event(&mut self, timeout: &Duration) -> io::Result<DeviceId> {
        let mut nbytes: DWORD = 0;
        let mut key: ULONG_PTR = 0 as ULONG_PTR;
        let mut overlapped: LPOVERLAPPED = 0 as LPOVERLAPPED;
        let timeout_millis =
        	(timeout.as_secs() as DWORD * 1000) +
        	(timeout.subsec_nanos() as DWORD / 1000000);
        unsafe {
            let rc = GetQueuedCompletionStatus(
        		self.handle,
             	&mut nbytes as LPDWORD,
             	&mut key as PULONG_PTR,
             	&mut overlapped as *mut LPOVERLAPPED,
          	 	timeout_millis);
            if rc == 0 {
                return Err(io::Error::last_os_error());
            }
        };
        let id = key as DeviceId;
        let was_closed = {
            let handler = self.handlers.get_mut(&id).unwrap();
            if let Some(event) = handler.device().process_event() {
            	if let Err(e) = handler.process_event(event) {
            	    error!("unexpected error while processing IO event from device {}: {:?}",
            	        handler.device().name(), e);
            	    info!("closing connection to device {} due to IO errors",
            	        handler.device().name());
            	    try!(handler.device().close());
            	}
            }
          	handler.device().is_closed()
        };
        if was_closed {
            self.handlers.remove(&id);
        }
        Ok(id)
    }

    pub fn is_timeout_error(&self, error: &io::Error) -> bool {
        error.raw_os_error() == Some(258)
    }
}

#[cfg(test)]
mod test {

    use std::fs::File;
    use std::io;
	use std::io::{Read, Write};
	use std::path::Path;
	use std::time::Duration;

    use tempdir::TempDir;

	use io::device::*;

    use super::*;

	#[test]
	fn should_read_device() {
	    with_file_content("should_read_device", "This is a file with some content", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(FileReader::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
        });
	}

	#[test]
	fn should_write_device() {
	    with_file_content("should_write_device", "", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(FileWriter::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    assert_file_contains(path, "This is a file with some content");
        });
	}

	#[test]
	fn should_write_device_concurrently() {
	    with_file_content("should_write_device_concurrently", "", |path| {
		    let mut iocp = CompletionPort::new().unwrap();
	        let file = Device::open(path).unwrap();
	        iocp.attach(ParallelFileWriter::new(file)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    iocp.process_event(&Duration::from_millis(100)).unwrap();
		    // Since no offset is given when writing, both writes will start at the begining
		    // of the file. This is pointless for serial and network ports.
		    assert_file_contains(path, "This is Sparta with some content");
        });
	}

	fn with_file_content<F: FnOnce(&Path)>(name: &str, content: &str, f: F) {
	    let tmp_dir = TempDir::new("fv").expect("create temp dir");
	    let file_path = tmp_dir.path().join(name);
	    {
	    	let mut file = File::create(&file_path).expect("create temp file");
	    	write!(file, "{}", content).unwrap();
	    }
	    f(&file_path);
	}

	fn assert_file_contains(path: &Path, content: &str) {
	    let mut file = File::open(path).expect("open file");
	    let mut line = String::new();
	    file.read_to_string(&mut line).expect("read file");
	    assert_eq!(line, content);
	}

	struct FileReader { dev: Device }

	impl FileReader {
	    fn new(dev: Device) -> FileReader {
	        FileReader { dev: dev }
	    }
	}

	impl DeviceHandler for FileReader {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => self.dev.request_read(),
    	        Event::BytesRead(n) => {
    	            assert_eq!(n, 32);
    	            assert_eq!(self.dev.recv_bytes(), b"This is a file with some content");
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}

	struct FileWriter { dev: Device }

	impl FileWriter {
	    fn new(dev: Device) -> FileWriter {
	        FileWriter { dev: dev }
	    }
	}

	impl DeviceHandler for FileWriter {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => self.dev.request_write(b"This is a file with some content"),
    	        Event::BytesWritten(n) => {
    	            assert_eq!(n, 32);
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}

	struct ParallelFileWriter { dev: Device, written: usize }

	impl ParallelFileWriter {
	    fn new(dev: Device) -> ParallelFileWriter {
	        ParallelFileWriter { dev: dev, written: 0 }
	    }
	}

	impl DeviceHandler for ParallelFileWriter {

	    fn device(&mut self) -> &mut Device { &mut self.dev }

    	fn process_event(&mut self, event: Event) -> io::Result<()> {
    	    match event {
    	        Event::Ready => {
    	            try!(self.dev.request_write(b"This is a file with some content"));
    	            self.dev.request_write(b"This is Sparta")
    	        }
    	        Event::BytesWritten(n) if self.written == 0 => {
    	            assert_eq!(n, 32);
    	            self.written += n;
    	            Ok(())
    	        }
    	        Event::BytesWritten(n) if self.written > 0 => {
    	            assert_eq!(n, 14);
    	            self.dev.close()
    	        }
    	        _ => Ok(()),
    	    }
    	}
	}
}<|endoftext|>


<commit_before>use cgmath::prelude::*;
use cgmath::Vector3;

use crate::color::Color;
use crate::Float;

use super::BSDFT;

#[derive(Debug)]
pub struct SpecularBRDF {
    color: Color,
}

impl SpecularBRDF {
    pub fn new(color: Color) -> Self {
        Self { color }
    }
}

impl BSDFT for SpecularBRDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let in_dir = Vector3::new(-out_dir.x, -out_dir.y, out_dir.z);
        (self.color, in_dir, 1.0)
    }
}

#[derive(Debug)]
pub struct SpecularBTDF {
    color: Color,
    eta: Float,
}

impl SpecularBTDF {
    pub fn new(color: Color, eta: Float) -> Self {
        Self { color, eta }
    }
}

impl BSDFT for SpecularBTDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let (n, eta) = if out_dir.z > 0.0 {
            (Vector3::unit_z(), 1.0 / self.eta)
        } else {
            (-Vector3::unit_z(), self.eta)
        };
        let cos_i = out_dir.dot(n);
        let sin2_i = (1.0 - cos_i.powi(2)).max(0.0);
        let sin2_t = eta.powi(2) * sin2_i;
        // Total internal reflection
        if sin2_t >= 1.0 {
            return (Color::black(), Vector3::unit_z(), 1.0);
        }
        let cos_t = (1.0 - sin2_t).sqrt();
        let in_dir = -out_dir * eta + (eta * cos_i - cos_t) * n;
        (self.color / in_dir.z.abs(), in_dir, 1.0)
    }
}

#[derive(Debug)]
pub struct FresnelBSDF {
    brdf: SpecularBRDF,
    btdf: SpecularBTDF,
}

impl FresnelBSDF {
    pub fn new(reflect: Color, transmit: Color, eta: Float) -> Self {
        let brdf = SpecularBRDF::new(reflect);
        let btdf = SpecularBTDF::new(transmit, eta);
        Self {
            brdf, btdf
        }
    }
}

fn fresnel_dielectric(mut cos_i: Float, eta_mat: Float) -> Float {
    let (eta_i, eta_t) = if cos_i > 0.0 {
        (eta_mat, 1.0)
    } else {
        cos_i = -cos_i;
        (1.0, eta_mat)
    };
    let sin2_i = (1.0 - cos_i.powi(2)).max(0.0);
    let sin2_t = (eta_i / eta_t).powi(2) * sin2_i;
    // Total internal reflection
    if sin2_t >= 1.0 {
        return 1.0
    }
    let cos_t = (1.0 - sin2_t).sqrt();
    let paral = (eta_t * cos_i - eta_i * cos_t) / (eta_t * cos_i + eta_i * cos_t);
    let perp = (eta_i * cos_i - eta_t * cos_t) / (eta_i * cos_i + eta_t * cos_t);
    (paral.powi(2) + perp.powi(2)) / 2.0
}

impl BSDFT for FresnelBSDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let f = fresnel_dielectric(out_dir.z, self.btdf.eta);
        if rand::random::<Float>() < f {
            // println!("reflect: {:?}", f);
            let (color, in_dir, pdf) = self.brdf.sample(out_dir);
            (f * color, in_dir, f * pdf)
        } else {
            // println!("transmit: {:?}, {:?}", f, out_dir.z);
            let (color, in_dir, pdf) = self.btdf.sample(out_dir);
            let ft = 1.0 - f;
            (ft * color, in_dir, ft * pdf)
        }
    }
}
<commit_msg>Fix fresnel indices of refraction<commit_after>use cgmath::prelude::*;
use cgmath::Vector3;

use crate::color::Color;
use crate::Float;

use super::BSDFT;

#[derive(Debug)]
pub struct SpecularBRDF {
    color: Color,
}

impl SpecularBRDF {
    pub fn new(color: Color) -> Self {
        Self { color }
    }
}

impl BSDFT for SpecularBRDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let in_dir = Vector3::new(-out_dir.x, -out_dir.y, out_dir.z);
        (self.color, in_dir, 1.0)
    }
}

#[derive(Debug)]
pub struct SpecularBTDF {
    color: Color,
    eta: Float,
}

impl SpecularBTDF {
    pub fn new(color: Color, eta: Float) -> Self {
        Self { color, eta }
    }
}

impl BSDFT for SpecularBTDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let (n, eta) = if out_dir.z > 0.0 {
            (Vector3::unit_z(), 1.0 / self.eta)
        } else {
            (-Vector3::unit_z(), self.eta)
        };
        let cos_i = out_dir.dot(n);
        let sin2_i = (1.0 - cos_i.powi(2)).max(0.0);
        let sin2_t = eta.powi(2) * sin2_i;
        // Total internal reflection
        if sin2_t >= 1.0 {
            return (Color::black(), Vector3::unit_z(), 1.0);
        }
        let cos_t = (1.0 - sin2_t).sqrt();
        let in_dir = -out_dir * eta + (eta * cos_i - cos_t) * n;
        (self.color / in_dir.z.abs(), in_dir, 1.0)
    }
}

#[derive(Debug)]
pub struct FresnelBSDF {
    brdf: SpecularBRDF,
    btdf: SpecularBTDF,
}

impl FresnelBSDF {
    pub fn new(reflect: Color, transmit: Color, eta: Float) -> Self {
        let brdf = SpecularBRDF::new(reflect);
        let btdf = SpecularBTDF::new(transmit, eta);
        Self {
            brdf, btdf
        }
    }
}

fn fresnel_dielectric(mut cos_i: Float, eta_mat: Float) -> Float {
    let (eta_i, eta_t) = if cos_i > 0.0 {
        (1.0, eta_mat)
    } else {
        cos_i = -cos_i;
        (eta_mat, 1.0)
    };
    let sin2_i = (1.0 - cos_i.powi(2)).max(0.0);
    let sin2_t = (eta_i / eta_t).powi(2) * sin2_i;
    // Total internal reflection
    if sin2_t >= 1.0 {
        return 1.0
    }
    let cos_t = (1.0 - sin2_t).sqrt();
    let paral = (eta_t * cos_i - eta_i * cos_t) / (eta_t * cos_i + eta_i * cos_t);
    let perp = (eta_i * cos_i - eta_t * cos_t) / (eta_i * cos_i + eta_t * cos_t);
    (paral.powi(2) + perp.powi(2)) / 2.0
}

impl BSDFT for FresnelBSDF {
    fn is_specular(&self) -> bool {
        true
    }

    fn eval(&self, _in_dir: Vector3<Float>, _out_dir: Vector3<Float>) -> Color {
        Color::black()
    }

    fn sample(&self, out_dir: Vector3<Float>) -> (Color, Vector3<Float>, Float) {
        let f = fresnel_dielectric(out_dir.z, self.btdf.eta);
        if rand::random::<Float>() < f {
            let (color, in_dir, pdf) = self.brdf.sample(out_dir);
            (f * color, in_dir, f * pdf)
        } else {
            let (color, in_dir, pdf) = self.btdf.sample(out_dir);
            let ft = 1.0 - f;
            (ft * color, in_dir, ft * pdf)
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]

#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
#[cfg(feature="dbus_enabled")]
extern crate dbus;
extern crate libc;
extern crate libudev;
extern crate nix;

#[cfg(test)]
extern crate quickcheck;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature="dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: &StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device
                   .devnode()
                   .and_then(|devnode| {
                                 device
                                     .devnum()
                                     .and_then(|devnum| {
                                                   Some((Device::from(devnum),
                                                         PathBuf::from(devnode)))
                                               })
                             });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn create_pid_file() -> StratisResult<File> {
    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)?;
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write_all(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!("Daemon already running with pid: {}", pid_str)))
        }
    }
}

fn run() -> StratisResult<()> {

    // Exit immediately if stratisd is already running
    let _ = create_pid_file()?;

    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    initialize_log(matches.is_present("debug"))
        .expect("This is the first and only invocation of this method; it must succeed.");

    // We must setup a udev listener before we initialize the
    // engine. It is possible that a device may appear after the engine has read the
    // /dev directory but before it has completed initialization. Unless the udev
    // event has been recorded, the engine will be unaware of the existence of the
    // device.
    // This is especially important since stratisd is required to be able to run
    // during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
                 fd: udev.as_raw_fd(),
                 revents: 0,
                 events: libc::POLLIN,
             });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                         fd: evt.get_pollable_fd(),
                         revents: 0,
                         events: libc::POLLIN,
                     });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature="dbus_enabled")]
    let (dbus_conn, mut tree, base_object_path, dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            while let Some(event) = udev.receive_event() {
                if let Some((device, devnode)) = handle_udev_add(&event) {

                    // If block evaluate returns an error we are going to ignore it as
                    // there is nothing we can do for a device we are getting errors with.
                    let pool_uuid = engine
                        .borrow_mut()
                        .block_evaluate(device, devnode)
                        .unwrap_or(None);

                    // We need to pretend that we aren't using the variable _pool_uuid so
                    // that we can conditionally compile out the register_pool when dbus
                    // is not enabled.
                    if let Some(_pool_uuid) = pool_uuid {
                                #[cfg(feature="dbus_enabled")]
                        libstratis::dbus_api::register_pool(&dbus_conn,
                                                            &Rc::clone(&engine),
                                                            &dbus_context,
                                                            &mut tree,
                                                            _pool_uuid,
                                                            &base_object_path)?;
                    }
                }
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature="dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                    .iter()
                    .filter(|pfd| pfd.revents != 0) {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                                 &item,
                                                                 &mut tree,
                                                                 &dbus_context) {
                        print_err(&From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let error_code = match run() {
        Ok(_) => 0,
        Err(err) => {
            print_err(&err);
            1
        }
    };
    exit(error_code);
}
<commit_msg>Move general startup tasks into main()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]

#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
#[cfg(feature="dbus_enabled")]
extern crate dbus;
extern crate libc;
extern crate libudev;
extern crate nix;

#[cfg(test)]
extern crate quickcheck;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg, ArgMatches};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature="dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: &StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device
                   .devnode()
                   .and_then(|devnode| {
                                 device
                                     .devnum()
                                     .and_then(|devnum| {
                                                   Some((Device::from(devnum),
                                                         PathBuf::from(devnode)))
                                               })
                             });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn trylock_pid_file() -> StratisResult<File> {
    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)?;
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write_all(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!("Daemon already running with pid: {}", pid_str)))
        }
    }
}

fn run(matches: &ArgMatches) -> StratisResult<()> {

    initialize_log(matches.is_present("debug"))
        .expect("This is the first and only invocation of this method; it must succeed.");

    // We must setup a udev listener before we initialize the
    // engine. It is possible that a device may appear after the engine has read the
    // /dev directory but before it has completed initialization. Unless the udev
    // event has been recorded, the engine will be unaware of the existence of the
    // device.
    // This is especially important since stratisd is required to be able to run
    // during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
                 fd: udev.as_raw_fd(),
                 revents: 0,
                 events: libc::POLLIN,
             });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                         fd: evt.get_pollable_fd(),
                         revents: 0,
                         events: libc::POLLIN,
                     });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature="dbus_enabled")]
    let (dbus_conn, mut tree, base_object_path, dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            while let Some(event) = udev.receive_event() {
                if let Some((device, devnode)) = handle_udev_add(&event) {

                    // If block evaluate returns an error we are going to ignore it as
                    // there is nothing we can do for a device we are getting errors with.
                    let pool_uuid = engine
                        .borrow_mut()
                        .block_evaluate(device, devnode)
                        .unwrap_or(None);

                    // We need to pretend that we aren't using the variable _pool_uuid so
                    // that we can conditionally compile out the register_pool when dbus
                    // is not enabled.
                    if let Some(_pool_uuid) = pool_uuid {
                                #[cfg(feature="dbus_enabled")]
                        libstratis::dbus_api::register_pool(&dbus_conn,
                                                            &Rc::clone(&engine),
                                                            &dbus_context,
                                                            &mut tree,
                                                            _pool_uuid,
                                                            &base_object_path)?;
                    }
                }
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature="dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                    .iter()
                    .filter(|pfd| pfd.revents != 0) {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                                 &item,
                                                                 &mut tree,
                                                                 &dbus_context) {
                        print_err(&From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    let result = trylock_pid_file().and_then(|_pidfile| run(&matches));
    if let Err(err) = result {
        print_err(&err);
        exit(1);
    } else {
        exit(0);
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{self, Read, Write};

extern crate byteorder;
use byteorder::{LittleEndian, WriteBytesExt};
extern crate idchoppers;
use idchoppers::errors::{Error, Result};
extern crate svg;
use svg::Document;
use svg::node::Node;
use svg::node::element::{Group, Line, Path, Rectangle, Style};
use svg::node::element::path::Data;
extern crate termcolor;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn main() {
    match run() {
        Ok(()) => {}
        Err(err) => {
            drop(write_err(err));
        }
    }
}

fn write_err(err: Error) -> Result<()> {
    let mut stderr = StandardStream::stderr(ColorChoice::Auto);
    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
    write!(&mut stderr, "error: ")?;
    stderr.set_color(&ColorSpec::new())?;
    writeln!(&mut stderr, "{}", err)?;
    Ok(())
}

fn run() -> Result<()> {
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf)?;

    let wad = try!(idchoppers::parse_wad(buf.as_slice()));
    println!("found {:?}, {:?}, {:?}", wad.header.identification, wad.header.numlumps, wad.header.infotableofs);
    for map_range in wad.iter_maps() {
        let bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        match bare_map {
            // TODO interesting diagnostic: mix of map formats in the same wad
            idchoppers::BareMap::Doom(map) => {
                println!("{} - Doom format map", map_range.name);
            }
            idchoppers::BareMap::Hexen(map) => {
                println!("{} - Hexen format map", map_range.name);
            }
        }
    }

    // FIXME this also catches F1_START etc, dammit
    for entry in wad.iter_entries_between("F_START", "F_END") {
        println!("{}", entry.name);
    }

    let texture_entries;
    if let Some(texbuf) = wad.first_entry("TEXTURE1") {
        texture_entries = try!(idchoppers::parse_texturex_names(texbuf));
    }
    else if let Some(texbuf) = wad.first_entry("TEXTURE2") {
        texture_entries = try!(idchoppers::parse_texturex_names(texbuf));
    }
    else {
        texture_entries = vec![];
    }
    for entry in texture_entries.iter() {
        println!("{}", entry.name);
    }
    return Ok(());
    let mut buffer = Vec::new();
    let mut directory = Vec::new();
    let mut filepos: usize = 0;
    for map_range in wad.iter_maps() {
        let mut bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        if let idchoppers::BareMap::Doom(mut map) = bare_map {
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: 0,
                name: wad.directory[map_range.marker_index].name,
            });

            // Things
            for thing in map.things.iter_mut() {
                thing.y = -thing.y;
                thing.angle = -thing.angle;
                thing.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "THINGS",
            });
            filepos = buffer.len();

            // Lines
            for line in map.lines.iter_mut() {
                std::mem::swap(&mut line.v0, &mut line.v1);
                line.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "LINEDEFS",
            });
            filepos = buffer.len();

            // Sides
            for side in map.sides.iter_mut() {
                side.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SIDEDEFS",
            });
            filepos = buffer.len();

            // Vertices
            for vertex in map.vertices.iter_mut() {
                vertex.y = -vertex.y;
                vertex.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "VERTEXES",
            });
            filepos = buffer.len();

            // Sectors
            for sector in map.sectors.iter_mut() {
                sector.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SECTORS",
            });
            filepos = buffer.len();

            println!("{} - Doom format map", map_range.name);
        }
    }
    let mut f = try!(File::create("flipped.wad"));
    try!(f.write("PWAD".as_bytes()));
    try!(f.write_u32::<LittleEndian>(directory.len() as u32));
    try!(f.write_u32::<LittleEndian>((12 + buffer.len()) as u32));
    try!(f.write_all(&buffer[..]));
    for entry in directory.iter() {
        println!("{:?}", entry);
        try!(f.write_u32::<LittleEndian>(entry.filepos));
        try!(f.write_u32::<LittleEndian>(entry.size));
        try!(f.write(entry.name.as_bytes()));
        for _ in entry.name.len() .. 8 {
            try!(f.write(&[0]));
        }
    }

    Ok(())
}

fn write_bare_map_as_svg<L: idchoppers::BareBinaryLine, T: idchoppers::BareBinaryThing>(map: &idchoppers::BareBinaryMap<L, T>) {
    let mut group = Group::new();
    let mut minx;
    let mut miny;
    if let Some(vertex) = map.vertices.first() {
        minx = vertex.x;
        miny = vertex.y;
    }
    else if let Some(thing) = map.things.first() {
        let (x, y) = thing.coords();
        minx = x;
        miny = y;
    }
    else {
        minx = 0;
        miny = 0;
    }
    let mut maxx = minx;
    let mut maxy = miny;

    for vertex in map.vertices.iter() {
        if vertex.x < minx {
            minx = vertex.x;
        }
        if vertex.x > maxx {
            maxx = vertex.x;
        }
        if vertex.y < miny {
            miny = vertex.y;
        }
        if vertex.y > maxy {
            maxy = vertex.y;
        }
    }

    let mut classes = Vec::new();
    for line in map.lines.iter() {
        classes.clear();
        let (v0i, v1i) = line.vertex_indices();
        let v0 = &map.vertices[v0i as usize];
        let v1 = &map.vertices[v1i as usize];
        classes.push("line");
        let (frontid, backid) = line.side_indices();
        if frontid == -1 && backid == -1 {
            classes.push("zero-sided");
        }
        else if frontid == -1 || backid == -1 {
            classes.push("one-sided");
        }
        else {
            classes.push("two-sided");
        }
        if line.has_special() {
            classes.push("has-special");
        }

        group.append(
            Line::new()
            .set("x1", v0.x)
            .set("y1", v0.y)
            .set("x2", v1.x)
            .set("y2", v1.y)
            .set("class", classes.join(" "))
        );
    }

    for thing in map.things.iter() {
        let (x, y) = thing.coords();
        if x < minx {
            minx = x;
        }
        if x > maxx {
            maxx = x;
        }
        if y < miny {
            miny = y;
        }
        if y > maxy {
            maxy = y;
        }
        let (color, radius);
        if let Some(thing_type) = idchoppers::universe::lookup_thing_type(thing.doomednum() as u32) {
            color = match thing_type.category {
                idchoppers::universe::ThingCategory::PlayerStart(_) => "green",
                idchoppers::universe::ThingCategory::Monster => "red",
            };
            radius = thing_type.radius;
        }
        else {
            color = "gray";
            radius = 8;
        }
        group.append(
            Rectangle::new()
            .set("x", x - (radius as i16))
            .set("y", y - (radius as i16))
            .set("width", radius * 2)
            .set("height", radius * 2)
            .set("fill", color));
    }

    for (s, sector) in map.sectors.iter().enumerate() {
        let mut data = Data::new();
        let polys = map.sector_to_polygons(s);
        // TODO wait, hang on, can i have multiple shapes in one path?  i think so...
        for poly in polys.iter() {
            let (first, others) = poly.split_first().unwrap();
            data = data.move_to((first.x, first.y));
            for vertex in others.iter() {
                data = data.line_to((vertex.x, vertex.y));
            }
            data = data.line_to((first.x, first.y));
        }

        let mut path = Path::new().set("d", data);
        if sector.sector_tag != 0 {
            path.assign("data-sector-tag", sector.sector_tag);
        }
        classes.clear();
        classes.push("sector");
        if sector.sector_type == 9 {
            classes.push("secret");
        }
        path.assign("class", classes.join(" "));
        group.append(path);
    }

    // Doom's y-axis points up, but SVG's points down.  Rather than mucking with coordinates
    // everywhere we write them, just flip the entire map.  (WebKit doesn't support "transform" on
    // the <svg> element, hence the need for this group.)
    group.assign("transform", "scale(1 -1)");
    let doc = Document::new()
        .set("viewBox", (minx, -maxy, maxx - minx, maxy - miny))
        .add(Style::new(include_str!("map-svg.css")))
        .add(group);
    svg::save("idchoppers-temp.svg", &doc).unwrap();
}
<commit_msg>Figure out how to show backtraces only when available<commit_after>use std::fs::File;
use std::io::{self, Read, Write};

extern crate byteorder;
use byteorder::{LittleEndian, WriteBytesExt};
extern crate idchoppers;
use idchoppers::errors::{Error, Result};
extern crate svg;
use svg::Document;
use svg::node::Node;
use svg::node::element::{Group, Line, Path, Rectangle, Style};
use svg::node::element::path::Data;
extern crate termcolor;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn main() {
    match run() {
        Ok(()) => {}
        Err(err) => {
            drop(write_err(err));
        }
    }
}

fn write_err(err: Error) -> Result<()> {
    let mut stderr = StandardStream::stderr(ColorChoice::Auto);
    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
    write!(&mut stderr, "error: ")?;
    stderr.set_color(&ColorSpec::new())?;
    writeln!(&mut stderr, "{}", err)?;
    if let Some(backtrace) = err.backtrace() {
        writeln!(&mut stderr, "{:?}", backtrace)?;
    }
    Ok(())
}

fn run() -> Result<()> {
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf)?;

    let wad = try!(idchoppers::parse_wad(buf.as_slice()));
    println!("found {:?}, {:?}, {:?}", wad.header.identification, wad.header.numlumps, wad.header.infotableofs);
    for map_range in wad.iter_maps() {
        let bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        match bare_map {
            // TODO interesting diagnostic: mix of map formats in the same wad
            idchoppers::BareMap::Doom(map) => {
                println!("{} - Doom format map", map_range.name);
            }
            idchoppers::BareMap::Hexen(map) => {
                println!("{} - Hexen format map", map_range.name);
            }
        }
    }

    // FIXME this also catches F1_START etc, dammit
    for entry in wad.iter_entries_between("F_START", "F_END") {
        println!("{}", entry.name);
    }

    let texture_entries;
    if let Some(texbuf) = wad.first_entry("TEXTURE1") {
        texture_entries = try!(idchoppers::parse_texturex_names(texbuf));
    }
    else if let Some(texbuf) = wad.first_entry("TEXTURE2") {
        texture_entries = try!(idchoppers::parse_texturex_names(texbuf));
    }
    else {
        texture_entries = vec![];
    }
    for entry in texture_entries.iter() {
        println!("{}", entry.name);
    }
    return Ok(());
    let mut buffer = Vec::new();
    let mut directory = Vec::new();
    let mut filepos: usize = 0;
    for map_range in wad.iter_maps() {
        let mut bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        if let idchoppers::BareMap::Doom(mut map) = bare_map {
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: 0,
                name: wad.directory[map_range.marker_index].name,
            });

            // Things
            for thing in map.things.iter_mut() {
                thing.y = -thing.y;
                thing.angle = -thing.angle;
                thing.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "THINGS",
            });
            filepos = buffer.len();

            // Lines
            for line in map.lines.iter_mut() {
                std::mem::swap(&mut line.v0, &mut line.v1);
                line.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "LINEDEFS",
            });
            filepos = buffer.len();

            // Sides
            for side in map.sides.iter_mut() {
                side.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SIDEDEFS",
            });
            filepos = buffer.len();

            // Vertices
            for vertex in map.vertices.iter_mut() {
                vertex.y = -vertex.y;
                vertex.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "VERTEXES",
            });
            filepos = buffer.len();

            // Sectors
            for sector in map.sectors.iter_mut() {
                sector.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SECTORS",
            });
            filepos = buffer.len();

            println!("{} - Doom format map", map_range.name);
        }
    }
    let mut f = try!(File::create("flipped.wad"));
    try!(f.write("PWAD".as_bytes()));
    try!(f.write_u32::<LittleEndian>(directory.len() as u32));
    try!(f.write_u32::<LittleEndian>((12 + buffer.len()) as u32));
    try!(f.write_all(&buffer[..]));
    for entry in directory.iter() {
        println!("{:?}", entry);
        try!(f.write_u32::<LittleEndian>(entry.filepos));
        try!(f.write_u32::<LittleEndian>(entry.size));
        try!(f.write(entry.name.as_bytes()));
        for _ in entry.name.len() .. 8 {
            try!(f.write(&[0]));
        }
    }

    Ok(())
}

fn write_bare_map_as_svg<L: idchoppers::BareBinaryLine, T: idchoppers::BareBinaryThing>(map: &idchoppers::BareBinaryMap<L, T>) {
    let mut group = Group::new();
    let mut minx;
    let mut miny;
    if let Some(vertex) = map.vertices.first() {
        minx = vertex.x;
        miny = vertex.y;
    }
    else if let Some(thing) = map.things.first() {
        let (x, y) = thing.coords();
        minx = x;
        miny = y;
    }
    else {
        minx = 0;
        miny = 0;
    }
    let mut maxx = minx;
    let mut maxy = miny;

    for vertex in map.vertices.iter() {
        if vertex.x < minx {
            minx = vertex.x;
        }
        if vertex.x > maxx {
            maxx = vertex.x;
        }
        if vertex.y < miny {
            miny = vertex.y;
        }
        if vertex.y > maxy {
            maxy = vertex.y;
        }
    }

    let mut classes = Vec::new();
    for line in map.lines.iter() {
        classes.clear();
        let (v0i, v1i) = line.vertex_indices();
        let v0 = &map.vertices[v0i as usize];
        let v1 = &map.vertices[v1i as usize];
        classes.push("line");
        let (frontid, backid) = line.side_indices();
        if frontid == -1 && backid == -1 {
            classes.push("zero-sided");
        }
        else if frontid == -1 || backid == -1 {
            classes.push("one-sided");
        }
        else {
            classes.push("two-sided");
        }
        if line.has_special() {
            classes.push("has-special");
        }

        group.append(
            Line::new()
            .set("x1", v0.x)
            .set("y1", v0.y)
            .set("x2", v1.x)
            .set("y2", v1.y)
            .set("class", classes.join(" "))
        );
    }

    for thing in map.things.iter() {
        let (x, y) = thing.coords();
        if x < minx {
            minx = x;
        }
        if x > maxx {
            maxx = x;
        }
        if y < miny {
            miny = y;
        }
        if y > maxy {
            maxy = y;
        }
        let (color, radius);
        if let Some(thing_type) = idchoppers::universe::lookup_thing_type(thing.doomednum() as u32) {
            color = match thing_type.category {
                idchoppers::universe::ThingCategory::PlayerStart(_) => "green",
                idchoppers::universe::ThingCategory::Monster => "red",
            };
            radius = thing_type.radius;
        }
        else {
            color = "gray";
            radius = 8;
        }
        group.append(
            Rectangle::new()
            .set("x", x - (radius as i16))
            .set("y", y - (radius as i16))
            .set("width", radius * 2)
            .set("height", radius * 2)
            .set("fill", color));
    }

    for (s, sector) in map.sectors.iter().enumerate() {
        let mut data = Data::new();
        let polys = map.sector_to_polygons(s);
        // TODO wait, hang on, can i have multiple shapes in one path?  i think so...
        for poly in polys.iter() {
            let (first, others) = poly.split_first().unwrap();
            data = data.move_to((first.x, first.y));
            for vertex in others.iter() {
                data = data.line_to((vertex.x, vertex.y));
            }
            data = data.line_to((first.x, first.y));
        }

        let mut path = Path::new().set("d", data);
        if sector.sector_tag != 0 {
            path.assign("data-sector-tag", sector.sector_tag);
        }
        classes.clear();
        classes.push("sector");
        if sector.sector_type == 9 {
            classes.push("secret");
        }
        path.assign("class", classes.join(" "));
        group.append(path);
    }

    // Doom's y-axis points up, but SVG's points down.  Rather than mucking with coordinates
    // everywhere we write them, just flip the entire map.  (WebKit doesn't support "transform" on
    // the <svg> element, hence the need for this group.)
    group.assign("transform", "scale(1 -1)");
    let doc = Document::new()
        .set("viewBox", (minx, -maxy, maxx - minx, maxy - miny))
        .add(Style::new(include_str!("map-svg.css")))
        .add(group);
    svg::save("idchoppers-temp.svg", &doc).unwrap();
}
<|endoftext|>