<commit_before>//! Sets up the dbus interface for Awesome.

use std::{cell::RefCell, os::unix::io::RawFd, thread::LocalKey};

use dbus_rs::{BusType, Connection, Message, MessageItem, MessageType, MsgHandler,
              MsgHandlerType, MsgHandlerResult};
use rlua::{self, Lua, Table, Value, ToLua, ToLuaMulti, MultiValue,
           Error::RuntimeError};

use ::lua::LUA;
use ::common::signal;

type GlobalConnection = RefCell<Option<DBusConnection>>;

const SIGNALS_NAME: &'static str = "signals";

thread_local! {
    static SESSION_BUS: GlobalConnection = RefCell::new(None);
    static SYSTEM_BUS:  GlobalConnection = RefCell::new(None);
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// current session dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_session_refresh(_: libc::c_void) -> bool {
    SESSION_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// system dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_system_refresh(_: libc::c_void) -> bool {
    SYSTEM_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

struct DBusConnection {
    connection: Connection
}

struct DBusHandler {
    global_connection: &'static LocalKey<GlobalConnection>
}

impl std::ops::Deref for DBusConnection {
    type Target = Connection;

    fn deref(&self) -> &Self::Target {
        &self.connection
    }
}

impl Drop for DBusConnection {
    fn drop(&mut self) {
        unsafe {
            ::remove_dbus_from_glib();
        }
        // Need to close both of them, no idea which one was just destroyed,
        // so try to destroy both of them.
        SESSION_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
        SYSTEM_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
    }
}

impl MsgHandler for DBusHandler {
    fn handler_type(&self) -> MsgHandlerType {
        MsgHandlerType::All
    }

    fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
        let (_, _, interface, member) = msg.headers();
        if msg.msg_type() == MessageType::Signal {
            match (interface.unwrap().as_str(), member.unwrap().as_str()) {
                ("org.freedesktop.DBus.Local", "Disconnected") => {
                    self.global_connection.with(|bus| {
                        *bus.borrow_mut() = None;
                    });
                    // TODO not none
                    return Some(MsgHandlerResult {
                        handled: true,
                        done: true,
                        reply: vec![]
                    });
                },
                _ => {}
            }
        }
        let reply = LUA.with(|lua| {
            let lua = lua.borrow();
            self.process_request(&*lua)
        }).unwrap_or_else(|err| {
            ::lua::log_error(err);
            vec![]
        });

        return Some(MsgHandlerResult {
            handled: true,
            done: false,
            reply
        })
    }
}

impl DBusHandler {
    fn process_request(&mut self, lua: &Lua) -> rlua::Result<Vec<Message>> {
        unimplemented!()
    }
}

/// Set up the connections to the session bus and system bus.
///
/// The message handling is all done within the `dbus` module,
/// it's up to the caller of this function to register the DBus
/// file descriptors with glib so that we can awaken and deal
/// with dbus events when necessary.
pub fn connect() -> Result<(RawFd, RawFd), dbus::Error> {
    let session_con = Connection::get_private(BusType::Session)?;
    let system_con = Connection::get_private(BusType::System)?;
    let session_fds = session_con.watch_fds();
    let system_fds = system_con.watch_fds();
    assert_eq!(session_fds.len(), 1, "Only one fd per dbus connection");
    assert_eq!(system_fds.len(), 1, "Only one fd per dbus connection");
    session_con.add_handler(DBusHandler { global_connection: &SESSION_BUS });
    system_con.add_handler(DBusHandler { global_connection: &SYSTEM_BUS });

    SESSION_BUS.with(|session_bus| {
        let mut session_bus = session_bus.borrow_mut();
        *session_bus = Some(DBusConnection { connection: session_con });
    });
    SYSTEM_BUS.with(|system_bus| {
        let mut system_bus = system_bus.borrow_mut();
        *system_bus = Some(DBusConnection{ connection: system_con });
    });

    Ok((session_fds[0].fd(), system_fds[0].fd()))
}

/// Set up the DBus object in Lua so that the user libs can interact with
/// Awesome via DBus.
pub fn lua_init(lua: &Lua) -> rlua::Result<()> {
    let dbus_table = lua.create_table()?;
    dbus_table.set(SIGNALS_NAME, lua.create_table()?)?;
    dbus_table.set("request_name", lua.create_function(request_name)?)?;
    dbus_table.set("release_name", lua.create_function(release_name)?)?;
    dbus_table.set("add_match", lua.create_function(add_match)?)?;
    dbus_table.set("remove_match", lua.create_function(remove_match)?)?;
    dbus_table.set("connect_signal", lua.create_function(connect_signal)?)?;
    dbus_table.set("disconnect_signal", lua.create_function(disconnect_signal)?)?;
    dbus_table.set("emit_signal", lua.create_function(emit_signal)?)?;
    lua.globals().set("dbus", dbus_table)?;
    Ok(())
}

fn get_bus_by_name<'bus>(bus_name: &str)
                         -> rlua::Result<&'bus LocalKey<GlobalConnection>> {
    match bus_name {
        "session" => {
            Ok(&SESSION_BUS)
        },
        "system" => {
            Ok(&SYSTEM_BUS)
        },
        v => Err(RuntimeError(format!("Unknown bus type {}", v)))
    }
}

fn convert_value(lua: &Lua, type_: Value, value: Value) -> rlua::Result<MessageItem> {
    use rlua::Value;
    use ::dbus_rs::arg::ArgType;
    let type_ = match type_ {
        Value::String(s) => s.to_str()?.to_string(),
        _ => return Err(RuntimeError("D-Bus type name was not a string".into()))
    };
    let is_ascii = type_.chars().next()
        .map(|c| !char::is_ascii(&c))
        .unwrap_or(false);
    if type_.len() > 1 ||  !is_ascii {
        return Err(RuntimeError(format!("{} is an invalid type name", type_)))
    }
    let type_ = ArgType::from_i32(type_.chars().next().unwrap() as i32)
        .map_err(|_| RuntimeError(format!(
            "{} is an invalid type name", type_)))?;
    match (type_, value) {
        (ArgType::Array, Value::Table(value)) => {
            let size = value.len()?;
            if size % 2 != 0 {
                return Err(RuntimeError(
                    "your D-Bus signal handling method returned \
                     wrong number of arguments".into()))
            }
            let types = value.clone().sequence_values().step_by(2);
            let values = value.clone().sequence_values().skip(1).step_by(2);
            let mut list = Vec::with_capacity(size as usize);
            for (type_, value) in types.zip(values) {
                list.push(convert_value(lua, type_?, value?)?)
            }
            MessageItem::new_array(list)
               .map_err(|_| RuntimeError("Empty list is invalid".into()))
        },
        (ArgType::Boolean, Value::Boolean(value)) => {
            Ok(value.into())
        },
        (ArgType::String, Value::String(value)) => {
            Ok(value.to_str()?.into())
        },
        (ArgType::Byte, Value::Integer(value)) |
        (ArgType::Int16, Value::Integer(value)) |
        (ArgType::UInt16, Value::Integer(value)) |
        (ArgType::Int32, Value::Integer(value)) |
        (ArgType::UInt32, Value::Integer(value)) |
        (ArgType::Int64, Value::Integer(value)) |
        (ArgType::UInt64, Value::Integer(value))  => {
            Ok(value.into())
        },
        (ArgType::Byte, Value::Number(value)) |
        (ArgType::Int16, Value::Number(value)) |
        (ArgType::UInt16, Value::Number(value)) |
        (ArgType::Int32, Value::Number(value)) |
        (ArgType::UInt32, Value::Number(value)) |
        (ArgType::Int64, Value::Number(value)) |
        (ArgType::UInt64, Value::Number(value)) |
        (ArgType::Double, Value::Number(value)) => {
            Ok(value.into())
        }
        (type_, value) => {
            Err(RuntimeError(format!("Invalid type {:?} or value {:?}",
                                            type_, value)))
        }
    }
}

fn request_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.register_name(name.as_str(), 0)
            .expect(&format!("Could not register name {}", name.as_str()));
    });
    Ok(true)
}

fn release_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.release_name(name.as_str())
            .expect(&format!("Could not release name {}", name.as_str()));
    });
    Ok(true)
}

fn add_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.add_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn remove_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.remove_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn connect_signal<'lua>(lua: &'lua Lua, (name, func): (String, rlua::Function))
                  -> rlua::Result<MultiValue<'lua>> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus")
        .unwrap().get(SIGNALS_NAME).unwrap();
    if signals.get(name.as_str())? {
        let error_msg = format!(
            "Cannot add signal {} on D-Bus, already existing", name.as_str());
        warn!("{}", error_msg);
        (rlua::Nil, error_msg).to_lua_multi(lua)
    } else {
        signal::connect_signals(lua, signals, name, &[func])?;
        (true.to_lua_multi(lua))
    }
}

fn disconnect_signal(lua: &Lua, (name, func): (String, rlua::Function))
                     -> rlua::Result<()> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus").unwrap()
        .get(SIGNALS_NAME).unwrap();
    signal::disconnect_signals(lua, signals, name)
}

fn emit_signal<'lua>(lua: &'lua Lua, (bus, path, interface, name, args):
                     (String, String, String, String, MultiValue))
                     -> rlua::Result<Value<'lua>> {
    if args.len() % 2 != 0 {
        let error_msg =
            "your D-Bus signal emitting metod has wrong number of arguments";
        warn!("{}", error_msg);
        return false.to_lua(lua)
    }
    let types = args.iter().step_by(2);
    let values = args.iter().skip(1).step_by(2);
    let args = types.zip(values)
        .map(|v| convert_value(lua, v.0.clone(), v.1.clone()))
        .collect::<rlua::Result<Vec<MessageItem>>>()?;
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        // TODO catch panic, convert to error
        let mut msg = Message::signal(&path.into(), &interface.into(), &name.into());
        msg.append_items(&args);
        bus.send(msg)
    }).map_err(|_| RuntimeError("Could not send D-Bus message".into()))?;
    true.to_lua(lua)
}
<commit_msg>Stubbed out process request<commit_after>//! Sets up the dbus interface for Awesome.

