<commit_before>//! TODO Fill in

use std::{ptr, mem};
use xcb::{xkb, Connection};
use xcb::ffi::{self, xproto};
use nix::{self, libc};
use render;
use std::ffi::{CStr, CString};
use gdk_pixbuf::Pixbuf;
use glib::translate::ToGlibPtr;
use std::fmt::{self, Display, Formatter};
use std::process::{Command, Stdio};
use std::thread;
use std::default::Default;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::{XCB_CONNECTION_HANDLE, signal};
use super::xproperty::{XProperty, XPropertyType, PROPERTIES};

// TODO FIXME
// Often we are getting some raw pointers from the xcb replies
// we need to free them because the memory management for them is manual.

// TODO this isn't defined in the xcb crate, even though it should be.
// A patch should be open adding this to its generation scheme
extern "C" {
    fn xcb_xkb_get_names_value_list_unpack(buffer: *mut libc::c_void,
                                           nTypes: u8,
                                           indicators: u32,
                                           virtualMods: u16,
                                           groupNames: u8,
                                           nKeys: u8,
                                           nKeyAliases: u8,
                                           nRadioGroups: u8,
                                           which: u32,
                                           aux: *mut ffi::xkb::xcb_xkb_get_names_value_list_t) -> libc::c_int;
}

#[derive(Clone, Debug)]
pub struct AwesomeState {
    preferred_icon_size: u32
}

impl Default for AwesomeState {
    fn default() -> Self {
        AwesomeState {
            preferred_icon_size: 0
        }
    }
}

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

impl UserData for AwesomeState {}

pub fn init(lua: &Lua) -> rlua::Result<()> {
    let awesome_table = lua.create_table()?;
    state_setup(lua, &awesome_table)?;
    meta_setup(lua, &awesome_table)?;
    method_setup(lua, &awesome_table)?;
    property_setup(lua, &awesome_table)?;
    let globals = lua.globals();
    globals.set("awesome", awesome_table)
}

fn state_setup(lua: &Lua, awesome_table: &Table) -> rlua::Result<()> {
    awesome_table.set("__data", AwesomeState::default().to_lua(lua)?)
}

fn meta_setup(lua: &Lua, awesome_table: &Table) -> rlua::Result<()> {
    let meta_table = awesome_table.get_metatable().map_or_else(|| {
        let table = lua.create_table()?;
        awesome_table.set_metatable(Some(table.clone()));
        Ok(table)
    }, |v| Ok(v))?;
    meta_table.set("__tostring", lua.create_function(|_, val: Table| {
        Ok(format!("{}", val.get::<_, AwesomeState>("__data")?))
    })?)
}

fn method_setup<'lua>(lua: &'lua Lua, awesome_table: &Table<'lua>) -> rlua::Result<()> {
    // TODO Fill in rest
    awesome_table.set("connect_signal", lua.create_function(signal::global_connect_signal)?)?;
    awesome_table.set("disconnect_signal", lua.create_function(signal::global_disconnect_signal)?)?;
    awesome_table.set("emit_signal", lua.create_function(signal::global_emit_signal)?)?;
    awesome_table.set("xrdb_get_value", lua.create_function(xrdb_get_value)?)?;
    awesome_table.set("xkb_set_layout_group", lua.create_function(xkb_set_layout_group)?)?;
    awesome_table.set("xkb_get_layout_group", lua.create_function(xkb_get_layout_group)?)?;
    awesome_table.set("set_preferred_icon_size", lua.create_function(set_preferred_icon_size)?)?;
    awesome_table.set("register_xproperty", lua.create_function(register_xproperty)?)?;
    awesome_table.set("xkb_get_group_names", lua.create_function(xkb_get_group_names)?)?;
    awesome_table.set("set_xproperty", lua.create_function(set_xproperty)?)?;
    awesome_table.set("get_xproperty", lua.create_function(get_xproperty)?)?;
    awesome_table.set("restart", lua.create_function(restart)?)?;
    awesome_table.set("load_image", lua.create_function(load_image)?)?;
    awesome_table.set("sync", lua.create_function(sync)?)?;
    awesome_table.set("exec", lua.create_function(exec)?)?;
    awesome_table.set("kill", lua.create_function(kill)?)?;
    awesome_table.set("quit", lua.create_function(quit)?)
}

fn property_setup<'lua>(lua: &'lua Lua, awesome_table: &Table<'lua>) -> rlua::Result<()> {
    // TODO Do properly
    awesome_table.set("version", "0".to_lua(lua)?)?;
    awesome_table.set("themes_path", "/usr/share/awesome/themes".to_lua(lua)?)?;
    awesome_table.set("conffile", "".to_lua(lua)?)
}

/// Registers a new X property
fn register_xproperty<'lua>(lua: &'lua Lua, (name_rust, v_type): (String, String)) -> rlua::Result<()> {
    let name = CString::new(name_rust.clone()).expect("XProperty was not CString");
    let arg_type = XPropertyType::from_string(v_type.clone())
        .ok_or(rlua::Error::RuntimeError(format!("{} not a valid xproperty", v_type)))?;
    unsafe {
        let raw_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0 as _;
        let atom_c = xproto::xcb_intern_atom_unchecked(raw_con,
                                                       false as u8,
                                                       name.to_bytes().len() as u16,
                                                       name.as_ptr());
        let atom_r = xproto::xcb_intern_atom_reply(raw_con, atom_c, ptr::null_mut());
        if atom_r.is_null() {
            return Ok(())
        }
        let new_property = XProperty::new(name_rust.clone(), arg_type, (*atom_r).atom);
        let mut properties = PROPERTIES.lock()
            .expect("Could not lock properties list");
        if let Some(found) = properties.iter().find(|&property| property == &new_property) {
            if found.type_ != new_property.type_ {
                return Err(rlua::Error::RuntimeError(
                    format!("property '{}' already registered with different type",
                            name_rust)));
            }
            return Ok(())
        }
        properties.push(new_property);
    }
    Ok(())
}

/// Get layout short names
fn xkb_get_group_names<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Value<'lua>> {
    let xcb_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0;
    unsafe {
        let con = Connection::from_raw_conn(xcb_con as _);
        let raw_con = con.get_raw_conn();
        let id = xkb::ID_USE_CORE_KBD as _;
        // The structure here looks weird because we need to ensure Connection
        // isn't cleaned up even in the event of an error...
        let names_r = {
            let names_cookie = xkb::get_names_unchecked(&con, id, xkb::NAME_DETAIL_SYMBOLS);
            names_cookie.get_reply()
        };
        mem::forget(con);
        let names_r = match names_r {
            Ok(names_r) => {
                if names_r.ptr.is_null() {
                    warn!("Failed to get xkb symbols name");
                    return Ok(Value::Nil)
                }
                names_r
            },
            Err(err) => {
                warn!("Failed to get xkb symbols name {:?}", err);
                return Ok(Value::Nil)
            }
        };
        let buffer = ffi::xkb::xcb_xkb_get_names_value_list(names_r.ptr);
        if buffer.is_null() {
            warn!("Returned buffer was NULL");
            return Ok(Value::Nil)
        }
        let names_r_ptr = names_r.ptr;
        if names_r_ptr.is_null() {
            warn!("Name reply pointer was NULL");
            return Ok(Value::Nil)
        }
        let mut names_list: ffi::xkb::xcb_xkb_get_names_value_list_t = mem::uninitialized();
        xcb_xkb_get_names_value_list_unpack(buffer, (*names_r_ptr).nTypes, (*names_r_ptr).indicators, (*names_r_ptr).virtualMods,
                                            (*names_r_ptr).groupNames, (*names_r_ptr).nKeys, (*names_r_ptr).nKeyAliases,
                                            (*names_r_ptr).nRadioGroups, (*names_r_ptr).which, &mut names_list);
        let atom_name_c = ffi::xproto::xcb_get_atom_name_unchecked(raw_con, names_list.symbolsName);
        let atom_name_r = ffi::xproto::xcb_get_atom_name_reply(raw_con, atom_name_c, ptr::null_mut());
        if atom_name_r.is_null() {
            warn!("Failed to get atom symbols name");
            return Ok(Value::Nil)
        }
        let name_c = ffi::xproto::xcb_get_atom_name_name(atom_name_r);
        CStr::from_ptr(name_c).to_string_lossy().into_owned().to_lua(lua)
    }
}

/// Restart Awesome by restarting the Lua thread
fn restart<'lua>(_: &'lua Lua, _: ()) -> rlua::Result<()> {
    use lua::{self, LuaQuery};
    if let Err(err) = lua::send(LuaQuery::Restart) {
        warn!("Could not restart Lua thread {:#?}", err);
    }
    Ok(())
}