use std::{cell::RefCell, os::unix::io::RawFd, thread::LocalKey};

use dbus_rs::{BusType, Connection, Message, MessageItem, MessageType, MsgHandler,
              MsgHandlerType, MsgHandlerResult};
use rlua::{self, Lua, Table, Value, ToLua, ToLuaMulti, MultiValue,
           Error::RuntimeError};

use ::lua::LUA;
use ::common::signal;

type GlobalConnection = RefCell<Option<DBusConnection>>;

const SIGNALS_NAME: &'static str = "signals";

thread_local! {
    static SESSION_BUS: GlobalConnection = RefCell::new(None);
    static SYSTEM_BUS:  GlobalConnection = RefCell::new(None);
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// current session dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_session_refresh(_: libc::c_void) -> bool {
    SESSION_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

/// Called from `wayland_glib_interface.c` whenever a request is sent to the
/// system dbus file descriptor.
///
/// This will kick off the special handling code in `dbus.rs`.
#[no_mangle]
pub extern "C" fn dbus_system_refresh(_: libc::c_void) -> bool {
    SYSTEM_BUS.with(|session_bus| {
        let session_bus = session_bus.borrow_mut();
        let session_bus = session_bus.as_ref().unwrap();
        let _ = session_bus.incoming(0).collect::<Vec<_>>();
    });
    true
}

struct DBusConnection {
    connection: Connection
}

struct DBusHandler {
    global_connection: &'static LocalKey<GlobalConnection>
}

impl std::ops::Deref for DBusConnection {
    type Target = Connection;

    fn deref(&self) -> &Self::Target {
        &self.connection
    }
}

impl Drop for DBusConnection {
    fn drop(&mut self) {
        unsafe {
            ::remove_dbus_from_glib();
        }
        // Need to close both of them, no idea which one was just destroyed,
        // so try to destroy both of them.
        SESSION_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
        SYSTEM_BUS.try_with(|session_bus| {
            *session_bus.borrow_mut() = None
        }).ok();
    }
}

impl MsgHandler for DBusHandler {
    fn handler_type(&self) -> MsgHandlerType {
        MsgHandlerType::All
    }

    fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
        if msg.msg_type() == MessageType::Signal {
            let (_, _, interface, member) = msg.headers();
            match (interface.unwrap().as_str(), member.unwrap().as_str()) {
                ("org.freedesktop.DBus.Local", "Disconnected") => {
                    self.global_connection.with(|bus| {
                        *bus.borrow_mut() = None;
                    });
                    // TODO not none
                    return Some(MsgHandlerResult {
                        handled: true,
                        done: true,
                        reply: vec![]
                    });
                },
                _ => {}
            }
        }
        let reply = LUA.with(|lua| {
            let lua = lua.borrow();
            self.process_request(&*lua, msg)
        }).unwrap_or_else(|err| {
            ::lua::log_error(err);
            vec![]
        });

        return Some(MsgHandlerResult {
            handled: true,
            done: false,
            reply
        })
    }
}

impl DBusHandler {
    /// Gives the D-Bus message to Lua for processing the reply.
    fn process_request(&mut self, lua: &Lua, msg: &Message) -> rlua::Result<Vec<Message>> {
        let message_metadata = lua.create_table()?;
        let msg_type = match msg.msg_type() {
            MessageType::Signal => "signal",
            MessageType::MethodCall => "method_call",
            MessageType::MethodReturn => "method_return",
            MessageType::Error => "error",
            MessageType::Invalid => "unknown"
        };
        message_metadata.set("type", msg_type)?;
        let (type_, path, interface, member) = msg.headers();
        message_metadata.set("interface", interface.unwrap_or_else(|| "".into()))?;
        message_metadata.set("path", path.unwrap_or_else(|| "".into()))?;
        message_metadata.set("member", member.unwrap_or_else(|| "".into()))?;
        if let Some(sender) = msg.sender() {
            message_metadata.set("sender", sender.to_string())?;
        }
        if self.global_connection as *const _ == &SYSTEM_BUS as *const _ {
            message_metadata.set("bus", "system")?;
        } else {
            message_metadata.set("bus", "session")?;
        };
        let lua_message = dbus_to_lua_value(lua, msg)?;
        unimplemented!()
    }
}

/// Set up the connections to the session bus and system bus.
///
/// The message handling is all done within the `dbus` module,
/// it's up to the caller of this function to register the DBus
/// file descriptors with glib so that we can awaken and deal
/// with dbus events when necessary.
pub fn connect() -> Result<(RawFd, RawFd), dbus::Error> {
    let session_con = Connection::get_private(BusType::Session)?;
    let system_con = Connection::get_private(BusType::System)?;
    let session_fds = session_con.watch_fds();
    let system_fds = system_con.watch_fds();
    assert_eq!(session_fds.len(), 1, "Only one fd per dbus connection");
    assert_eq!(system_fds.len(), 1, "Only one fd per dbus connection");
    session_con.add_handler(DBusHandler { global_connection: &SESSION_BUS });
    system_con.add_handler(DBusHandler { global_connection: &SYSTEM_BUS });

    SESSION_BUS.with(|session_bus| {
        let mut session_bus = session_bus.borrow_mut();
        *session_bus = Some(DBusConnection { connection: session_con });
    });
    SYSTEM_BUS.with(|system_bus| {
        let mut system_bus = system_bus.borrow_mut();
        *system_bus = Some(DBusConnection{ connection: system_con });
    });

    Ok((session_fds[0].fd(), system_fds[0].fd()))
}

/// Set up the DBus object in Lua so that the user libs can interact with
/// Awesome via DBus.
pub fn lua_init(lua: &Lua) -> rlua::Result<()> {
    let dbus_table = lua.create_table()?;
    dbus_table.set(SIGNALS_NAME, lua.create_table()?)?;
    dbus_table.set("request_name", lua.create_function(request_name)?)?;
    dbus_table.set("release_name", lua.create_function(release_name)?)?;
    dbus_table.set("add_match", lua.create_function(add_match)?)?;
    dbus_table.set("remove_match", lua.create_function(remove_match)?)?;
    dbus_table.set("connect_signal", lua.create_function(connect_signal)?)?;
    dbus_table.set("disconnect_signal", lua.create_function(disconnect_signal)?)?;
    dbus_table.set("emit_signal", lua.create_function(emit_signal)?)?;
    lua.globals().set("dbus", dbus_table)?;
    Ok(())
}

fn get_bus_by_name<'bus>(bus_name: &str)
                         -> rlua::Result<&'bus LocalKey<GlobalConnection>> {
    match bus_name {
        "session" => {
            Ok(&SESSION_BUS)
        },
        "system" => {
            Ok(&SYSTEM_BUS)
        },
        v => Err(RuntimeError(format!("Unknown bus type {}", v)))
    }
}

fn dbus_to_lua_value<'lua>(lua: &'lua Lua, msg: &Message)
                           -> rlua::Result<MultiValue<'lua>> {
    unimplemented!();
}

/// Converts an `rlua::Value` into a `dbus_rs::MessageItem`.
fn lua_value_to_dbus(lua: &Lua, type_: Value, value: Value)
                     -> rlua::Result<MessageItem> {
    use rlua::Value;
    use ::dbus_rs::arg::ArgType;
    let type_ = match type_ {
        Value::String(s) => s.to_str()?.to_string(),
        _ => return Err(RuntimeError("D-Bus type name was not a string".into()))
    };
    let is_ascii = type_.chars().next()
        .map(|c| !char::is_ascii(&c))
        .unwrap_or(false);
    if type_.len() > 1 ||  !is_ascii {
        return Err(RuntimeError(format!("{} is an invalid type name", type_)))
    }
    let type_ = ArgType::from_i32(type_.chars().next().unwrap() as i32)
        .map_err(|_| RuntimeError(format!(
            "{} is an invalid type name", type_)))?;
    match (type_, value) {
        (ArgType::Array, Value::Table(value)) => {
            let size = value.len()?;
            if size % 2 != 0 {
                return Err(RuntimeError(
                    "your D-Bus signal handling method returned \
                     wrong number of arguments".into()))
            }
            let types = value.clone().sequence_values().step_by(2);
            let values = value.clone().sequence_values().skip(1).step_by(2);
            let mut list = Vec::with_capacity(size as usize);
            for (type_, value) in types.zip(values) {
                list.push(lua_value_to_dbus(lua, type_?, value?)?)
            }
            MessageItem::new_array(list)
               .map_err(|_| RuntimeError("Empty list is invalid".into()))
        },
        (ArgType::Boolean, Value::Boolean(value)) => {
            Ok(value.into())
        },
        (ArgType::String, Value::String(value)) => {
            Ok(value.to_str()?.into())
        },
        (ArgType::Byte, Value::Integer(value)) |
        (ArgType::Int16, Value::Integer(value)) |
        (ArgType::UInt16, Value::Integer(value)) |
        (ArgType::Int32, Value::Integer(value)) |
        (ArgType::UInt32, Value::Integer(value)) |
        (ArgType::Int64, Value::Integer(value)) |
        (ArgType::UInt64, Value::Integer(value))  => {
            Ok(value.into())
        },
        (ArgType::Byte, Value::Number(value)) |
        (ArgType::Int16, Value::Number(value)) |
        (ArgType::UInt16, Value::Number(value)) |
        (ArgType::Int32, Value::Number(value)) |
        (ArgType::UInt32, Value::Number(value)) |
        (ArgType::Int64, Value::Number(value)) |
        (ArgType::UInt64, Value::Number(value)) |
        (ArgType::Double, Value::Number(value)) => {
            Ok(value.into())
        }
        (type_, value) => {
            Err(RuntimeError(format!("Invalid type {:?} or value {:?}",
                                            type_, value)))
        }
    }
}

fn request_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.register_name(name.as_str(), 0)
            .expect(&format!("Could not register name {}", name.as_str()));
    });
    Ok(true)
}

fn release_name(_: &Lua, (bus, name): (String, String)) -> rlua::Result<bool> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.release_name(name.as_str())
            .expect(&format!("Could not release name {}", name.as_str()));
    });
    Ok(true)
}

fn add_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.add_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn remove_match(_: &Lua, (bus, name): (String, String)) -> rlua::Result<()> {
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        bus.remove_match(name.as_str())
            .map_err(|err| RuntimeError(format!("{}", err)))
    })?;
    Ok(())
}

fn connect_signal<'lua>(lua: &'lua Lua, (name, func): (String, rlua::Function))
                  -> rlua::Result<MultiValue<'lua>> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus")
        .unwrap().get(SIGNALS_NAME).unwrap();
    if signals.get(name.as_str())? {
        let error_msg = format!(
            "Cannot add signal {} on D-Bus, already existing", name.as_str());
        warn!("{}", error_msg);
        (rlua::Nil, error_msg).to_lua_multi(lua)
    } else {
        signal::connect_signals(lua, signals, name, &[func])?;
        (true.to_lua_multi(lua))
    }
}

fn disconnect_signal(lua: &Lua, (name, func): (String, rlua::Function))
                     -> rlua::Result<()> {
    let signals: Table = lua.globals()
        .get::<_, Table>("dbus").unwrap()
        .get(SIGNALS_NAME).unwrap();
    signal::disconnect_signals(lua, signals, name)
}

fn emit_signal<'lua>(lua: &'lua Lua, (bus, path, interface, name, args):
                     (String, String, String, String, MultiValue))
                     -> rlua::Result<Value<'lua>> {
    if args.len() % 2 != 0 {
        let error_msg =
            "your D-Bus signal emitting metod has wrong number of arguments";
        warn!("{}", error_msg);
        return false.to_lua(lua)
    }
    let types = args.iter().step_by(2);
    let values = args.iter().skip(1).step_by(2);
    let args = types.zip(values)
        .map(|v| lua_value_to_dbus(lua, v.0.clone(), v.1.clone()))
        .collect::<rlua::Result<Vec<MessageItem>>>()?;
    let bus = get_bus_by_name(bus.as_str())?;
    bus.with(|bus| {
        let bus = bus.borrow_mut();
        let bus = bus.as_ref().unwrap();
        // TODO use new_signal, convert error
        let mut msg = Message::signal(&path.into(), &interface.into(), &name.into());
        msg.append_items(&args);
        bus.send(msg)
    }).map_err(|_| RuntimeError("Could not send D-Bus message".into()))?;
    true.to_lua(lua)
}
<|endoftext|>


<commit_before>//! Awesome compatibility modules

extern crate cairo;
extern crate cairo_sys;
extern crate env_logger;
extern crate exec;
extern crate getopts;
extern crate gdk_pixbuf;
extern crate glib;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nix;
extern crate rlua;
extern crate xcb;
#[macro_use]
extern crate wayland_client;

// TODO remove
extern crate wlroots;
use wlroots::{KeyboardModifier, key_events::KeyEvent, wlr_key_state::*};

#[macro_use]
mod macros;

mod objects;
mod common;
mod wayland_obj;

mod awesome;
mod keygrabber;
mod mousegrabber;
mod root;
mod lua;

use std::{env, mem, path::PathBuf, process::exit};

use exec::Command;
use lua::setup_lua;
use rlua::{LightUserData, Lua, Table};
use log::LogLevel;
use nix::sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet};
use xcb::{xkb, Connection};
use wayland_client::{Display, GlobalManager};
use wayland_client::protocol::{wl_output, wl_display::RequestsTrait};
use wayland_client::sys::client::wl_display;

use self::lua::{LUA, NEXT_LUA};

use self::objects::key::Key;
use self::common::{object::{Object, Objectable}, signal::*};
use self::root::ROOT_KEYS_HANDLE;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const GIT_VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/git-version.txt"));
pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// Called from `wayland_glib_interface.c` after every call back into the
/// wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn refresh_awesome() {
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            let awesome = env::args().next().unwrap();
            let args: Vec<_> = env::args().skip(1).collect();
            let err = Command::new(awesome)
                .args(args.as_slice())
                .exec();
            error!("error: {:?}", err);
            panic!("Could not restart Awesome");
        }
    });
}

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("", "version", "show version information");
    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            exit(1);
        }
    };
    if matches.opt_present("version") {
        if !GIT_VERSION.is_empty() {
            println!("Way Cooler {} @ {}", VERSION, GIT_VERSION);
        } else {
            println!("Way Cooler {}", VERSION);
        }
        return
    }
    init_logs();
    let sig_action = SigAction::new(SigHandler::Handler(sig_handle),
                                    SaFlags::empty(),
                                    SigSet::empty());
    unsafe {
        signal::sigaction(signal::SIGINT, &sig_action).expect("Could not set SIGINT catcher");
    }
    init_wayland();
    lua::setup_lua();
    lua::enter_glib_loop();
}

fn init_wayland() {
    let (display, mut event_queue) = match Display::connect_to_env() {
        Ok(res) => res,
        Err(err) => {
            error!("Could not connect to Wayland server. Is it running?");
            exit(1);
        }
    };
    unsafe {
        #[link(name = "wayland_glib_interface", kind = "static")]
        extern "C" {
            fn wayland_glib_interface_init(display: *mut wl_display);
        }
        wayland_glib_interface_init(display.c_ptr() as *mut wl_display);
    }
    let _globals = GlobalManager::new_with_cb(
        display.get_registry().unwrap(),
        global_filter!(
            [wl_output::WlOutput, 2, wayland_obj::Output::new]
        ),
    );
    // TODO Remove
    event_queue.sync_roundtrip().unwrap();
    event_queue.sync_roundtrip().unwrap();
    event_queue.sync_roundtrip().unwrap();
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;

    for mut xdg_data_path in
        env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".into())
                                 .split(':')
                                 .map(PathBuf::from)
    {
        xdg_data_path.push("awesome/lib");
        path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                               xdg_data_path.as_os_str().to_string_lossy()));
        cpath.push_str(&format!(";{}/?.so", xdg_data_path.into_os_string().to_string_lossy()));
    }

    for mut xdg_config_path in env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                          .split(':')
                                                          .map(PathBuf::from)
    {
        xdg_config_path.push("awesome");
        cpath.push_str(&format!(";{}/?.so",
                                xdg_config_path.into_os_string().to_string_lossy()));
    }

    package.set("path", path)?;
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

/// Emits the Awesome keybindinsg.
fn emit_awesome_keybindings(lua: &Lua,
                            event: &KeyEvent,
                            event_modifiers: KeyboardModifier)
                            -> rlua::Result<()> {
    let state_string = if event.key_state() == WLR_KEY_PRESSED {
        "press"
    } else {
        "release"
    };
    // TODO Should also emit by current focused client so we can
    // do client based rules.
    let keybindings = lua.named_registry_value::<Vec<rlua::AnyUserData>>(ROOT_KEYS_HANDLE)?;
    for event_keysym in event.pressed_keys() {
        for binding in &keybindings {
            let obj: Object = binding.clone().into();
            let key = Key::cast(obj.clone()).unwrap();
            let keycode = key.keycode()?;
            let keysym = key.keysym()?;
            let modifiers = key.modifiers()?;
            let binding_match = (keysym != 0 && keysym == event_keysym
                                 || keycode != 0 && keycode == event.keycode())
                                && modifiers == 0
                                || modifiers == event_modifiers.bits();
            if binding_match {
                emit_object_signal(&*lua, obj, state_string.into(), event_keysym)?;
            }
        }
    }
    Ok(())
}

/// Formats the log strings properly
fn log_format(record: &log::LogRecord) -> String {
    let color = match record.level() {
        LogLevel::Info => "",
        LogLevel::Trace => "\x1B[37m",
        LogLevel::Debug => "\x1B[44m",
        LogLevel::Warn => "\x1B[33m",
        LogLevel::Error => "\x1B[31m"
    };
    let location = record.location();
    let file = location.file();
    let line = location.line();
    let mut module_path = location.module_path();
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    format!("{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color,
            record.level(),
            module_path,
            file,
            line,
            record.args())
}

fn init_logs() {
    let mut builder = env_logger::LogBuilder::new();
    builder.format(log_format);
    builder.filter(None, log::LogLevelFilter::Trace);
    if env::var("WAY_COOLER_LOG").is_ok() {
        builder.parse(&env::var("WAY_COOLER_LOG").expect("WAY_COOLER_LOG not defined"));
    }
    builder.init().expect("Unable to initialize logging!");
    info!("Logger initialized");
}

/// Handler for SIGINT signal
extern "C" fn sig_handle(_: nix::libc::c_int) {
    lua::terminate();
    exit(130);
}
<commit_msg>Remove a piece of code which was duplicate.<commit_after>//! Awesome compatibility modules

extern crate cairo;
extern crate cairo_sys;
extern crate env_logger;
extern crate exec;
extern crate getopts;
extern crate gdk_pixbuf;
extern crate glib;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate nix;
extern crate rlua;
extern crate xcb;
#[macro_use]
extern crate wayland_client;

// TODO remove
extern crate wlroots;
use wlroots::{KeyboardModifier, key_events::KeyEvent, wlr_key_state::*};

#[macro_use]
mod macros;

mod objects;
mod common;
mod wayland_obj;

mod awesome;
mod keygrabber;
mod mousegrabber;
mod root;
mod lua;