/// Load an image from the given path
/// Returns either a cairo surface as light user data, nil and an error message
fn load_image<'lua>(lua: &'lua Lua, file_path: String) -> rlua::Result<Value<'lua>> {
    let pixbuf = Pixbuf::new_from_file(file_path.as_str())
        .map_err(|err| rlua::Error::RuntimeError(format!("{}", err)))?;
    let surface = render::load_surface_from_pixbuf(pixbuf);
    // UGH, I wanted to do to_glib_full, but that isn't defined apparently
    // So now I have to ignore the lifetime completely and just forget about the surface.
    let surface_ptr = surface.to_glib_none().0;
    ::std::mem::forget(surface);
    rlua::LightUserData(surface_ptr as _).to_lua(lua)
}

fn exec(_: &Lua, command: String) -> rlua::Result<()> {
    trace!("exec: \"{}\"", command);
    thread::Builder::new().name(command.clone()).spawn(|| {
        Command::new(command)
            .stdout(Stdio::null())
            .spawn()
            .expect("Could not spawn command")
    }).expect("Unable to spawn thread");
    Ok(())
}

/// Kills a PID with the given signal
///
/// Returns false if it could not send the signal to that process
fn kill(_: &Lua, (pid, sig): (libc::pid_t, libc::c_int)) -> rlua::Result<bool> {
    Ok(nix::sys::signal::kill(pid, sig).is_ok())
}

fn set_preferred_icon_size(lua: &Lua, val: u32) -> rlua::Result<()> {
    let mut awesome_state: AwesomeState = lua.globals().get::<_, Table>("awesome")?.get("__data")?;
    awesome_state.preferred_icon_size = val;
    lua.globals().get::<_, Table>("awesome")?.set("__data", awesome_state.to_lua(lua)?)

}

fn quit(_: &Lua, _: ()) -> rlua::Result<()> {
    ::rustwlc::terminate();
    Ok(())
}

/// No need to sync in Wayland
fn sync(_: &Lua, _: ()) -> rlua::Result<()> { Ok(()) }

fn set_xproperty(_: &Lua, _: Value) -> rlua::Result<()> {
    warn!("set_xproperty not supported");
    Ok(())
}

fn get_xproperty(_: &Lua, _: Value) -> rlua::Result<()> {
    warn!("get_xproperty not supported");
    Ok(())
}

fn xkb_set_layout_group(_: &Lua, _group: i32) -> rlua::Result<()> {
    warn!("xkb_set_layout_group not supported; Wait until wlroots");
    Ok(())
}

fn xkb_get_layout_group<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Value<'lua>> {
    use xcb::ffi::xkb;
    unsafe {
        let raw_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0 as _;
        let state_c = xkb::xcb_xkb_get_state_unchecked(raw_con, xkb::XCB_XKB_ID_USE_CORE_KBD as _);
        let state_r = xkb::xcb_xkb_get_state_reply(raw_con, state_c, ptr::null_mut());
        if state_r.is_null() {
            warn!("State reply was NULL");
            return Ok(Value::Nil);
        }
        (*state_r).group.to_lua(lua)
    }
}

fn xrdb_get_value(_lua: &Lua, (_resource_class, _resource_name): (String, String))
                  -> rlua::Result<Value> {
    warn!("xrdb_get_value not supported");
    Ok(Value::Nil)
}
<commit_msg>Add awesome.systray<commit_after>//! TODO Fill in

use std::{ptr, mem};
use xcb::{xkb, Connection};
use xcb::ffi::{self, xproto};
use nix::{self, libc};
use render;
use std::ffi::{CStr, CString};
use gdk_pixbuf::Pixbuf;
use glib::translate::ToGlibPtr;
use std::fmt::{self, Display, Formatter};
use std::process::{Command, Stdio};
use std::thread;
use std::default::Default;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::{XCB_CONNECTION_HANDLE, signal};
use super::xproperty::{XProperty, XPropertyType, PROPERTIES};

// TODO FIXME
// Often we are getting some raw pointers from the xcb replies
// we need to free them because the memory management for them is manual.

// TODO this isn't defined in the xcb crate, even though it should be.
// A patch should be open adding this to its generation scheme
extern "C" {
    fn xcb_xkb_get_names_value_list_unpack(buffer: *mut libc::c_void,
                                           nTypes: u8,
                                           indicators: u32,
                                           virtualMods: u16,
                                           groupNames: u8,
                                           nKeys: u8,
                                           nKeyAliases: u8,
                                           nRadioGroups: u8,
                                           which: u32,
                                           aux: *mut ffi::xkb::xcb_xkb_get_names_value_list_t) -> libc::c_int;
}

#[derive(Clone, Debug)]
pub struct AwesomeState {
    preferred_icon_size: u32
}

impl Default for AwesomeState {
    fn default() -> Self {
        AwesomeState {
            preferred_icon_size: 0
        }
    }
}

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

impl UserData for AwesomeState {}

pub fn init(lua: &Lua) -> rlua::Result<()> {
    let awesome_table = lua.create_table()?;
    state_setup(lua, &awesome_table)?;
    meta_setup(lua, &awesome_table)?;
    method_setup(lua, &awesome_table)?;
    property_setup(lua, &awesome_table)?;
    let globals = lua.globals();
    globals.set("awesome", awesome_table)
}

fn state_setup(lua: &Lua, awesome_table: &Table) -> rlua::Result<()> {
    awesome_table.set("__data", AwesomeState::default().to_lua(lua)?)
}

fn meta_setup(lua: &Lua, awesome_table: &Table) -> rlua::Result<()> {
    let meta_table = awesome_table.get_metatable().map_or_else(|| {
        let table = lua.create_table()?;
        awesome_table.set_metatable(Some(table.clone()));
        Ok(table)
    }, |v| Ok(v))?;
    meta_table.set("__tostring", lua.create_function(|_, val: Table| {
        Ok(format!("{}", val.get::<_, AwesomeState>("__data")?))
    })?)
}

fn method_setup<'lua>(lua: &'lua Lua, awesome_table: &Table<'lua>) -> rlua::Result<()> {
    // TODO Fill in rest
    awesome_table.set("connect_signal", lua.create_function(signal::global_connect_signal)?)?;
    awesome_table.set("disconnect_signal", lua.create_function(signal::global_disconnect_signal)?)?;
    awesome_table.set("emit_signal", lua.create_function(signal::global_emit_signal)?)?;
    awesome_table.set("xrdb_get_value", lua.create_function(xrdb_get_value)?)?;
    awesome_table.set("xkb_set_layout_group", lua.create_function(xkb_set_layout_group)?)?;
    awesome_table.set("xkb_get_layout_group", lua.create_function(xkb_get_layout_group)?)?;
    awesome_table.set("set_preferred_icon_size", lua.create_function(set_preferred_icon_size)?)?;
    awesome_table.set("register_xproperty", lua.create_function(register_xproperty)?)?;
    awesome_table.set("xkb_get_group_names", lua.create_function(xkb_get_group_names)?)?;
    awesome_table.set("set_xproperty", lua.create_function(set_xproperty)?)?;
    awesome_table.set("get_xproperty", lua.create_function(get_xproperty)?)?;
    awesome_table.set("systray", lua.create_function(systray)?)?;
    awesome_table.set("restart", lua.create_function(restart)?)?;
    awesome_table.set("load_image", lua.create_function(load_image)?)?;
    awesome_table.set("sync", lua.create_function(sync)?)?;
    awesome_table.set("exec", lua.create_function(exec)?)?;
    awesome_table.set("kill", lua.create_function(kill)?)?;
    awesome_table.set("quit", lua.create_function(quit)?)
}

fn property_setup<'lua>(lua: &'lua Lua, awesome_table: &Table<'lua>) -> rlua::Result<()> {
    // TODO Do properly
    awesome_table.set("version", "0".to_lua(lua)?)?;
    awesome_table.set("themes_path", "/usr/share/awesome/themes".to_lua(lua)?)?;
    awesome_table.set("conffile", "".to_lua(lua)?)
}

/// Registers a new X property
fn register_xproperty<'lua>(lua: &'lua Lua, (name_rust, v_type): (String, String)) -> rlua::Result<()> {
    let name = CString::new(name_rust.clone()).expect("XProperty was not CString");
    let arg_type = XPropertyType::from_string(v_type.clone())
        .ok_or(rlua::Error::RuntimeError(format!("{} not a valid xproperty", v_type)))?;
    unsafe {
        let raw_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0 as _;
        let atom_c = xproto::xcb_intern_atom_unchecked(raw_con,
                                                       false as u8,
                                                       name.to_bytes().len() as u16,
                                                       name.as_ptr());
        let atom_r = xproto::xcb_intern_atom_reply(raw_con, atom_c, ptr::null_mut());
        if atom_r.is_null() {
            return Ok(())
        }
        let new_property = XProperty::new(name_rust.clone(), arg_type, (*atom_r).atom);
        let mut properties = PROPERTIES.lock()
            .expect("Could not lock properties list");
        if let Some(found) = properties.iter().find(|&property| property == &new_property) {
            if found.type_ != new_property.type_ {
                return Err(rlua::Error::RuntimeError(
                    format!("property '{}' already registered with different type",
                            name_rust)));
            }
            return Ok(())
        }
        properties.push(new_property);
    }
    Ok(())
}

/// Get layout short names
fn xkb_get_group_names<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Value<'lua>> {
    let xcb_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0;
    unsafe {
        let con = Connection::from_raw_conn(xcb_con as _);
        let raw_con = con.get_raw_conn();
        let id = xkb::ID_USE_CORE_KBD as _;
        // The structure here looks weird because we need to ensure Connection
        // isn't cleaned up even in the event of an error...
        let names_r = {
            let names_cookie = xkb::get_names_unchecked(&con, id, xkb::NAME_DETAIL_SYMBOLS);
            names_cookie.get_reply()
        };
        mem::forget(con);
        let names_r = match names_r {
            Ok(names_r) => {
                if names_r.ptr.is_null() {
                    warn!("Failed to get xkb symbols name");
                    return Ok(Value::Nil)
                }
                names_r
            },
            Err(err) => {
                warn!("Failed to get xkb symbols name {:?}", err);
                return Ok(Value::Nil)
            }
        };
        let buffer = ffi::xkb::xcb_xkb_get_names_value_list(names_r.ptr);
        if buffer.is_null() {
            warn!("Returned buffer was NULL");
            return Ok(Value::Nil)
        }
        let names_r_ptr = names_r.ptr;
        if names_r_ptr.is_null() {
            warn!("Name reply pointer was NULL");
            return Ok(Value::Nil)
        }
        let mut names_list: ffi::xkb::xcb_xkb_get_names_value_list_t = mem::uninitialized();
        xcb_xkb_get_names_value_list_unpack(buffer, (*names_r_ptr).nTypes, (*names_r_ptr).indicators, (*names_r_ptr).virtualMods,
                                            (*names_r_ptr).groupNames, (*names_r_ptr).nKeys, (*names_r_ptr).nKeyAliases,
                                            (*names_r_ptr).nRadioGroups, (*names_r_ptr).which, &mut names_list);
        let atom_name_c = ffi::xproto::xcb_get_atom_name_unchecked(raw_con, names_list.symbolsName);
        let atom_name_r = ffi::xproto::xcb_get_atom_name_reply(raw_con, atom_name_c, ptr::null_mut());
        if atom_name_r.is_null() {
            warn!("Failed to get atom symbols name");
            return Ok(Value::Nil)
        }
        let name_c = ffi::xproto::xcb_get_atom_name_name(atom_name_r);
        CStr::from_ptr(name_c).to_string_lossy().into_owned().to_lua(lua)
    }
}

/// Query & set information about the systray
fn systray<'lua>(_: &'lua Lua, _: ()) -> rlua::Result<(u32, Value)> {
    Ok((0, Value::Nil))
}

/// Restart Awesome by restarting the Lua thread
fn restart<'lua>(_: &'lua Lua, _: ()) -> rlua::Result<()> {
    use lua::{self, LuaQuery};
    if let Err(err) = lua::send(LuaQuery::Restart) {
        warn!("Could not restart Lua thread {:#?}", err);
    }
    Ok(())
}

/// Load an image from the given path
/// Returns either a cairo surface as light user data, nil and an error message
fn load_image<'lua>(lua: &'lua Lua, file_path: String) -> rlua::Result<Value<'lua>> {
    let pixbuf = Pixbuf::new_from_file(file_path.as_str())
        .map_err(|err| rlua::Error::RuntimeError(format!("{}", err)))?;
    let surface = render::load_surface_from_pixbuf(pixbuf);
    // UGH, I wanted to do to_glib_full, but that isn't defined apparently
    // So now I have to ignore the lifetime completely and just forget about the surface.
    let surface_ptr = surface.to_glib_none().0;
    ::std::mem::forget(surface);
    rlua::LightUserData(surface_ptr as _).to_lua(lua)
}

fn exec(_: &Lua, command: String) -> rlua::Result<()> {
    trace!("exec: \"{}\"", command);
    thread::Builder::new().name(command.clone()).spawn(|| {
        Command::new(command)
            .stdout(Stdio::null())
            .spawn()
            .expect("Could not spawn command")
    }).expect("Unable to spawn thread");
    Ok(())
}

/// Kills a PID with the given signal
///
/// Returns false if it could not send the signal to that process
fn kill(_: &Lua, (pid, sig): (libc::pid_t, libc::c_int)) -> rlua::Result<bool> {
    Ok(nix::sys::signal::kill(pid, sig).is_ok())
}

fn set_preferred_icon_size(lua: &Lua, val: u32) -> rlua::Result<()> {
    let mut awesome_state: AwesomeState = lua.globals().get::<_, Table>("awesome")?.get("__data")?;
    awesome_state.preferred_icon_size = val;
    lua.globals().get::<_, Table>("awesome")?.set("__data", awesome_state.to_lua(lua)?)

}

fn quit(_: &Lua, _: ()) -> rlua::Result<()> {
    ::rustwlc::terminate();
    Ok(())
}

/// No need to sync in Wayland
fn sync(_: &Lua, _: ()) -> rlua::Result<()> { Ok(()) }

fn set_xproperty(_: &Lua, _: Value) -> rlua::Result<()> {
    warn!("set_xproperty not supported");
    Ok(())
}

fn get_xproperty(_: &Lua, _: Value) -> rlua::Result<()> {
    warn!("get_xproperty not supported");
    Ok(())
}

fn xkb_set_layout_group(_: &Lua, _group: i32) -> rlua::Result<()> {
    warn!("xkb_set_layout_group not supported; Wait until wlroots");
    Ok(())
}

fn xkb_get_layout_group<'lua>(lua: &'lua Lua, _: ()) -> rlua::Result<Value<'lua>> {
    use xcb::ffi::xkb;
    unsafe {
        let raw_con = lua.globals().get::<_, LightUserData>(XCB_CONNECTION_HANDLE)?.0 as _;
        let state_c = xkb::xcb_xkb_get_state_unchecked(raw_con, xkb::XCB_XKB_ID_USE_CORE_KBD as _);
        let state_r = xkb::xcb_xkb_get_state_reply(raw_con, state_c, ptr::null_mut());
        if state_r.is_null() {
            warn!("State reply was NULL");
            return Ok(Value::Nil);
        }
        (*state_r).group.to_lua(lua)
    }
}

fn xrdb_get_value(_lua: &Lua, (_resource_class, _resource_name): (String, String))
                  -> rlua::Result<Value> {
    warn!("xrdb_get_value not supported");
    Ok(Value::Nil)
}
<|endoftext|>


<commit_before>// Cooperative optimization solver
use std::collections::BinaryHeap;
use std::io::Write;
extern crate rand;

#[macro_use(max)]
extern crate gelpia_utils;
extern crate ga;
extern crate gr;

use ga::{ea, Individual};

use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};

use std::sync::atomic::{AtomicBool, Ordering};

use std::thread;

use std::time::Duration;

extern crate function;
use function::FuncObj;

extern crate args;
use args::{process_args};

extern crate time;

/// Returns the guaranteed upperbound for the algorithm
/// from the queue.
fn get_upper_bound(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
                   f_best_high: f64) -> f64{
    let mut max = f_best_high;
    for qi in q.iter() {
        max = max!{max, qi.fdata.upper()};
    }
    max
}

fn log_max(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
           f_best_low: f64,
           f_best_high: f64) {
    let max = get_upper_bound(q, f_best_high);
    let _ = writeln!(&mut std::io::stderr(),
                     "lb: {}, possible ub: {}, guaranteed ub: {}",
                     f_best_low,
                     f_best_high,
                     max);
}