use std::{env, mem, path::PathBuf, process::exit};

use exec::Command;
use lua::setup_lua;
use rlua::{LightUserData, Lua, Table};
use log::LogLevel;
use nix::sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet};
use xcb::{xkb, Connection};
use wayland_client::{Display, GlobalManager};
use wayland_client::protocol::{wl_output, wl_display::RequestsTrait};
use wayland_client::sys::client::wl_display;

use self::lua::{LUA, NEXT_LUA};

use self::objects::key::Key;
use self::common::{object::{Object, Objectable}, signal::*};
use self::root::ROOT_KEYS_HANDLE;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const GIT_VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/git-version.txt"));
pub const GLOBAL_SIGNALS: &'static str = "__awesome_global_signals";
pub const XCB_CONNECTION_HANDLE: &'static str = "__xcb_connection";

/// Called from `wayland_glib_interface.c` after every call back into the
/// wayland event loop.
///
/// This restarts the Lua thread if there is a new one pending
#[no_mangle]
pub extern "C" fn refresh_awesome() {
    NEXT_LUA.with(|new_lua_check| {
        if new_lua_check.get() {
            new_lua_check.set(false);
            let awesome = env::args().next().unwrap();
            let args: Vec<_> = env::args().skip(1).collect();
            let err = Command::new(awesome)
                .args(args.as_slice())
                .exec();
            error!("error: {:?}", err);
            panic!("Could not restart Awesome");
        }
    });
}

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("", "version", "show version information");
    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            exit(1);
        }
    };
    if matches.opt_present("version") {
        if !GIT_VERSION.is_empty() {
            println!("Way Cooler {} @ {}", VERSION, GIT_VERSION);
        } else {
            println!("Way Cooler {}", VERSION);
        }
        return
    }
    init_logs();
    let sig_action = SigAction::new(SigHandler::Handler(sig_handle),
                                    SaFlags::empty(),
                                    SigSet::empty());
    unsafe {
        signal::sigaction(signal::SIGINT, &sig_action).expect("Could not set SIGINT catcher");
    }
    init_wayland();
    lua::setup_lua();
    lua::enter_glib_loop();
}

fn init_wayland() {
    let (display, mut event_queue) = match Display::connect_to_env() {
        Ok(res) => res,
        Err(err) => {
            error!("Could not connect to Wayland server. Is it running?");
            exit(1);
        }
    };
    unsafe {
        #[link(name = "wayland_glib_interface", kind = "static")]
        extern "C" {
            fn wayland_glib_interface_init(display: *mut wl_display);
        }
        wayland_glib_interface_init(display.c_ptr() as *mut wl_display);
    }
    let _globals = GlobalManager::new_with_cb(
        display.get_registry().unwrap(),
        global_filter!(
            [wl_output::WlOutput, 2, wayland_obj::Output::new]
        ),
    );
    // TODO Remove
    event_queue.sync_roundtrip().unwrap();
    event_queue.sync_roundtrip().unwrap();
    event_queue.sync_roundtrip().unwrap();
}

fn setup_awesome_path(lua: &Lua) -> rlua::Result<()> {
    let globals = lua.globals();
    let package: Table = globals.get("package")?;
    let mut path = package.get::<_, String>("path")?;
    let mut cpath = package.get::<_, String>("cpath")?;

    for mut xdg_data_path in
        env::var("XDG_DATA_DIRS").unwrap_or("/usr/local/share:/usr/share".into())
                                 .split(':')
                                 .map(PathBuf::from)
    {
        xdg_data_path.push("awesome/lib");
        path.push_str(&format!(";{0}/?.lua;{0}/?/init.lua",
                               xdg_data_path.as_os_str().to_string_lossy()));
        cpath.push_str(&format!(";{}/?.so", xdg_data_path.into_os_string().to_string_lossy()));
    }

    for mut xdg_config_path in env::var("XDG_CONFIG_DIRS").unwrap_or("/etc/xdg".into())
                                                          .split(':')
                                                          .map(PathBuf::from)
    {
        xdg_config_path.push("awesome");
        cpath.push_str(&format!(";{}/?.so",
                                xdg_config_path.into_os_string().to_string_lossy()));
    }

    package.set("path", path)?;
    package.set("cpath", cpath)?;

    Ok(())
}

/// Set up global signals value
///
/// We need to store this in Lua, because this make it safer to use.
fn setup_global_signals(lua: &Lua) -> rlua::Result<()> {
    lua.set_named_registry_value(GLOBAL_SIGNALS, lua.create_table()?)
}

/// Sets up the xcb connection and stores it in Lua (for us to access it later)
fn setup_xcb_connection(lua: &Lua) -> rlua::Result<()> {
    let con = match Connection::connect(None) {
        Err(err) => {
            error!("Way Cooler requires XWayland in order to function");
            error!("However, xcb could not connect to it. Is it running?");
            error!("{:?}", err);
            panic!("Could not connect to XWayland instance");
        }
        Ok(con) => con.0
    };
    // Tell xcb we are using the xkb extension
    match xkb::use_extension(&con, 1, 0).get_reply() {
        Ok(r) => {
            if !r.supported() {
                panic!("xkb-1.0 is not supported");
            }
        }
        Err(err) => {
            panic!("Could not get xkb extension supported version {:?}", err);
        }
    }
    lua.set_named_registry_value(XCB_CONNECTION_HANDLE,
                                  LightUserData(con.get_raw_conn() as _))?;
    mem::forget(con);
    Ok(())
}

/// Formats the log strings properly
fn log_format(record: &log::LogRecord) -> String {
    let color = match record.level() {
        LogLevel::Info => "",
        LogLevel::Trace => "\x1B[37m",
        LogLevel::Debug => "\x1B[44m",
        LogLevel::Warn => "\x1B[33m",
        LogLevel::Error => "\x1B[31m"
    };
    let location = record.location();
    let file = location.file();
    let line = location.line();
    let mut module_path = location.module_path();
    if let Some(index) = module_path.find("way_cooler::") {
        let index = index + "way_cooler::".len();
        module_path = &module_path[index..];
    }
    format!("{} {} [{}] \x1B[37m{}:{}\x1B[0m{0} {} \x1B[0m",
            color,
            record.level(),
            module_path,
            file,
            line,
            record.args())
}

fn init_logs() {
    let mut builder = env_logger::LogBuilder::new();
    builder.format(log_format);
    builder.filter(None, log::LogLevelFilter::Trace);
    if env::var("WAY_COOLER_LOG").is_ok() {
        builder.parse(&env::var("WAY_COOLER_LOG").expect("WAY_COOLER_LOG not defined"));
    }
    builder.init().expect("Unable to initialize logging!");
    info!("Logger initialized");
}

/// Handler for SIGINT signal
extern "C" fn sig_handle(_: nix::libc::c_int) {
    lua::terminate();
    exit(130);
}
<|endoftext|>


<commit_before>use super::*;
use mktemp::Temp;
use std::path::PathBuf;

pub fn create_hook(path: PathBuf, contents: &str, mode: u32) {
    use std::fs::File;
    use std::fs::create_dir_all;
    use std::fs::metadata;
    use std::io::Write;
    use std::os::unix::fs::PermissionsExt;

    // ensure path exists
    create_dir_all(path.parent().unwrap()).unwrap();

    let mut file = File::create(&path).unwrap();
    file.write_all(contents.as_bytes()).unwrap();

    let mut permissions = metadata(path).unwrap().permissions();
    permissions.set_mode(mode);
    file.set_permissions(permissions).unwrap();
}

pub fn product_uid_hook(path: &Path) -> PathBuf {
    path.join(PRODUCT_UID_HOOK)
}

pub fn version_hook(path: &Path) -> PathBuf {
    path.join(VERSION_HOOK)
}

pub fn hardware_hook(path: &Path) -> PathBuf {
    path.join(HARDWARE_HOOK)
}

pub fn device_identity_dir(path: &Path) -> PathBuf {
    path.join(DEVICE_IDENTITY_DIR).join("identity")
}

pub fn device_attributes_dir(path: &Path) -> PathBuf {
    path.join(DEVICE_ATTRIBUTES_DIR).join("attributes")
}

pub enum FakeDevice {
    NoUpdate,
    HasUpdate,
    ExtraPoll,
    InvalidHardware,
}

pub fn create_fake_metadata(device: FakeDevice) -> PathBuf {
    let tmpdir = Temp::new_dir().unwrap().to_path_buf();

    // create fake hooks to be used to validate the load
    create_hook(
        product_uid_hook(&tmpdir),
        "#!/bin/sh\necho 229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
        0o755,
    );
    create_hook(version_hook(&tmpdir), "#!/bin/sh\necho 1.1", 0o755);
    create_hook(
        hardware_hook(&tmpdir),
        &format!(
            "#!/bin/sh\necho {}",
            match device {
                FakeDevice::InvalidHardware => "invalid",
                _ => "board",
            }
        ),
        0o755,
    );
    create_hook(
        device_identity_dir(&tmpdir),
        &format!(
            "#!/bin/sh\necho id1=value{}\necho id2=value2",
            match device {
                FakeDevice::NoUpdate => 1,
                FakeDevice::HasUpdate => 2,
                FakeDevice::ExtraPoll => 3,
                FakeDevice::InvalidHardware => 4,
            }
        ),
        0o755,
    );
    create_hook(
        device_attributes_dir(&tmpdir),
        "#!/bin/sh\necho attr1=attrvalue1\necho attr2=attrvalue2",
        0o755,
    );

    tmpdir
}

#[test]
fn run_multiple_hooks_in_a_dir() {
    let tmpdir = Temp::new_dir().unwrap();

    // create two scripts so we can test the parsing of output
    create_hook(
        tmpdir.to_path_buf().join("hook1"),
        "#!/bin/sh\necho key2=val2\necho key1=val1",
        0o755,
    );
    create_hook(
        tmpdir.to_path_buf().join("hook2"),
        "#!/bin/sh\necho key2=val4\necho key1=val3",
        0o755,
    );

    let fv = run_hooks_from_dir(&tmpdir.to_path_buf()).unwrap();

    assert_eq!(fv.keys().len(), 2);
    assert_eq!(fv.keys().collect::<Vec<_>>(), ["key1", "key2"]);
    assert_eq!(fv["key1"], ["val1", "val3"]);
    assert_eq!(fv["key2"], ["val2", "val4"]);
}

#[test]
fn check_load_metadata() {
    use std::fs::remove_file;

    {
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        // check error with a invalid product uid
        create_hook(
            product_uid_hook(&metadata_dir),
            "#!/bin/sh\necho 123",
            0o755,
        );
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check error when lacks product uid
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(product_uid_hook(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check error when lacks device identity
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(device_identity_dir(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check if is still valid without device attributes
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(device_attributes_dir(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir).unwrap();
        assert_eq!(
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            metadata.product_uid
        );
        assert_eq!("1.1", metadata.version);
        assert_eq!("board", metadata.hardware);
        assert_eq!(2, metadata.device_identity.len());
        assert_eq!(0, metadata.device_attributes.len());
    }

    {
        // complete metadata
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        let metadata = Metadata::new(&metadata_dir).unwrap();
        assert_eq!(
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            metadata.product_uid
        );
        assert_eq!("1.1", metadata.version);
        assert_eq!("board", metadata.hardware);
        assert_eq!(2, metadata.device_identity.len());
        assert_eq!(2, metadata.device_attributes.len());
    }
}
<commit_msg>firmware: tests: Add missing copyright / license header<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use super::*;
use mktemp::Temp;
use std::path::PathBuf;

pub fn create_hook(path: PathBuf, contents: &str, mode: u32) {
    use std::fs::File;
    use std::fs::create_dir_all;
    use std::fs::metadata;
    use std::io::Write;
    use std::os::unix::fs::PermissionsExt;

    // ensure path exists
    create_dir_all(path.parent().unwrap()).unwrap();

    let mut file = File::create(&path).unwrap();
    file.write_all(contents.as_bytes()).unwrap();

    let mut permissions = metadata(path).unwrap().permissions();
    permissions.set_mode(mode);
    file.set_permissions(permissions).unwrap();
}

pub fn product_uid_hook(path: &Path) -> PathBuf {
    path.join(PRODUCT_UID_HOOK)
}

pub fn version_hook(path: &Path) -> PathBuf {
    path.join(VERSION_HOOK)
}

pub fn hardware_hook(path: &Path) -> PathBuf {
    path.join(HARDWARE_HOOK)
}

pub fn device_identity_dir(path: &Path) -> PathBuf {
    path.join(DEVICE_IDENTITY_DIR).join("identity")
}

pub fn device_attributes_dir(path: &Path) -> PathBuf {
    path.join(DEVICE_ATTRIBUTES_DIR).join("attributes")
}

pub enum FakeDevice {
    NoUpdate,
    HasUpdate,
    ExtraPoll,
    InvalidHardware,
}

pub fn create_fake_metadata(device: FakeDevice) -> PathBuf {
    let tmpdir = Temp::new_dir().unwrap().to_path_buf();

    // create fake hooks to be used to validate the load
    create_hook(
        product_uid_hook(&tmpdir),
        "#!/bin/sh\necho 229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
        0o755,
    );
    create_hook(version_hook(&tmpdir), "#!/bin/sh\necho 1.1", 0o755);
    create_hook(
        hardware_hook(&tmpdir),
        &format!(
            "#!/bin/sh\necho {}",
            match device {
                FakeDevice::InvalidHardware => "invalid",
                _ => "board",
            }
        ),
        0o755,
    );
    create_hook(
        device_identity_dir(&tmpdir),
        &format!(
            "#!/bin/sh\necho id1=value{}\necho id2=value2",
            match device {
                FakeDevice::NoUpdate => 1,
                FakeDevice::HasUpdate => 2,
                FakeDevice::ExtraPoll => 3,
                FakeDevice::InvalidHardware => 4,
            }
        ),
        0o755,
    );
    create_hook(
        device_attributes_dir(&tmpdir),
        "#!/bin/sh\necho attr1=attrvalue1\necho attr2=attrvalue2",
        0o755,
    );

    tmpdir
}

#[test]
fn run_multiple_hooks_in_a_dir() {
    let tmpdir = Temp::new_dir().unwrap();

    // create two scripts so we can test the parsing of output
    create_hook(
        tmpdir.to_path_buf().join("hook1"),
        "#!/bin/sh\necho key2=val2\necho key1=val1",
        0o755,
    );
    create_hook(
        tmpdir.to_path_buf().join("hook2"),
        "#!/bin/sh\necho key2=val4\necho key1=val3",
        0o755,
    );

    let fv = run_hooks_from_dir(&tmpdir.to_path_buf()).unwrap();

    assert_eq!(fv.keys().len(), 2);
    assert_eq!(fv.keys().collect::<Vec<_>>(), ["key1", "key2"]);
    assert_eq!(fv["key1"], ["val1", "val3"]);
    assert_eq!(fv["key2"], ["val2", "val4"]);
}

#[test]
fn check_load_metadata() {
    use std::fs::remove_file;

    {
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        // check error with a invalid product uid
        create_hook(
            product_uid_hook(&metadata_dir),
            "#!/bin/sh\necho 123",
            0o755,
        );
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check error when lacks product uid
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(product_uid_hook(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check error when lacks device identity
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(device_identity_dir(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir);
        assert!(metadata.is_err());
    }

    {
        // check if is still valid without device attributes
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        remove_file(device_attributes_dir(&metadata_dir)).unwrap();
        let metadata = Metadata::new(&metadata_dir).unwrap();
        assert_eq!(
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            metadata.product_uid
        );
        assert_eq!("1.1", metadata.version);
        assert_eq!("board", metadata.hardware);
        assert_eq!(2, metadata.device_identity.len());
        assert_eq!(0, metadata.device_attributes.len());
    }

    {
        // complete metadata
        let metadata_dir = create_fake_metadata(FakeDevice::NoUpdate);
        let metadata = Metadata::new(&metadata_dir).unwrap();
        assert_eq!(
            "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
            metadata.product_uid
        );
        assert_eq!("1.1", metadata.version);
        assert_eq!("board", metadata.hardware);
        assert_eq!(2, metadata.device_identity.len());
        assert_eq!(2, metadata.device_attributes.len());
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ops::Deref;

use serde::ser::{Serialize, Serializer};

use types::*;

macro_rules! integer_id_impls {
    ($name: ident) => {
        impl $name {
            pub fn new(inner: Integer) -> Self {
                $name(inner)
            }
        }

        impl ::std::fmt::Display for $name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                self.0.fmt(f)
            }
        }

        impl From<Integer> for $name {
            fn from(inner: Integer) -> Self {
                $name::new(inner)
            }
        }

        impl From<$name> for Integer {
            fn from(from: $name) -> Self {
                from.0
            }
        }

        impl<'de> ::serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
                where D: ::serde::de::Deserializer<'de>
            {
                let inner = ::serde::de::Deserialize::deserialize(deserializer)?;
                Ok($name::new(inner))
            }
        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::ser::Serializer
            {
                serializer.serialize_i64(self.0)
            }
        }
    };
}

pub trait ToSourceChat {
    fn to_source_chat(&self) -> ChatId;
}

impl<S> ToSourceChat for S where S: Deref, S::Target: ToSourceChat {
    fn to_source_chat(&self) -> ChatId {
        self.deref().to_source_chat()
    }
}

impl ToSourceChat for Message {
    fn to_source_chat(&self) -> ChatId {
        self.chat.id()
    }
}

/// Unique identifier for the target chat or username of the
/// target channel (in the format @channelusername)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ChatRef<'a> {
    Id(ChatId),
    #[doc(hidden)]
    ChannelUsername(Cow<'a, str>,),
}

impl<'a> ChatRef<'a> {
    pub fn from_chat_id(chat_id: ChatId) -> ChatRef<'a> {
        ChatRef::Id(chat_id)
    }
}

pub trait ToChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a>;
}

impl<'a, S> ToChatRef<'a> for S where S: Deref, S::Target: ToChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.deref().to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.clone()
    }
}

impl<'a> ToChatRef<'a> for Chat {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.id().to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ChatMember {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.user.to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ForwardFrom {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        match *self {
            ForwardFrom::User {ref user, ..} => user.to_chat_ref(),
            ForwardFrom::Channel {ref channel, ..} => channel.to_chat_ref(),
        }
    }
}

impl<'a> ToChatRef<'a> for Forward {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.from.to_chat_ref()
    }
}

impl<'a> Serialize for ChatRef<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        match *self {
            ChatRef::Id(id) => serializer.serialize_i64(id.into()),
            ChatRef::ChannelUsername(ref username) => serializer.serialize_str(&username),
        }
    }
}

macro_rules! chat_id_impls {
    ($id: ident) => {
        integer_id_impls!($id);

        impl<'a> ToChatRef<'a> for $id {
            fn to_chat_ref(&self) -> ChatRef<'a> {
                ChatRef::from_chat_id((*self).into())
            }
        }
    };
}

macro_rules! specific_chat_id_impls {
    ($id: ident, $typ: ident) => {
        chat_id_impls!($id);

        impl From<$id> for ChatId {
            fn from(c: $id) -> Self {
                ChatId::new(c.into())
            }
        }

        impl<'a> ToChatRef<'a> for $typ {
            fn to_chat_ref(&self) -> ChatRef<'a> {
                self.id.to_chat_ref()
            }
        }
    };
}