// Returns the upper bound, the domain where this bound occurs and a status
// flag indicating whether the answer is complete for the problem.
fn ibba(x_0: Vec<GI>, e_x: Flt, e_f: Flt,
        f_bestag: Arc<RwLock<Flt>>,
        f_best_shared: Arc<RwLock<Flt>>,
        x_bestbb: Arc<RwLock<Vec<GI>>>,
        b1: Arc<Barrier>, b2: Arc<Barrier>,
        q: Arc<RwLock<BinaryHeap<Quple>>>,
        sync: Arc<AtomicBool>, stop: Arc<AtomicBool>,
        f: FuncObj,
        logging: bool)
        -> (Flt, Vec<GI>, bool) {
    let mut f_best_high = NINF;
    let mut f_best_low  = NINF;
    let mut best_x = x_0.clone();

    let mut iters: u32 = 0;
    q.write().unwrap().push(Quple{p: INF, pf: 0, data: x_0.clone(),
                                  fdata: f.call(&x_0)});

    while q.read().unwrap().len() != 0 && !stop.load(Ordering::Acquire) {
        if sync.load(Ordering::Acquire) {
            // Ugly: Update the update thread's view of the best branch bound.
            *f_best_shared.write().unwrap() = f_best_low;
            b1.wait();
            b2.wait();
        }
        // Take q as writable during an iteration
        let mut q = q.write().unwrap();
        let fbl_orig = f_best_low;
        f_best_low = max!(f_best_low, *f_bestag.read().unwrap());
        if (iters % 1024 == 0) && iters != 0 {
            let guaranteed_bound = get_upper_bound(&q, f_best_high);
            if (guaranteed_bound - f_best_high).abs() < e_f {
                f_best_high = guaranteed_bound;
                break;
            }

        }

        if fbl_orig != f_best_low && logging {
            log_max(&q, f_best_low, f_best_high);
        }

        let (ref x, fx) =
            match q.pop() {
                Some(y) => (y.data, y.fdata),
                None    => unreachable!()
            };

        if fx.upper() < f_best_low ||
            width_box(x, e_x) ||
            eps_tol(fx, e_f) {
                if f_best_high < fx.upper() {
                    f_best_high = fx.upper();
                    best_x = x.clone();
                    if logging {
                        log_max(&q, f_best_low, f_best_high);
                    }
                }
                continue;
            }
        else {
            let x_s = split_box(&x);
            for sx in x_s {
                let mid = midpoint_box(&sx);
                let est_m = f.call(&mid);
                let fsx = f.call(&sx);
                let est_max = if est_m.lower() > fsx.lower() {est_m} else {fsx};
                if f_best_low < est_max.lower()  {
                    f_best_low = est_max.lower();
                    *x_bestbb.write().unwrap() = sx.clone();
                }
                iters += 1;
                q.push(Quple{p: est_max.upper(),
                             pf: iters,
                             data: sx,
                             fdata: fsx});
            }
        }
    }
    if !stop.load(Ordering::Acquire) {
        // Exiting normally
        // Tell GA thread to stop
        stop.store(true, Ordering::Release);
        (f_best_high, best_x, true)
    }
    else {
        // Exiting by some other condition
        (f_best_high, best_x, false)
    }
}

fn distance(p: &Vec<GI>, x: &Vec<GI>) -> Flt {
    let mut result = 0.0;
    for i in 0..x.len() {
        let dx = max!(x[i].lower() - p[i].lower(),
                      0.0, &p[i].lower() - x[i].upper());
        result += dx*dx;
    }
    result.sqrt()
}

fn update(q: Arc<RwLock<BinaryHeap<Quple>>>, population: Arc<RwLock<Vec<Individual>>>,
          f_best_shared: Arc<RwLock<Flt>>,
          stop: Arc<AtomicBool>, sync: Arc<AtomicBool>,
          b1: Arc<Barrier>, b2: Arc<Barrier>,
          f: FuncObj,
          upd_interval: u32,
          timeout: u32) {
    let start = time::get_time();

    'out: while !stop.load(Ordering::Acquire) {
        // Timer code...
        let last_update = time::get_time();
        while (time::get_time() - last_update).num_seconds() <= upd_interval as i64 {
            thread::sleep(Duration::new(1, 0));
            if timeout > 0 &&
                (time::get_time() - start).num_seconds() >= timeout as i64 {
                    let _ = writeln!(&mut std::io::stderr(), "Stopping early...");
                    stop.store(true, Ordering::Release);
                    break 'out;
                }
            if stop.load(Ordering::Acquire) { // Check if we've already stopped
                break 'out;
            }
        }
        // Signal EA and IBBA threads.
        sync.store(true, Ordering::Release);

        // Wait for EA and IBBA threads to stop.
        b1.wait();                                                               // Dead lock here?
        // Do update bizness.
        let mut q_u = q.write().unwrap();
        let mut q = Vec::new();
        for qi in q_u.iter() {
            q.push(qi.clone());
        }
        let mut pop = population.write().unwrap();
        let fbest = f_best_shared.read().unwrap();

        for i in 0..pop.len() {
            let mut d_min = INF;
            let mut j = 0;
            let mut x: Vec<GI>;
            let px: Flt = NINF;
            let mut x_c: Vec<GI> = q[0].data.clone();
            while j < q.len() && d_min != 0.0 {
                x = q[j].data.clone();
                if f.call(&x).upper() < *fbest {
                    q.remove(j);
                    continue;
                }
                let d = distance(&pop[i].solution, &x);

                if d == 0.0 {d_min = 0.0; continue;}
                else {
                    if d < d_min {d_min = d; x_c = x.clone();}
                }
                j += 1;
            }
            if d_min == 0.0 {
                let fp = f.call(&pop[i].solution).lower();
                if px < fp {
                    q[j] = Quple{p: fp, pf: q[j].pf,
                                 data: q[j].data.clone(),
                                 fdata: q[j].fdata }.clone();
                }
            }
            else {
                // Individual is outside the search region.
                // Project it back into the nearest current search space.
                project(&mut pop[i], &x_c, f.clone());
            }
        }
        // Restore the q for the ibba thread.
        (*q_u) = BinaryHeap::from(q);
        // Clear sync flag.
        sync.store(false, Ordering::SeqCst);

        // Resume EA and IBBA threads.
        b2.wait();                                                               // Dead lock here?
    }
}

/* Projects the box x into the box x_c */
fn project(p: &mut Individual, x_c: &Vec<GI>, f: FuncObj) {
    for i in 0..x_c.len() {
        if p.solution[i].lower() < x_c[i].lower()
            || p.solution[i].lower() > x_c[i].upper() {
                if x_c[i].upper() < p.solution[i].lower()
                {p.solution[i] = GI::new_d(x_c[i].upper(),
                                           x_c[i].upper());}
                else {p.solution[i] = GI::new_d(x_c[i].lower(),
                                                x_c[i].lower());}
            }
    }
    p.fitness = f.call(&p.solution).lower();
}

fn main() {
    let args = process_args();

    let ref x_0 = args.domain;
    let ref fo = args.function;
    let x_err = args.x_error;
    let y_err = args.y_error;

    // Early out if there are no input variables...
    if x_0.len() == 0 {
        let result = fo.call(&x_0);
        println!("[{}, {{}}]", result.upper());
        return
    }

    let q_inner: BinaryHeap<Quple> = BinaryHeap::new();
    let q = Arc::new(RwLock::new(q_inner));

    let population_inner: Vec<Individual> = Vec::new();
    let population = Arc::new(RwLock::new(population_inner));

    let b1 = Arc::new(Barrier::new(3));
    let b2 = Arc::new(Barrier::new(3));

    let sync = Arc::new(AtomicBool::new(false));
    let stop = Arc::new(AtomicBool::new(false));

    let f_bestag: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));
    let f_best_shared: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));

    let x_e = x_0.clone();
    let x_i = x_0.clone();

    let x_bestbb = Arc::new(RwLock::new(x_0.clone()));

    let ibba_thread =
    {
        let q = q.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let f_bestag = f_bestag.clone();
        let f_best_shared = f_best_shared.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let fo_c = fo.clone();
        let logging = args.logging;
        thread::Builder::new().name("IBBA".to_string()).spawn(move || {
            ibba(x_i, x_err, y_err,
                 f_bestag, f_best_shared,
                 x_bestbb,
                 b1, b2, q, sync, stop, fo_c, logging)
        })};

    let _ea_thread =
    {
        let population = population.clone();
        let f_bestag = f_bestag.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        thread::Builder::new().name("EA".to_string()).spawn(move || {
            ea(x_e, Parameters{population: 2000, //1000,
                               selection: 8, //4,
                               elitism: 5, //2,
                               mutation: 0.4_f64,//0.3_f64,
                               crossover: 0.3_f64 // 0.5_f64
            },
               population,
               f_bestag,
               x_bestbb,
               b1, b2,
               stop, sync, fo_c)
        })};

    // pending finding out how to kill threads
    //let update_thread =
    {
        let q = q.clone();
        let population = population.clone();
        let f_best_shared = f_best_shared.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        let to = args.timeout.clone();
        let ui = args.update_interval.clone();
        let _ = thread::Builder::new().name("Update".to_string()).spawn(move || {
            update(q, population, f_best_shared,
                   stop, sync, b1, b2, fo_c, ui, to)
        });};

    let result = ibba_thread.unwrap().join();
    /*let ea_result = ea_thread.unwrap().join();
    if !ea_result.is_ok() {
        unreachable!();
    }*/

    // Join EA and Update here pending stop signaling.
    if result.is_ok() {
        let (mut max, mut interval, completed) = result.unwrap();
        if !completed {
            // Go through all remaining intervals from IBBA to find the true
            // max
            let mut lq = q.write().unwrap();
            while lq.len() != 0 {
                let v = lq.pop().unwrap();
                let ref l_data = &v.data;
                let ub = fo.call(l_data).upper();
                if ub > max {
                    max = ub;
                    interval =v.data.clone();
                }
            }
        }
        println!("[{}, {{", max);
        for i in 0..args.names.len() {
            println!("'{}' : {},", args.names[i], interval[i].to_string());
        }
        println!("}}]");

    }
    else {println!("error")}
}
<commit_msg>Removed unecessary check<commit_after>// Cooperative optimization solver
use std::collections::BinaryHeap;
use std::io::Write;
extern crate rand;

#[macro_use(max)]
extern crate gelpia_utils;
extern crate ga;
extern crate gr;

use ga::{ea, Individual};

use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};

use std::sync::atomic::{AtomicBool, Ordering};

use std::thread;

use std::time::Duration;

extern crate function;
use function::FuncObj;

extern crate args;
use args::{process_args};

extern crate time;

/// Returns the guaranteed upperbound for the algorithm
/// from the queue.
fn get_upper_bound(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
                   f_best_high: f64) -> f64{
    let mut max = f_best_high;
    for qi in q.iter() {
        max = max!{max, qi.fdata.upper()};
    }
    max
}

fn log_max(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
           f_best_low: f64,
           f_best_high: f64) {
    let max = get_upper_bound(q, f_best_high);
    let _ = writeln!(&mut std::io::stderr(),
                     "lb: {}, possible ub: {}, guaranteed ub: {}",
                     f_best_low,
                     f_best_high,
                     max);
}

// Returns the upper bound, the domain where this bound occurs and a status
// flag indicating whether the answer is complete for the problem.
fn ibba(x_0: Vec<GI>, e_x: Flt, e_f: Flt,
        f_bestag: Arc<RwLock<Flt>>,
        f_best_shared: Arc<RwLock<Flt>>,
        x_bestbb: Arc<RwLock<Vec<GI>>>,
        b1: Arc<Barrier>, b2: Arc<Barrier>,
        q: Arc<RwLock<BinaryHeap<Quple>>>,
        sync: Arc<AtomicBool>, stop: Arc<AtomicBool>,
        f: FuncObj,
        logging: bool)
        -> (Flt, Vec<GI>, bool) {
    let mut f_best_high = NINF;
    let mut f_best_low  = NINF;
    let mut best_x = x_0.clone();

    let mut iters: u32 = 0;
    q.write().unwrap().push(Quple{p: INF, pf: 0, data: x_0.clone(),
                                  fdata: f.call(&x_0)});

    while q.read().unwrap().len() != 0 && !stop.load(Ordering::Acquire) {
        if sync.load(Ordering::Acquire) {
            // Ugly: Update the update thread's view of the best branch bound.
            *f_best_shared.write().unwrap() = f_best_low;
            b1.wait();
            b2.wait();
        }
        // Take q as writable during an iteration
        let mut q = q.write().unwrap();
        let fbl_orig = f_best_low;
        f_best_low = max!(f_best_low, *f_bestag.read().unwrap());

        if iters % 2048 == 0 {
            let guaranteed_bound = get_upper_bound(&q, f_best_high);
            if (guaranteed_bound - f_best_high).abs() < e_f {
                f_best_high = guaranteed_bound;
                break;
            }

        }

        if fbl_orig != f_best_low && logging {
            log_max(&q, f_best_low, f_best_high);
        }

        let (ref x, fx) =
            match q.pop() {
                Some(y) => (y.data, y.fdata),
                None    => unreachable!()
            };

        if fx.upper() < f_best_low ||
            width_box(x, e_x) ||
            eps_tol(fx, e_f) {
                if f_best_high < fx.upper() {
                    f_best_high = fx.upper();
                    best_x = x.clone();
                    if logging {
                        log_max(&q, f_best_low, f_best_high);
                    }
                }
                continue;
            }
        else {
            let x_s = split_box(&x);
            for sx in x_s {
                let mid = midpoint_box(&sx);
                let est_m = f.call(&mid);
                let fsx = f.call(&sx);
                let est_max = if est_m.lower() > fsx.lower() {est_m} else {fsx};
                if f_best_low < est_max.lower()  {
                    f_best_low = est_max.lower();
                    *x_bestbb.write().unwrap() = sx.clone();
                }
                iters += 1;
                q.push(Quple{p: est_max.upper(),
                             pf: iters,
                             data: sx,
                             fdata: fsx});
            }
        }
    }
    if !stop.load(Ordering::Acquire) {
        // Exiting normally
        // Tell GA thread to stop
        stop.store(true, Ordering::Release);
        (f_best_high, best_x, true)
    }
    else {
        // Exiting by some other condition
        (f_best_high, best_x, false)
    }
}

fn distance(p: &Vec<GI>, x: &Vec<GI>) -> Flt {
    let mut result = 0.0;
    for i in 0..x.len() {
        let dx = max!(x[i].lower() - p[i].lower(),
                      0.0, &p[i].lower() - x[i].upper());
        result += dx*dx;
    }
    result.sqrt()
}

fn update(q: Arc<RwLock<BinaryHeap<Quple>>>, population: Arc<RwLock<Vec<Individual>>>,
          f_best_shared: Arc<RwLock<Flt>>,
          stop: Arc<AtomicBool>, sync: Arc<AtomicBool>,
          b1: Arc<Barrier>, b2: Arc<Barrier>,
          f: FuncObj,
          upd_interval: u32,
          timeout: u32) {
    let start = time::get_time();

    'out: while !stop.load(Ordering::Acquire) {
        // Timer code...
        let last_update = time::get_time();
        while (time::get_time() - last_update).num_seconds() <= upd_interval as i64 {
            thread::sleep(Duration::new(1, 0));
            if timeout > 0 &&
                (time::get_time() - start).num_seconds() >= timeout as i64 {
                    let _ = writeln!(&mut std::io::stderr(), "Stopping early...");
                    stop.store(true, Ordering::Release);
                    break 'out;
                }
            if stop.load(Ordering::Acquire) { // Check if we've already stopped
                break 'out;
            }
        }
        // Signal EA and IBBA threads.
        sync.store(true, Ordering::Release);

        // Wait for EA and IBBA threads to stop.
        b1.wait();                                                               // Dead lock here?
        // Do update bizness.
        let mut q_u = q.write().unwrap();
        let mut q = Vec::new();
        for qi in q_u.iter() {
            q.push(qi.clone());
        }
        let mut pop = population.write().unwrap();
        let fbest = f_best_shared.read().unwrap();

        for i in 0..pop.len() {
            let mut d_min = INF;
            let mut j = 0;
            let mut x: Vec<GI>;
            let px: Flt = NINF;
            let mut x_c: Vec<GI> = q[0].data.clone();
            while j < q.len() && d_min != 0.0 {
                x = q[j].data.clone();
                if f.call(&x).upper() < *fbest {
                    q.remove(j);
                    continue;
                }
                let d = distance(&pop[i].solution, &x);

                if d == 0.0 {d_min = 0.0; continue;}
                else {
                    if d < d_min {d_min = d; x_c = x.clone();}
                }
                j += 1;
            }
            if d_min == 0.0 {
                let fp = f.call(&pop[i].solution).lower();
                if px < fp {
                    q[j] = Quple{p: fp, pf: q[j].pf,
                                 data: q[j].data.clone(),
                                 fdata: q[j].fdata }.clone();
                }
            }
            else {
                // Individual is outside the search region.
                // Project it back into the nearest current search space.
                project(&mut pop[i], &x_c, f.clone());
            }
        }
        // Restore the q for the ibba thread.
        (*q_u) = BinaryHeap::from(q);
        // Clear sync flag.
        sync.store(false, Ordering::SeqCst);

        // Resume EA and IBBA threads.
        b2.wait();                                                               // Dead lock here?
    }
}

/* Projects the box x into the box x_c */
fn project(p: &mut Individual, x_c: &Vec<GI>, f: FuncObj) {
    for i in 0..x_c.len() {
        if p.solution[i].lower() < x_c[i].lower()
            || p.solution[i].lower() > x_c[i].upper() {
                if x_c[i].upper() < p.solution[i].lower()
                {p.solution[i] = GI::new_d(x_c[i].upper(),
                                           x_c[i].upper());}
                else {p.solution[i] = GI::new_d(x_c[i].lower(),
                                                x_c[i].lower());}
            }
    }
    p.fitness = f.call(&p.solution).lower();
}