pub trait ToUserId {
    fn to_user_id(&self) -> UserId;
}

impl<S> ToUserId for S where S: Deref, S::Target: ToUserId {
    fn to_user_id(&self) -> UserId {
        self.deref().to_user_id()
    }
}

impl ToUserId for UserId {
    fn to_user_id(&self) -> UserId {
        *self
    }
}

impl ToUserId for ChatMember {
    fn to_user_id(&self) -> UserId {
        self.user.id
    }
}

impl ToUserId for User {
    fn to_user_id(&self) -> UserId {
        self.id
    }
}

/// Unique user identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserId(Integer);
specific_chat_id_impls!(UserId, User);

/// Unique group identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GroupId(Integer);
specific_chat_id_impls!(GroupId, Group);

/// Unique supergroup identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SupergroupId(Integer);
specific_chat_id_impls!(SupergroupId, Supergroup);

/// Unique channel identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChannelId(Integer);
specific_chat_id_impls!(ChannelId, Channel);

/// Unique chat identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChatId(Integer);
chat_id_impls!(ChatId);

pub trait ToMessageId {
    fn to_message_id(&self) -> MessageId;
}

impl<S> ToMessageId for S where S: Deref, S::Target: ToMessageId {
    fn to_message_id(&self) -> MessageId {
        self.deref().to_message_id()
    }
}

impl ToMessageId for MessageId {
    fn to_message_id(&self) -> MessageId {
        *self
    }
}

impl ToMessageId for Message {
    fn to_message_id(&self) -> MessageId {
        self.id
    }
}

/// Unique message identifier inside a chat.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MessageId(Integer);
integer_id_impls!(MessageId);
<commit_msg>docs: document conversion types<commit_after>use std::borrow::Cow;
use std::ops::Deref;

use serde::ser::{Serialize, Serializer};

use types::*;

macro_rules! integer_id_impls {
    ($name: ident) => {
        impl $name {
            pub fn new(inner: Integer) -> Self {
                $name(inner)
            }
        }

        impl ::std::fmt::Display for $name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                self.0.fmt(f)
            }
        }

        impl From<Integer> for $name {
            fn from(inner: Integer) -> Self {
                $name::new(inner)
            }
        }

        impl From<$name> for Integer {
            fn from(from: $name) -> Self {
                from.0
            }
        }

        impl<'de> ::serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
                where D: ::serde::de::Deserializer<'de>
            {
                let inner = ::serde::de::Deserialize::deserialize(deserializer)?;
                Ok($name::new(inner))
            }
        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::ser::Serializer
            {
                serializer.serialize_i64(self.0)
            }
        }
    };
}

/// Get source `ChatId` from the type reference.
pub trait ToSourceChat {
    fn to_source_chat(&self) -> ChatId;
}

impl<S> ToSourceChat for S where S: Deref, S::Target: ToSourceChat {
    fn to_source_chat(&self) -> ChatId {
        self.deref().to_source_chat()
    }
}

impl ToSourceChat for Message {
    fn to_source_chat(&self) -> ChatId {
        self.chat.id()
    }
}

/// Unique identifier for the target chat or username of the
/// target channel (in the format @channelusername)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ChatRef<'a> {
    Id(ChatId),
    #[doc(hidden)]
    ChannelUsername(Cow<'a, str>,),
}

impl<'a> ChatRef<'a> {
    pub fn from_chat_id(chat_id: ChatId) -> ChatRef<'a> {
        ChatRef::Id(chat_id)
    }
}

/// Get `ChatRef` from the type reference.
pub trait ToChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a>;
}

impl<'a, S> ToChatRef<'a> for S where S: Deref, S::Target: ToChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.deref().to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ChatRef<'a> {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.clone()
    }
}

impl<'a> ToChatRef<'a> for Chat {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.id().to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ChatMember {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.user.to_chat_ref()
    }
}

impl<'a> ToChatRef<'a> for ForwardFrom {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        match *self {
            ForwardFrom::User {ref user, ..} => user.to_chat_ref(),
            ForwardFrom::Channel {ref channel, ..} => channel.to_chat_ref(),
        }
    }
}

impl<'a> ToChatRef<'a> for Forward {
    fn to_chat_ref(&self) -> ChatRef<'a> {
        self.from.to_chat_ref()
    }
}

impl<'a> Serialize for ChatRef<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        match *self {
            ChatRef::Id(id) => serializer.serialize_i64(id.into()),
            ChatRef::ChannelUsername(ref username) => serializer.serialize_str(&username),
        }
    }
}

macro_rules! chat_id_impls {
    ($id: ident) => {
        integer_id_impls!($id);

        impl<'a> ToChatRef<'a> for $id {
            fn to_chat_ref(&self) -> ChatRef<'a> {
                ChatRef::from_chat_id((*self).into())
            }
        }
    };
}

macro_rules! specific_chat_id_impls {
    ($id: ident, $typ: ident) => {
        chat_id_impls!($id);

        impl From<$id> for ChatId {
            fn from(c: $id) -> Self {
                ChatId::new(c.into())
            }
        }

        impl<'a> ToChatRef<'a> for $typ {
            fn to_chat_ref(&self) -> ChatRef<'a> {
                self.id.to_chat_ref()
            }
        }
    };
}

/// Get `UserId` from the type reference.
pub trait ToUserId {
    fn to_user_id(&self) -> UserId;
}

impl<S> ToUserId for S where S: Deref, S::Target: ToUserId {
    fn to_user_id(&self) -> UserId {
        self.deref().to_user_id()
    }
}

impl ToUserId for UserId {
    fn to_user_id(&self) -> UserId {
        *self
    }
}

impl ToUserId for ChatMember {
    fn to_user_id(&self) -> UserId {
        self.user.id
    }
}

impl ToUserId for User {
    fn to_user_id(&self) -> UserId {
        self.id
    }
}

/// Unique user identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserId(Integer);
specific_chat_id_impls!(UserId, User);

/// Unique group identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GroupId(Integer);
specific_chat_id_impls!(GroupId, Group);

/// Unique supergroup identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SupergroupId(Integer);
specific_chat_id_impls!(SupergroupId, Supergroup);

/// Unique channel identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChannelId(Integer);
specific_chat_id_impls!(ChannelId, Channel);

/// Unique chat identifier.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChatId(Integer);
chat_id_impls!(ChatId);

/// Get `MessageId` from the type reference.
pub trait ToMessageId {
    fn to_message_id(&self) -> MessageId;
}

impl<S> ToMessageId for S where S: Deref, S::Target: ToMessageId {
    fn to_message_id(&self) -> MessageId {
        self.deref().to_message_id()
    }
}

impl ToMessageId for MessageId {
    fn to_message_id(&self) -> MessageId {
        *self
    }
}

impl ToMessageId for Message {
    fn to_message_id(&self) -> MessageId {
        self.id
    }
}

/// Unique message identifier inside a chat.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MessageId(Integer);
integer_id_impls!(MessageId);
<|endoftext|>


<commit_before>extern crate wasm_ast;

use self::TypeError::{DiscardedOpErr, NoSuchFunctionErr};
use wasm_ast::{BinOp, Export, Expr, Function, Import, Module, SignedTyp, Typ, VarDec};
use wasm_ast::Expr::{BinOpExpr, CallExpr, ConstExpr, GetLocalExpr, GrowMemoryExpr, IfThenExpr, IfThenElseExpr, LoadExpr, NopExpr, SetLocalExpr, StoreExpr, UnaryOpExpr};
use wasm_ast::SignedTyp::{F32s, F64s, I32s, I64s, U32s, U64s};
use wasm_ast::Typ::{F32, F64, I32, I64};

pub enum TypeError {
    NoSuchFunctionErr(String),
    DiscardedOpErr,
    OpTypeError,
}

pub trait TypeCheck {
    fn unsigned_type(&self, typ: &SignedTyp) -> Typ;
    fn lookup_function(&self, name: &str) -> Result<&Function, TypeError>;
    fn type_check_binop(&self, src: &SignedTyp, binop: &BinOp, tgt: &Typ) -> Result<(), TypeError>;
    fn type_check_expr(&self, vars: &[&VarDec], expr: &Expr, typ: &Option<Typ>) -> Result<(), TypeError>;
    fn type_check_function(&self, function: &Function) -> Result<(), TypeError>;
    fn type_check_export(&self, export: &Export) -> Result<(), TypeError>;
    fn type_check_module(&self) -> Result<(), TypeError>;
}

impl TypeCheck for Module {

    fn lookup_function(&self, name: &str) -> Result<&Function, TypeError> {
        self.functions.iter()
            .find(|function| function.name == name)
            .map_or_else (|| Err(NoSuchFunctionErr(String::from(name))), Ok)
    }

    fn unsigned_type(&self, typ: &SignedTyp) -> Typ {
        match typ {
            &F32s => F32,
            &F64s => F64,
            &I32s => I32,
            &I64s => I64,
            &U32s => I32,
            &U64s => I64,
        }
    }

    fn type_check_binop(&self, src: &SignedTyp, binop: &BinOp, tgt: &Typ) -> Result<(), TypeError> {
        match (src, binop, tgt) {
            _ => panic!("TODO"),
        }
    }

    fn type_check_expr(&self, vars: &[&VarDec], expr: &Expr, tgt: &Option<Typ>) -> Result<(), TypeError> {
        match expr {
            &BinOpExpr(ref src, ref binop, ref lhs, ref rhs) => {
                let tgt = try!(tgt.map_or(Err(DiscardedOpErr), Ok));
                let usrc = self.unsigned_type(src);
                try!(self.type_check_binop(src, binop, &tgt));
                try!(self.type_check_expr(vars, lhs, &Some(usrc)));
                try!(self.type_check_expr(vars, rhs, &Some(usrc)));
            },
            _ => panic!("TODO"),
        }
        Ok(())
    }

    fn type_check_function(&self, function: &Function) -> Result<(), TypeError> {
        let vars: Vec<&VarDec> = function.params.iter().chain(function.locals.iter()).collect();
        let mut exprs = function.body.iter();
        let last = exprs.next_back();
        for expr in exprs {
            try!(self.type_check_expr(&vars[..], expr, &None));
        }
        if let Some(expr) = last {
            try!(self.type_check_expr(&vars[..], expr, &function.result));
        }
        Ok(())
    }

    fn type_check_export(&self, export: &Export) -> Result<(), TypeError> {
        try!(self.lookup_function(&*export.name));
        Ok(())
    }

    fn type_check_module(&self) -> Result<(), TypeError> {
        for function in self.functions.iter() {
            try!(self.type_check_function(&function));
        }
        for export in self.exports.iter() {
            try!(self.type_check_export(&export));
        }
        Ok(())
    }

}
<commit_msg>Added type checking for expressions.<commit_after>extern crate wasm_ast;

use self::TypeError::{BinOpResultErr, ConstTypeErr, GrowMemoryResultErr, NoSuchFunctionErr, FunctionArgCountErr, FunctionResultErr, IfThenResultErr, LoadTypeErr, NopResultErr, StoreTypeErr, UnaryOpResultErr, VarTypeErr};
use wasm_ast::{BinOp, Const, Export, Expr, Function, Module, SignedTyp, Size, Typ, UnaryOp, VarDec, VarUse};
use wasm_ast::Expr::{BinOpExpr, CallExpr, ConstExpr, GetLocalExpr, GrowMemoryExpr, IfThenExpr, IfThenElseExpr, LoadExpr, NopExpr, SetLocalExpr, StoreExpr, UnaryOpExpr};
use wasm_ast::SignedTyp::{F32s, F64s, I32s, I64s, U32s, U64s};
use wasm_ast::Size::{Bits32, Bits64};
use wasm_ast::Typ::{F32, F64, I32, I64};

pub enum TypeError {
    BinOpResultErr(SignedTyp, BinOp, Option<Typ>),
    ConstTypeErr(Const, Option<Typ>),
    FunctionArgCountErr(String, usize),
    FunctionResultErr(String, Option<Typ>),
    GrowMemoryResultErr(Typ),
    IfThenResultErr(Typ),
    LoadTypeErr(SignedTyp, Size, Option<Typ>),
    NoSuchFunctionErr(String),
    NopResultErr(Typ),
    StoreTypeErr(Typ, Size, Option<Typ>),
    UnaryOpResultErr(Typ, UnaryOp, Option<Typ>),
    VarTypeErr(VarUse, Option<Typ>),
}

pub trait TypeCheck {
    fn size_of_type(&self, typ: &Typ) -> Size;
    fn unsigned_type(&self, typ: &SignedTyp) -> Typ;

    fn lookup_function(&self, name: &str) -> Result<&Function, TypeError>;

    fn binary_op_type(&self, src: &SignedTyp, op: &BinOp) -> Result<Typ, TypeError>;
    fn unary_op_type(&self, src: &Typ, op: &UnaryOp) -> Result<Typ, TypeError>;
    fn const_type(&self, constant: &Const) -> Typ;
    fn var_type(&self, vars: &[&VarDec], expr: &VarUse) -> Result<Typ, TypeError>;

    fn type_check_expr(&self, vars: &[&VarDec], expr: &Expr, typ: &Option<Typ>) -> Result<(), TypeError>;
    fn type_check_function(&self, function: &Function) -> Result<(), TypeError>;
    fn type_check_export(&self, export: &Export) -> Result<(), TypeError>;
    fn type_check_module(&self) -> Result<(), TypeError>;
}

impl TypeCheck for Module {

    fn lookup_function(&self, name: &str) -> Result<&Function, TypeError> {
        self.functions.iter()
            .find(|function| function.name == name)
            .map_or_else (|| Err(NoSuchFunctionErr(String::from(name))), Ok)
    }

    fn size_of_type(&self, typ: &Typ) -> Size {
        match typ {
            &F32 => Bits32,
            &F64 => Bits64,
            &I32 => Bits32,
            &I64 => Bits64,
        }
    }

    fn unsigned_type(&self, typ: &SignedTyp) -> Typ {
        match typ {
            &F32s => F32,
            &F64s => F64,
            &I32s => I32,
            &I64s => I64,
            &U32s => I32,
            &U64s => I64,
        }
    }

    fn binary_op_type(&self, src: &SignedTyp, op: &BinOp) -> Result<Typ, TypeError> {
        match (src, op) {
            _ => panic!("TODO"),
        }
    }

    fn unary_op_type(&self, src: &Typ, op: &UnaryOp) -> Result<Typ, TypeError> {
        match (src, op) {
            _ => panic!("TODO"),
        }
    }

    fn const_type(&self, constant: &Const) -> Typ {
        match constant {
            _ => panic!("TODO"),
        }
    }

    fn var_type(&self, vars: &[&VarDec], expr: &VarUse) -> Result<Typ, TypeError> {
        panic!("TODO")
    }

    fn type_check_expr(&self, vars: &[&VarDec], expr: &Expr, tgt: &Option<Typ>) -> Result<(), TypeError> {
        match expr {
            &BinOpExpr(ref src, ref op, ref lhs, ref rhs) => {
                let usrc = self.unsigned_type(src);
                try!(self.type_check_expr(vars, lhs, &Some(usrc)));
                try!(self.type_check_expr(vars, rhs, &Some(usrc)));
                if tgt != &Some(try!(self.binary_op_type(src, op))) {
                    return Err(BinOpResultErr(*src, *op, *tgt));
                }
            },
            &CallExpr(ref name, ref args) => {
                let function = try!(self.lookup_function(name));
                if function.params.len() != args.len() {
                    return Err(FunctionArgCountErr(name.clone(), args.len()));
                }
                for (arg, param) in args.iter().zip(function.params.iter()) {
                    try!(self.type_check_expr(vars, arg, &Some(param.typ)));
                }
                if &function.result != tgt {
                    return Err(FunctionResultErr(name.clone(), *tgt))
                }
            },
            &ConstExpr(ref constant) => {
                if tgt != &Some(self.const_type(constant)) {
                    return Err(ConstTypeErr(*constant, *tgt));
                }
            },
            &GetLocalExpr(ref var) => {
                if tgt != &Some(try!(self.var_type(vars, var))) {
                    return Err(VarTypeErr(var.clone(), *tgt));
                }
            },
            &GrowMemoryExpr(ref size) => {
                try!(self.type_check_expr(vars, size, &Some(I32)));
                if let &Some(ref tgt) = tgt {
                    return Err(GrowMemoryResultErr(*tgt))
                }
            },
            &IfThenExpr(ref cond, ref true_branch) => {
                try!(self.type_check_expr(vars, cond, &Some(I32)));
                try!(self.type_check_expr(vars, true_branch, tgt));
                if let &Some(ref tgt) = tgt {
                    return Err(IfThenResultErr(*tgt))
                }
            },
            &IfThenElseExpr(ref cond, ref true_branch, ref false_branch) => {
                try!(self.type_check_expr(vars, cond, &Some(I32)));
                try!(self.type_check_expr(vars, true_branch, tgt));
                try!(self.type_check_expr(vars, false_branch, tgt));
            },
            &LoadExpr(ref src, ref size, ref addr) => {
                let usrc = self.unsigned_type(src);
                try!(self.type_check_expr(vars, addr, &Some(I32)));
                if tgt != &Some(usrc) || *size > self.size_of_type(&usrc) {
                    return Err(LoadTypeErr(*src, *size, *tgt))
                }
            },
            &NopExpr => {
                if let &Some(ref tgt) = tgt {
                    return Err(NopResultErr(*tgt));
                }
            },
            &SetLocalExpr(ref var, ref rhs) => {
                try!(self.type_check_expr(vars, rhs, tgt));
                if tgt != &Some(try!(self.var_type(vars, var))) {
                    return Err(VarTypeErr(var.clone(), *tgt));
                }
            },
            &StoreExpr(ref src, ref size, ref addr, ref rhs) => {
                try!(self.type_check_expr(vars, addr, &Some(I32)));
                try!(self.type_check_expr(vars, rhs, tgt));
                if tgt != &Some(*src) || *size > self.size_of_type(src) {
                    return Err(StoreTypeErr(*src, *size, *tgt))
                }
            },
            &UnaryOpExpr(ref src, ref op, ref arg) => {
                try!(self.type_check_expr(vars, arg, &Some(*src)));
                if tgt != &Some(try!(self.unary_op_type(src, op))) {
                    return Err(UnaryOpResultErr(*src, *op, *tgt));
                }
            },
        }
        Ok(())
    }

    fn type_check_function(&self, function: &Function) -> Result<(), TypeError> {
        let vars: Vec<&VarDec> = function.params.iter().chain(function.locals.iter()).collect();
        let mut exprs = function.body.iter();
        let last = exprs.next_back();
        for expr in exprs {
            try!(self.type_check_expr(&vars[..], expr, &None));
        }
        if let Some(expr) = last {
            try!(self.type_check_expr(&vars[..], expr, &function.result));
        }
        Ok(())
    }

    fn type_check_export(&self, export: &Export) -> Result<(), TypeError> {
        try!(self.lookup_function(&*export.name));
        Ok(())
    }

    fn type_check_module(&self) -> Result<(), TypeError> {
        for function in self.functions.iter() {
            try!(self.type_check_function(&function));
        }
        for export in self.exports.iter() {
            try!(self.type_check_export(&export));
        }
        Ok(())
    }

}
<|endoftext|>


<commit_before>extern crate bolts;