fn main() {
    let args = process_args();

    let ref x_0 = args.domain;
    let ref fo = args.function;
    let x_err = args.x_error;
    let y_err = args.y_error;

    // Early out if there are no input variables...
    if x_0.len() == 0 {
        let result = fo.call(&x_0);
        println!("[{}, {{}}]", result.upper());
        return
    }

    let q_inner: BinaryHeap<Quple> = BinaryHeap::new();
    let q = Arc::new(RwLock::new(q_inner));

    let population_inner: Vec<Individual> = Vec::new();
    let population = Arc::new(RwLock::new(population_inner));

    let b1 = Arc::new(Barrier::new(3));
    let b2 = Arc::new(Barrier::new(3));

    let sync = Arc::new(AtomicBool::new(false));
    let stop = Arc::new(AtomicBool::new(false));

    let f_bestag: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));
    let f_best_shared: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));

    let x_e = x_0.clone();
    let x_i = x_0.clone();

    let x_bestbb = Arc::new(RwLock::new(x_0.clone()));

    let ibba_thread =
    {
        let q = q.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let f_bestag = f_bestag.clone();
        let f_best_shared = f_best_shared.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let fo_c = fo.clone();
        let logging = args.logging;
        thread::Builder::new().name("IBBA".to_string()).spawn(move || {
            ibba(x_i, x_err, y_err,
                 f_bestag, f_best_shared,
                 x_bestbb,
                 b1, b2, q, sync, stop, fo_c, logging)
        })};

    let _ea_thread =
    {
        let population = population.clone();
        let f_bestag = f_bestag.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        thread::Builder::new().name("EA".to_string()).spawn(move || {
            ea(x_e, Parameters{population: 2000, //1000,
                               selection: 8, //4,
                               elitism: 5, //2,
                               mutation: 0.4_f64,//0.3_f64,
                               crossover: 0.3_f64 // 0.5_f64
            },
               population,
               f_bestag,
               x_bestbb,
               b1, b2,
               stop, sync, fo_c)
        })};

    // pending finding out how to kill threads
    //let update_thread =
    {
        let q = q.clone();
        let population = population.clone();
        let f_best_shared = f_best_shared.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        let to = args.timeout.clone();
        let ui = args.update_interval.clone();
        let _ = thread::Builder::new().name("Update".to_string()).spawn(move || {
            update(q, population, f_best_shared,
                   stop, sync, b1, b2, fo_c, ui, to)
        });};

    let result = ibba_thread.unwrap().join();
    /*let ea_result = ea_thread.unwrap().join();
    if !ea_result.is_ok() {
        unreachable!();
    }*/

    // Join EA and Update here pending stop signaling.
    if result.is_ok() {
        let (mut max, mut interval, completed) = result.unwrap();
        if !completed {
            // Go through all remaining intervals from IBBA to find the true
            // max
            let mut lq = q.write().unwrap();
            while lq.len() != 0 {
                let v = lq.pop().unwrap();
                let ref l_data = &v.data;
                let ub = fo.call(l_data).upper();
                if ub > max {
                    max = ub;
                    interval =v.data.clone();
                }
            }
        }
        println!("[{}, {{", max);
        for i in 0..args.names.len() {
            println!("'{}' : {},", args.names[i], interval[i].to_string());
        }
        println!("}}]");

    }
    else {println!("error")}
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate porteurbars;
extern crate tempdir;

use tempdir::TempDir;
use clap::{ArgMatches, App, Arg};
use porteurbars::{Result, Template};
use porteurbars::git;

fn run(args: ArgMatches) -> Result<()> {
    let repo = args.value_of("repository").unwrap();
    let url = porteurbars::git::Url::from_str(repo)?;
    let target = args.value_of("target").unwrap_or(".");
    let root = args.value_of("template_root");
    info!("Cloning...");
    let tmp = TempDir::new("porteurbars")?;
    git::clone(url, &tmp, "master")?;
    info!("Applying template...");
    Template::new(&tmp).apply(target, root)?;
    println!("off you go");
    Ok(())
}

fn main() {
    env_logger::init().unwrap();
    let args = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("portable git hosted project templates")
        .arg(Arg::with_name("repository").value_name("repository")
        .required(true).help(
            "uri of template to apply.
example uris
github: user/repo
 local: file:///path/to/repo
   git: git@github.com:user/repo.git"
        ))
        .arg(Arg::with_name("target")
            .value_name("target")
            .help("directory to write template output to. defaults to current working directory"))
        .arg(Arg::with_name("base")
                .short("b")
                .long("base")
                .value_name("base_directory")
                .takes_value(true)
                .help("directory within <repository> to use as root. defaults to base of repo"))
        .get_matches();

    if let Err(ref e) = run(args) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";
        writeln!(stderr, "error: {}", e).expect(errmsg);
        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(errmsg);
        }
        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(errmsg);
        }

        ::std::process::exit(1);
    }
}
<commit_msg>cargo fmt<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate porteurbars;
extern crate tempdir;

use tempdir::TempDir;
use clap::{ArgMatches, App, Arg};
use porteurbars::{Result, Template};
use porteurbars::git;

fn run(args: ArgMatches) -> Result<()> {
    let repo = args.value_of("repository").unwrap();
    let url = porteurbars::git::Url::from_str(repo)?;
    let target = args.value_of("target").unwrap_or(".");
    let root = args.value_of("template_root");
    info!("Cloning...");
    let tmp = TempDir::new("porteurbars")?;
    git::clone(url, &tmp, "master")?;
    info!("Applying template...");
    Template::new(&tmp).apply(target, root)?;
    println!("off you go");
    Ok(())
}

fn main() {
    env_logger::init().unwrap();
    let args = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("portable git hosted project templates")
        .arg(
            Arg::with_name("repository")
                .value_name("repository")
                .required(true)
                .help(
                    "uri of template to apply.
example uris
github: user/repo
 local: file:///path/to/repo
   git: git@github.com:user/repo.git",
                ),
        )
        .arg(Arg::with_name("target").value_name("target").help(
            "directory to write template output to. defaults to current working directory",
        ))
        .arg(
            Arg::with_name("base")
                .short("b")
                .long("base")
                .value_name("base_directory")
                .takes_value(true)
                .help(
                    "directory within <repository> to use as root. defaults to base of repo",
                ),
        )
        .get_matches();

    if let Err(ref e) = run(args) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";
        writeln!(stderr, "error: {}", e).expect(errmsg);
        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(errmsg);
        }
        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(errmsg);
        }

        ::std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>use hyper;
use webdriver::command::{GetParameters, LocatorParameters};
use webdriver::common::LocatorStrategy;
use webdriver::error::{WebDriverResult, ErrorStatus, WebDriverError};
use webdriver::response::{WebDriverResponse, ValueResponse};
use std::io::Read;
use std::ops::Drop;

use rustc_serialize::json;
use rustc_serialize::json::{Json, ToJson};
use rustc_serialize::base64::{FromBase64Error, FromBase64};

use std::process::{Command, Child};
use std::env;
use std::net::TcpStream;
use std::time;
use std::thread;

use std::sync::{Arc, Mutex, MutexGuard};
use nickel::{Request, Response, Middleware, Continue, MiddlewareResult};
use plugin::{Pluggable, Extensible};
use typemap::Key;

use rand;
use rand::Rng;

pub struct Webdriver {
    client: hyper::client::Client,
    host: String,
    port: u16,
    process: Child,
}

impl Default for Webdriver {
    fn default() -> Self {
        panic!("No good default for webdriver");
    }
}

pub struct WebdriverSession {
    client: hyper::client::Client,
    base_url: String,
    session_id: String,
}

fn decode_response(json_str: &str) -> WebDriverResult<Json> {
    let decoded = Json::from_str(&json_str).unwrap();
    let status =
        decoded.find("status").expect("status code").as_u64().expect("numeric status code");
    let value = decoded.find("value").expect("value");
    return match status {
        0 => Ok(decoded.clone()),
        _ => {
            let message = value.find("message")
                .expect("error message")
                .as_string()
                .expect("a string message")
                .to_string();
            let kind = match status {
                7 => ErrorStatus::NoSuchElement,
                _ => ErrorStatus::UnknownError,
            };
            return Err(WebDriverError::new(kind, message));
        }
    };
}

trait DoesPost {
    fn do_post(&self, url: String, body: &String) -> WebDriverResult<Json> {
        debug!("Request: {:?}", body);
        let mut res = self.client()
            .post(&url)
            .body(body)
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        debug!("Buffer: {}", buffer);
        // assert_eq!(res.status, hyper::Ok);
        let decoded = decode_response(&buffer);
        debug!("Decoded: {:?}", decoded);
        return decoded;
    }

    fn client(&self) -> &hyper::client::Client;
}

impl Webdriver {
    pub fn new() -> Webdriver {
        let chromedriver_path = match env::var("CHROMEDRIVER") {
            Ok(val) => val,
            Err(_) => "./chromedriver".to_string(),
        };
        info!("Using {} as chromedriver path", chromedriver_path);

        let port: u16 = {
            let mut rng = rand::thread_rng();
            let mut candidate: u16;
            loop {
                candidate = rng.gen_range(2048, 65535);
                debug!("Candidate port {}", candidate);
                let stream = TcpStream::connect(("localhost", candidate));
                if stream.is_ok() {
                    debug!("Already used port {}", candidate);
                } else {
                    debug!("Good port {}", candidate);
                    break;
                }
            }
            candidate
        };

        let child = Command::new(&chromedriver_path)
            .arg(format!("--port={}", port))
            .spawn()
            .expect(&format!("spawning chromedriver from {}", &chromedriver_path));

        loop {
            let stream = TcpStream::connect(("localhost", port));
            if stream.is_ok() {
                debug!("Found chromedriver");
                break;
            } else {
                debug!("Can't connect to chromedriver yet");
                thread::sleep(time::Duration::from_secs(1));
            }
        }

        Webdriver {
            client: hyper::client::Client::new(),
            host: "localhost".to_string(),
            port: port,
            process: child,
        }
    }

    pub fn url(&self, rest: &str) -> String {
        format!("http://{}:{}/session{}", self.host, self.port, rest)
    }

    pub fn make_session(&self) -> WebdriverSession {
        let mut mobile_emulation: json::Object = json::Object::new();
        mobile_emulation.insert("deviceName".to_string(), "Apple iPhone 5".to_json());
        let mut chrome_options: json::Object = json::Object::new();
        chrome_options.insert("mobileEmulation".to_string(), mobile_emulation.to_json());
        let mut desired: json::Object = json::Object::new();
        desired.insert("chromeOptions".to_string(), chrome_options.to_json());
        let mut request: json::Object = json::Object::new();
        request.insert("desiredCapabilities".to_string(), desired.to_json());
        let json_str = (&request).to_json().to_string();
        let decoded = self.do_post(self.url(""), &json_str);
        let session_id = decoded.expect("ok response")
            .find("sessionId")
            .expect("sessionId")
            .as_string()
            .expect("string session id")
            .to_string();
        WebdriverSession {
            client: hyper::client::Client::new(),
            base_url: self.url(""),
            session_id: session_id,
        }
    }
}

impl Drop for Webdriver {
    fn drop(&mut self) {
        self.process.kill().unwrap();
        debug!("Killed chromedriver process");
    }
}

pub struct WebdriverMiddleware {
    pub mutex: Arc<Mutex<Webdriver>>,
}

impl WebdriverMiddleware {
    pub fn new(client: Webdriver) -> WebdriverMiddleware {
        WebdriverMiddleware { mutex: Arc::new(Mutex::new(client)) }
    }
}

impl<D> Middleware<D> for WebdriverMiddleware {
    fn invoke<'mw, 'conn>(&self,
                          req: &mut Request<'mw, 'conn, D>,
                          res: Response<'mw, D>)
                          -> MiddlewareResult<'mw, D> {
        req.extensions_mut().insert::<WebdriverMiddleware>(self.mutex.clone());
        Ok(Continue(res))
    }
}

impl Key for WebdriverMiddleware {
    type Value = Arc<Mutex<Webdriver>>;
}

pub trait WebdriverRequestExtensions {
    fn webdriver(&self) -> MutexGuard<Webdriver>;
}

impl<'a, 'b, D> WebdriverRequestExtensions for Request<'a, 'b, D> {
    fn webdriver(&self) -> MutexGuard<Webdriver> {
        self.extensions().get::<WebdriverMiddleware>().unwrap().lock().unwrap()
    }
}

impl DoesPost for Webdriver {
    fn client(&self) -> &hyper::client::Client {
        &self.client
    }
}
impl DoesPost for WebdriverSession {
    fn client(&self) -> &hyper::client::Client {
        &self.client
    }
}

impl Drop for WebdriverSession {
    fn drop(&mut self) {
        self.client()
            .delete(&self.url(format!("/{}", self.session_id)))
            .send()
            .unwrap();
    }
}

impl WebdriverSession {
    fn url(&self, rest: String) -> String {
        format!("{}{}", self.base_url, rest)
    }

    pub fn goto_url(&self, url: String) {
        let params = GetParameters { url: url };
        self.client()
            .post(&self.url(format!("/{}/url", self.session_id)))
            .body(&params.to_json().to_string())
            .send()
            .unwrap();
    }

    pub fn find_element_by_xpath(&self, xpath: String) -> WebDriverResult<WebDriverResponse> {
        let params = LocatorParameters {
            value: xpath.clone(),
            using: LocatorStrategy::XPath,
        };
        let decoded = self.do_post(self.url(format!("/{}/element", self.session_id)),
                                   &params.to_json().to_string());
        return match decoded {
            Err(val) => Err(val),
            Ok(val) => {
                Ok(WebDriverResponse::Generic(ValueResponse::new(val.find("value")
                    .expect("has value")
                    .clone())))
            }
        };
    }

    fn get_for_element(&self, element: &ValueResponse, kind: &str) -> WebDriverResult<Json> {
        let element_id =
            element.value.find("ELEMENT").expect("ELEMENT").as_string().expect("String ELEMENT");
        let mut res = self.client()
            .get(&self.url(format!("/{}/element/{}/{}", self.session_id, element_id, kind)))
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        debug!("Buffer: {}", buffer);
        // assert_eq!(res.status, hyper::Ok);
        let decoded = decode_response(&buffer);
        debug!("Decoded: {:?}", decoded);
        return decoded;
    }

    pub fn get_element_location(&self, element: &ValueResponse) -> WebDriverResult<Json> {
        return self.get_for_element(element, "location");
    }

    pub fn get_element_size(&self, element: &ValueResponse) -> WebDriverResult<Json> {
        return self.get_for_element(element, "size");
    }

    pub fn get_screenshot_as_png(&self) -> Result<Vec<u8>, FromBase64Error> {
        let mut res = self.client()
            .get(&self.url(format!("/{}/screenshot", self.session_id)))
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        let decoded = decode_response(&buffer).expect("decoded");
        let value = decoded.find("value").expect("value").as_string().expect("string value");
        return value.clone().from_base64();
    }
}
<commit_msg>Run chrome without sandbox so it works in Docker<commit_after>use hyper;
use webdriver::command::{GetParameters, LocatorParameters};
use webdriver::common::LocatorStrategy;
use webdriver::error::{WebDriverResult, ErrorStatus, WebDriverError};
use webdriver::response::{WebDriverResponse, ValueResponse};
use std::io::Read;
use std::ops::Drop;

use rustc_serialize::json;
use rustc_serialize::json::{Json, ToJson};
use rustc_serialize::base64::{FromBase64Error, FromBase64};

use std::process::{Command, Child};
use std::env;
use std::net::TcpStream;
use std::time;
use std::thread;

use std::sync::{Arc, Mutex, MutexGuard};
use nickel::{Request, Response, Middleware, Continue, MiddlewareResult};
use plugin::{Pluggable, Extensible};
use typemap::Key;

use rand;
use rand::Rng;

pub struct Webdriver {
    client: hyper::client::Client,
    host: String,
    port: u16,
    process: Child,
}

impl Default for Webdriver {
    fn default() -> Self {
        panic!("No good default for webdriver");
    }
}

pub struct WebdriverSession {
    client: hyper::client::Client,
    base_url: String,
    session_id: String,
}

fn decode_response(json_str: &str) -> WebDriverResult<Json> {
    let decoded = Json::from_str(&json_str).unwrap();
    let status =
        decoded.find("status").expect("status code").as_u64().expect("numeric status code");
    let value = decoded.find("value").expect("value");
    return match status {
        0 => Ok(decoded.clone()),
        _ => {
            let message = value.find("message")
                .expect("error message")
                .as_string()
                .expect("a string message")
                .to_string();
            let kind = match status {
                7 => ErrorStatus::NoSuchElement,
                _ => ErrorStatus::UnknownError,
            };
            return Err(WebDriverError::new(kind, message));
        }
    };
}

trait DoesPost {
    fn do_post(&self, url: String, body: &String) -> WebDriverResult<Json> {
        debug!("Request: {:?}", body);
        let mut res = self.client()
            .post(&url)
            .body(body)
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        debug!("Buffer: {}", buffer);
        // assert_eq!(res.status, hyper::Ok);
        let decoded = decode_response(&buffer);
        debug!("Decoded: {:?}", decoded);
        return decoded;
    }

    fn client(&self) -> &hyper::client::Client;
}

impl Webdriver {
    pub fn new() -> Webdriver {
        let chromedriver_path = match env::var("CHROMEDRIVER") {
            Ok(val) => val,
            Err(_) => "./chromedriver".to_string(),
        };
        info!("Using {} as chromedriver path", chromedriver_path);

        let port: u16 = {
            let mut rng = rand::thread_rng();
            let mut candidate: u16;
            loop {
                candidate = rng.gen_range(2048, 65535);
                debug!("Candidate port {}", candidate);
                let stream = TcpStream::connect(("localhost", candidate));
                if stream.is_ok() {
                    debug!("Already used port {}", candidate);
                } else {
                    debug!("Good port {}", candidate);
                    break;
                }
            }
            candidate
        };

        let child = Command::new(&chromedriver_path)
            .arg(format!("--port={}", port))
            .spawn()
            .expect(&format!("spawning chromedriver from {}", &chromedriver_path));

        loop {
            let stream = TcpStream::connect(("localhost", port));
            if stream.is_ok() {
                debug!("Found chromedriver");
                break;
            } else {
                debug!("Can't connect to chromedriver yet");
                thread::sleep(time::Duration::from_secs(1));
            }
        }

        Webdriver {
            client: hyper::client::Client::new(),
            host: "localhost".to_string(),
            port: port,
            process: child,
        }
    }