use std::path::Path;
include!("../config.rs");
fn main() {

	bolts::run(config::default_layout(), config::markdown_file());
}
<commit_msg>making generated app have no warnings<commit_after>extern crate bolts;

include!("../config.rs");
fn main() {

	bolts::run(config::default_layout(), config::markdown_file()).unwrap();
}
<|endoftext|>


<commit_before>pub const TILES_PCS_W: usize = 64;
pub const TILES_PCS_H: usize = 64;

pub const TILEMAP_BUF_LENGTH: usize = 4096;
pub const CHARACTER_BUF_LENGTH: usize = 224;

pub const RESOLUTION_X: u32 = 1920;
pub const RESOLUTION_Y: u32 = 1080;

pub const ASPECT_RATIO: f32 = (RESOLUTION_X / RESOLUTION_Y) as f32;

pub const VIEW_DISTANCE: f32 = 300.0;

pub const CHARSHEET_TOTAL_WIDTH: f32 = 16_128f32;
pub const SPRITE_OFFSET: f32 = 2.0;

pub const ZOMBIESHEET_TOTAL_WIDTH: f32 = 7_872f32;

//Assets
pub const ZOMBIE_JSON_PATH: &'static str = "assets/zombie.json";
pub const CHARACTER_JSON_PATH: &'static str = "assets/character.json";
pub const PISTOL_AUDIO_PATH: &'static str = "assets/audio/pistol.ogg";
pub const MAP_FILE_PATH: &'static str = "assets/maps/tilemap.tmx";
pub const RUN_SPRITE_OFFSET: usize = 64;
<commit_msg>Remove unnecessary static lifetimes<commit_after>pub const TILES_PCS_W: usize = 64;
pub const TILES_PCS_H: usize = 64;

pub const TILEMAP_BUF_LENGTH: usize = 4096;
pub const CHARACTER_BUF_LENGTH: usize = 224;

pub const RESOLUTION_X: u32 = 1920;
pub const RESOLUTION_Y: u32 = 1080;

pub const ASPECT_RATIO: f32 = (RESOLUTION_X / RESOLUTION_Y) as f32;

pub const VIEW_DISTANCE: f32 = 300.0;

pub const CHARSHEET_TOTAL_WIDTH: f32 = 16_128f32;
pub const SPRITE_OFFSET: f32 = 2.0;

pub const ZOMBIESHEET_TOTAL_WIDTH: f32 = 7_872f32;

//Assets
pub const ZOMBIE_JSON_PATH: &str = "assets/zombie.json";
pub const CHARACTER_JSON_PATH: &str = "assets/character.json";
pub const PISTOL_AUDIO_PATH: &str = "assets/audio/pistol.ogg";
pub const MAP_FILE_PATH: &str = "assets/maps/tilemap.tmx";
pub const RUN_SPRITE_OFFSET: usize = 64;
<|endoftext|>


<commit_before>use bullet;
use character;
use critter::CharacterSprite;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use graphics::{DeltaTime, orientation::Stance};
use graphics::Drawables;
use hud;
use hud::TextDrawable;
use specs;
use specs::prelude::{ReadStorage, Read, WriteStorage};
use std::time::Instant;
use terrain;
use terrain_object;
use terrain_object::TerrainTexture;
use zombie;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  terrain_object_system: [terrain_object::TerrainObjectDrawSystem<D::Resources>; 2],
  text_system: hud::TextDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
                where F: gfx::Factory<D::Resources> {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      terrain_object_system: [
        terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone(), TerrainTexture::House),
        terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone(), TerrainTexture::Tree)
      ],
      text_system: hud::TextDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0,
    }
  }
}

impl<'a, D> specs::prelude::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     ReadStorage<'a, TextDrawable>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     WriteStorage<'a, terrain_object::terrain_objects::TerrainObjects>,
                     Read<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, text, mut zombies, mut bullets, mut terrain_objects, d): Self::SystemData) {
    use specs::join::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.1;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "framerate") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, tx, zs, bs, obj) in (&mut terrain, &mut character, &mut character_sprite, &text, &mut zombies, &mut bullets, &mut terrain_objects).join() {
      self.terrain_system.draw(t, &mut encoder);

      self.text_system.draw(tx, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_death_idx(5),
            Stance::CriticalDeath => z.update_death_idx(7),
            Stance::Walking => z.update_alive_idx(7),
            Stance::Still => z.update_alive_idx(3),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      let mut drawables: Vec<Drawables> = vec![];
      drawables.append(&mut bs.bullets.iter().map(|b| Drawables::Bullet(b)).collect());
      drawables.append(&mut zs.zombies.iter_mut().map(|z| Drawables::Zombie(z)).collect());

      for (idx, o) in obj.objects.iter().enumerate() {
        if idx < 2 {
          drawables.push(Drawables::TerrainHouse(o));
        } else {
          drawables.push(Drawables::TerrainTree(o));
        }
      }

      drawables.push(Drawables::Character(c));

      drawables.sort_by(|a, b| {
        let a_val = match a {
          Drawables::Bullet(e) => e.position.position[1],
          Drawables::Zombie(e) => e.position.position[1],
          Drawables::TerrainHouse(e) => e.position.position[1],
          Drawables::TerrainTree(e) => e.position.position[1],
          Drawables::Character(e) => e.position.position[1],
        };

        let b_val = match b {
          Drawables::Bullet(e) => e.position.position[1],
          Drawables::Zombie(e) => e.position.position[1],
          Drawables::TerrainHouse(e) => e.position.position[1],
          Drawables::TerrainTree(e) => e.position.position[1],
          Drawables::Character(e) => e.position.position[1],
        };

        b_val.partial_cmp(&a_val).unwrap()
      });

      for mut e in &mut drawables {
        match *e {
          Drawables::Bullet(ref e) => { self.bullet_system.draw(e, &mut encoder) },
          Drawables::Zombie(ref mut e) => { self.zombie_system.draw(e, &mut encoder) },
          Drawables::TerrainHouse(ref mut e) => { self.terrain_object_system[0].draw(e, &mut encoder) },
          Drawables::TerrainTree(ref mut e) => { self.terrain_object_system[1].draw(e, &mut encoder) },
          Drawables::Character(ref mut e) => { self.character_system.draw(e, cs, &mut encoder) },
        }
      }
    }

    self.encoder_queue.sender.send(encoder);
  }
}
<commit_msg>Adjust character animation speed<commit_after>use bullet;
use character;
use critter::CharacterSprite;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use graphics::{DeltaTime, orientation::Stance};
use graphics::Drawables;
use hud;
use hud::TextDrawable;
use specs;
use specs::prelude::{ReadStorage, Read, WriteStorage};
use std::time::Instant;
use terrain;
use terrain_object;
use terrain_object::TerrainTexture;
use zombie;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  terrain_object_system: [terrain_object::TerrainObjectDrawSystem<D::Resources>; 2],
  text_system: hud::TextDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
                where F: gfx::Factory<D::Resources> {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      terrain_object_system: [
        terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone(), TerrainTexture::House),
        terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone(), TerrainTexture::Tree)
      ],
      text_system: hud::TextDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0,
    }
  }
}

impl<'a, D> specs::prelude::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     ReadStorage<'a, TextDrawable>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     WriteStorage<'a, terrain_object::terrain_objects::TerrainObjects>,
                     Read<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, text, mut zombies, mut bullets, mut terrain_objects, d): Self::SystemData) {
    use specs::join::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.05;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "framerate") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, tx, zs, bs, obj) in (&mut terrain, &mut character, &mut character_sprite, &text, &mut zombies, &mut bullets, &mut terrain_objects).join() {
      self.terrain_system.draw(t, &mut encoder);

      self.text_system.draw(tx, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_death_idx(5),
            Stance::CriticalDeath => z.update_death_idx(7),
            Stance::Walking => z.update_alive_idx(7),
            Stance::Still => z.update_alive_idx(3),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      let mut drawables: Vec<Drawables> = vec![];
      drawables.append(&mut bs.bullets.iter().map(|b| Drawables::Bullet(b)).collect());
      drawables.append(&mut zs.zombies.iter_mut().map(|z| Drawables::Zombie(z)).collect());

      for (idx, o) in obj.objects.iter().enumerate() {
        if idx < 2 {
          drawables.push(Drawables::TerrainHouse(o));
        } else {
          drawables.push(Drawables::TerrainTree(o));
        }
      }

      drawables.push(Drawables::Character(c));

      drawables.sort_by(|a, b| {
        let a_val = match a {
          Drawables::Bullet(e) => e.position.position[1],
          Drawables::Zombie(e) => e.position.position[1],
          Drawables::TerrainHouse(e) => e.position.position[1],
          Drawables::TerrainTree(e) => e.position.position[1],
          Drawables::Character(e) => e.position.position[1],
        };

        let b_val = match b {
          Drawables::Bullet(e) => e.position.position[1],
          Drawables::Zombie(e) => e.position.position[1],
          Drawables::TerrainHouse(e) => e.position.position[1],
          Drawables::TerrainTree(e) => e.position.position[1],
          Drawables::Character(e) => e.position.position[1],
        };

        b_val.partial_cmp(&a_val).unwrap()
      });

      for mut e in &mut drawables {
        match *e {
          Drawables::Bullet(ref e) => { self.bullet_system.draw(e, &mut encoder) },
          Drawables::Zombie(ref mut e) => { self.zombie_system.draw(e, &mut encoder) },
          Drawables::TerrainHouse(ref mut e) => { self.terrain_object_system[0].draw(e, &mut encoder) },
          Drawables::TerrainTree(ref mut e) => { self.terrain_object_system[1].draw(e, &mut encoder) },
          Drawables::Character(ref mut e) => { self.character_system.draw(e, cs, &mut encoder) },
        }
      }
    }

    self.encoder_queue.sender.send(encoder);
  }
}
<|endoftext|>