    pub fn url(&self, rest: &str) -> String {
        format!("http://{}:{}/session{}", self.host, self.port, rest)
    }

    pub fn make_session(&self) -> WebdriverSession {
        let mut mobile_emulation: json::Object = json::Object::new();
        mobile_emulation.insert("deviceName".to_string(), "Apple iPhone 5".to_json());
        let mut chrome_options: json::Object = json::Object::new();
        chrome_options.insert("mobileEmulation".to_string(), mobile_emulation.to_json());
        chrome_options.insert("args".to_string(), ["--no-sandbox".to_string()].to_json());
        let mut desired: json::Object = json::Object::new();
        desired.insert("chromeOptions".to_string(), chrome_options.to_json());
        let mut request: json::Object = json::Object::new();
        request.insert("desiredCapabilities".to_string(), desired.to_json());
        let json_str = (&request).to_json().to_string();
        let decoded = self.do_post(self.url(""), &json_str);
        let session_id = decoded.expect("ok response")
            .find("sessionId")
            .expect("sessionId")
            .as_string()
            .expect("string session id")
            .to_string();
        WebdriverSession {
            client: hyper::client::Client::new(),
            base_url: self.url(""),
            session_id: session_id,
        }
    }
}

impl Drop for Webdriver {
    fn drop(&mut self) {
        self.process.kill().unwrap();
        debug!("Killed chromedriver process");
    }
}

pub struct WebdriverMiddleware {
    pub mutex: Arc<Mutex<Webdriver>>,
}

impl WebdriverMiddleware {
    pub fn new(client: Webdriver) -> WebdriverMiddleware {
        WebdriverMiddleware { mutex: Arc::new(Mutex::new(client)) }
    }
}

impl<D> Middleware<D> for WebdriverMiddleware {
    fn invoke<'mw, 'conn>(&self,
                          req: &mut Request<'mw, 'conn, D>,
                          res: Response<'mw, D>)
                          -> MiddlewareResult<'mw, D> {
        req.extensions_mut().insert::<WebdriverMiddleware>(self.mutex.clone());
        Ok(Continue(res))
    }
}

impl Key for WebdriverMiddleware {
    type Value = Arc<Mutex<Webdriver>>;
}

pub trait WebdriverRequestExtensions {
    fn webdriver(&self) -> MutexGuard<Webdriver>;
}

impl<'a, 'b, D> WebdriverRequestExtensions for Request<'a, 'b, D> {
    fn webdriver(&self) -> MutexGuard<Webdriver> {
        self.extensions().get::<WebdriverMiddleware>().unwrap().lock().unwrap()
    }
}

impl DoesPost for Webdriver {
    fn client(&self) -> &hyper::client::Client {
        &self.client
    }
}
impl DoesPost for WebdriverSession {
    fn client(&self) -> &hyper::client::Client {
        &self.client
    }
}

impl Drop for WebdriverSession {
    fn drop(&mut self) {
        self.client()
            .delete(&self.url(format!("/{}", self.session_id)))
            .send()
            .unwrap();
    }
}

impl WebdriverSession {
    fn url(&self, rest: String) -> String {
        format!("{}{}", self.base_url, rest)
    }

    pub fn goto_url(&self, url: String) {
        let params = GetParameters { url: url };
        self.client()
            .post(&self.url(format!("/{}/url", self.session_id)))
            .body(&params.to_json().to_string())
            .send()
            .unwrap();
    }

    pub fn find_element_by_xpath(&self, xpath: String) -> WebDriverResult<WebDriverResponse> {
        let params = LocatorParameters {
            value: xpath.clone(),
            using: LocatorStrategy::XPath,
        };
        let decoded = self.do_post(self.url(format!("/{}/element", self.session_id)),
                                   &params.to_json().to_string());
        return match decoded {
            Err(val) => Err(val),
            Ok(val) => {
                Ok(WebDriverResponse::Generic(ValueResponse::new(val.find("value")
                    .expect("has value")
                    .clone())))
            }
        };
    }

    fn get_for_element(&self, element: &ValueResponse, kind: &str) -> WebDriverResult<Json> {
        let element_id =
            element.value.find("ELEMENT").expect("ELEMENT").as_string().expect("String ELEMENT");
        let mut res = self.client()
            .get(&self.url(format!("/{}/element/{}/{}", self.session_id, element_id, kind)))
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        debug!("Buffer: {}", buffer);
        // assert_eq!(res.status, hyper::Ok);
        let decoded = decode_response(&buffer);
        debug!("Decoded: {:?}", decoded);
        return decoded;
    }

    pub fn get_element_location(&self, element: &ValueResponse) -> WebDriverResult<Json> {
        return self.get_for_element(element, "location");
    }

    pub fn get_element_size(&self, element: &ValueResponse) -> WebDriverResult<Json> {
        return self.get_for_element(element, "size");
    }

    pub fn get_screenshot_as_png(&self) -> Result<Vec<u8>, FromBase64Error> {
        let mut res = self.client()
            .get(&self.url(format!("/{}/screenshot", self.session_id)))
            .send()
            .unwrap();
        let mut buffer = String::new();
        res.read_to_string(&mut buffer).unwrap();
        let decoded = decode_response(&buffer).expect("decoded");
        let value = decoded.find("value").expect("value").as_string().expect("string value");
        return value.clone().from_base64();
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::str::FromStr;
use toml::Value;

use library;
use library::{Library, TypeId, MAIN_NAMESPACE};
use config::error::TomlHelper;
use config::parsable::{Parsable, Parse};
use super::child_properties::ChildProperties;
use super::functions::Functions;
use super::constants::Constants;
use super::members::Members;
use super::properties::Properties;
use super::signals::{Signal, Signals};
use version::Version;
use analysis::{ref_mode, conversion_type};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual, // already generated
    Generate,
    Comment,
    Ignore,
}

impl GStatus {
    pub fn ignored(&self) -> bool {
        self == &GStatus::Ignore
    }
    pub fn need_generate(&self) -> bool {
        self == &GStatus::Generate
    }
    pub fn normal(&self) -> bool {
        self == &GStatus::Generate || self == &GStatus::Manual
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "comment" => Ok(GStatus::Comment),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub generate_trait: bool,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            generate_trait: true,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(toml_objects: &Value, concurrency: library::Concurrency) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(toml_object: &Value, concurrency: library::Concurrency) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(|val| val.as_array()) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let module_name = toml_object
        .lookup("module_name")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let version = toml_object
        .lookup("version")
        .and_then(|v| v.as_str())
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let generate_trait = toml_object
        .lookup("trait")
        .and_then(|v| v.as_bool())
        .unwrap_or(true);
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(|v| v.as_str())
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(|v| v.as_str())
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(|v| v.as_str())
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!("conversion_type configuration used for non-manual object {}", name);
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        generate_trait,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Comment, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    toml.lookup(&name).map(|a| a.as_array().unwrap()).map(
        |a| for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        },
    );
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<commit_msg>Fix clippy warning option_map_unit_fn.<commit_after>use std::collections::BTreeMap;
use std::str::FromStr;
use toml::Value;

use library;
use library::{Library, TypeId, MAIN_NAMESPACE};
use config::error::TomlHelper;
use config::parsable::{Parsable, Parse};
use super::child_properties::ChildProperties;
use super::functions::Functions;
use super::constants::Constants;
use super::members::Members;
use super::properties::Properties;
use super::signals::{Signal, Signals};
use version::Version;
use analysis::{ref_mode, conversion_type};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual, // already generated
    Generate,
    Comment,
    Ignore,
}

impl GStatus {
    pub fn ignored(&self) -> bool {
        self == &GStatus::Ignore
    }
    pub fn need_generate(&self) -> bool {
        self == &GStatus::Generate
    }
    pub fn normal(&self) -> bool {
        self == &GStatus::Generate || self == &GStatus::Manual
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "comment" => Ok(GStatus::Comment),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub generate_trait: bool,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            generate_trait: true,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(toml_objects: &Value, concurrency: library::Concurrency) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(toml_object: &Value, concurrency: library::Concurrency) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(|val| val.as_array()) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let module_name = toml_object
        .lookup("module_name")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let version = toml_object
        .lookup("version")
        .and_then(|v| v.as_str())
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let generate_trait = toml_object
        .lookup("trait")
        .and_then(|v| v.as_bool())
        .unwrap_or(true);
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(|v| v.as_str())
        .map(|s| s.to_owned());
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(|v| v.as_str())
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(|v| v.as_str())
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(|v| v.as_str())
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(|v| v.as_bool())
        .unwrap_or(false);

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!("conversion_type configuration used for non-manual object {}", name);
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        generate_trait,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Comment, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    if let Some(a) = toml.lookup(&name).map(|a| a.as_array().unwrap()) {
        for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        }
    }
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<|endoftext|>