<commit_before>use dbus::Connection as DBusConnection;
use dbus::{BusType, Path, ConnPath, Message};
use dbus::arg::{Variant, Iter, Array, Get, RefArg};
use dbus::stdintf::OrgFreedesktopDBusProperties;
use dbus::Error;

pub const DEFAULT_TIMEOUT: u64 = 10;
pub const RETRIES_ALLOWED: usize = 10;

pub struct DBusApi {
    connection: DBusConnection,
    method_timeout: u64,
    base: &'static str,
    method_retry_error_names: Vec<&'static str>,
}

impl DBusApi {
    pub fn new(base: &'static str, method_retry_error_names: Vec<&'static str>) -> Self {
        let connection = DBusConnection::get_private(BusType::System).unwrap();

        DBusApi {
            connection: connection,
            method_timeout: DEFAULT_TIMEOUT,
            base: base,
            method_retry_error_names: method_retry_error_names,
        }
    }

    pub fn method_timeout(&self) -> u64 {
        self.method_timeout
    }

    pub fn set_method_timeout(&mut self, timeout: u64) {
        self.method_timeout = timeout;
    }

    pub fn call(&self, path: &str, interface: &str, method: &str) -> Result<Message, String> {
        self.call_with_args(path, interface, method, &[])
    }

    pub fn call_with_args(&self,
                          path: &str,
                          interface: &str,
                          method: &str,
                          args: &[&RefArg])
                          -> Result<Message, String> {
        let call_error = |details: &str| {
            Err(format!("D-Bus '{}'::'{}' method call failed on '{}': {}",
                        interface,
                        method,
                        path,
                        details))
        };

        match self.call_with_args_retry(path, interface, method, args) {
            Ok(response) => Ok(response),
            Err(error) => call_error(&error),
        }
    }

    fn call_with_args_retry(&self,
                            path: &str,
                            interface: &str,
                            method: &str,
                            args: &[&RefArg])
                            -> Result<Message, String> {
        let mut retries = 0;

        loop {
            if let Ok(result) = self.create_and_send_message(path, interface, method, args) {
                return result;
            }

            retries += 1;

            if retries == RETRIES_ALLOWED {
                return Err(format!("method failed after {} retries", RETRIES_ALLOWED));
            }

            ::std::thread::sleep(::std::time::Duration::from_secs(1));
        }
    }

    fn create_and_send_message(&self,
                               path: &str,
                               interface: &str,
                               method: &str,
                               args: &[&RefArg])
                               -> Result<Result<Message, String>, String> {
        match Message::new_method_call(self.base, path, interface, method) {
            Ok(mut message) => {
                if args.len() > 0 {
                    message = message.append_ref(args);
                }

                self.send_message_checked(message)
            }
            Err(details) => Ok(Err(details)),
        }
    }

    fn send_message_checked(&self, message: Message) -> Result<Result<Message, String>, String> {
        match self.connection
                  .send_with_reply_and_block(message, self.method_timeout as i32 * 1000) {
            Ok(response) => Ok(Ok(response)),
            Err(err) => {
                let message = get_error_message(&err).to_string();

                let name = err.name();
                for error_name in &self.method_retry_error_names {
                    if name == Some(error_name) {
                        return Err(message);
                    }
                }

                Ok(Err(message))
            }
        }
    }

    pub fn property<T>(&self, path: &str, interface: &str, name: &str) -> Result<T, String>
        where DBusApi: VariantTo<T>
    {
        let property_error = |details: &str| {
            Err(format!("D-Bus get '{}'::'{}' property failed on '{}': {}",
                        interface,
                        name,
                        path,
                        details))
        };

        let path = self.with_path(path);

        match path.get(interface, name) {
            Ok(variant) => {
                match DBusApi::variant_to(variant) {
                    Some(data) => Ok(data),
                    None => property_error("wrong property type"),
                }
            }
            Err(err) => {
                match err.message() {
                    Some(details) => property_error(details),
                    None => property_error("no details"),
                }
            }
        }
    }

    pub fn extract<'a, T>(&self, response: &'a Message) -> Result<T, String>
        where T: Get<'a>
    {
        response
            .get1()
            .ok_or("D-Bus wrong response type".to_string())
    }

    pub fn extract_two<'a, T1, T2>(&self, response: &'a Message) -> Result<(T1, T2), String>
        where T1: Get<'a>,
              T2: Get<'a>
    {
        let (first, second) = response.get2();

        if let Some(first) = first {
            if let Some(second) = second {
                return Ok((first, second));
            }
        }

        Err("D-Bus wrong response type".to_string())
    }

    fn with_path<'a, P: Into<Path<'a>>>(&'a self, path: P) -> ConnPath<'a, &'a DBusConnection> {
        self.connection
            .with_path(self.base, path, self.method_timeout as i32 * 1000)
    }
}

pub trait VariantTo<T> {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<T>;
}

impl VariantTo<String> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<String> {
        variant_to_string(value)
    }
}

impl VariantTo<i64> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<i64> {
        variant_to_i64(value)
    }
}

impl VariantTo<u32> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<u32> {
        variant_to_u32(value)
    }
}

impl VariantTo<bool> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<bool> {
        variant_to_bool(value)
    }
}

impl VariantTo<Vec<String>> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<Vec<String>> {
        variant_to_string_vec(value)
    }
}

impl VariantTo<Vec<u8>> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<Vec<u8>> {
        variant_to_u8_vec(value)
    }
}

fn variant_to_string_vec(value: Variant<Box<RefArg>>) -> Option<Vec<String>> {
    let mut result = Vec::new();

    if let Some(list) = value.0.as_iter() {
        for element in list {
            if let Some(string) = element.as_str() {
                result.push(string.to_string());
            } else {
                return None;
            }
        }

        Some(result)
    } else {
        None
    }
}

fn variant_to_u8_vec(value: Variant<Box<RefArg>>) -> Option<Vec<u8>> {
    let mut result = Vec::new();

    if let Some(list) = value.0.as_iter() {
        for element in list {
            if let Some(value) = element.as_i64() {
                result.push(value as u8);
            } else {
                return None;
            }
        }

        Some(result)
    } else {
        None
    }
}

fn variant_to_string(value: Variant<Box<RefArg>>) -> Option<String> {
    value.0.as_str().and_then(|v| Some(v.to_string()))
}

fn variant_to_i64(value: Variant<Box<RefArg>>) -> Option<i64> {
    value.0.as_i64()
}

fn variant_to_u32(value: Variant<Box<RefArg>>) -> Option<u32> {
    value.0.as_i64().and_then(|v| Some(v as u32))
}

fn variant_to_bool(value: Variant<Box<RefArg>>) -> Option<bool> {
    value.0.as_i64().and_then(|v| Some(v == 0))
}

pub fn extract<'a, T>(var: &'a Variant<Iter>) -> Result<T, String>
    where T: Get<'a>
{
    var.0
        .clone()
        .get::<T>()
        .ok_or(format!("D-Bus variant type does not match: {:?}", var))
}

pub fn utf8_vec_u8_to_string(var: Vec<u8>) -> Result<String, String> {
    String::from_utf8(var).or(Err(format!("D-Bus variant not a UTF-8 string")))
}

pub fn utf8_variant_to_string(var: &Variant<Iter>) -> Result<String, String> {
    let array_option = &var.0.clone().get::<Array<u8, _>>();

    if let Some(array) = *array_option {
        utf8_vec_u8_to_string(array.collect())
    } else {
        Err(format!("D-Bus variant not an array: {:?}", var))
    }
}

pub fn string_to_utf8_vec_u8(var: &String) -> Vec<u8> {
    var.as_bytes().to_vec()
}

pub fn path_to_string(path: &Path) -> Result<String, String> {
    if let Ok(slice) = path.as_cstr().to_str() {
        Ok(slice.to_string())
    } else {
        Err(format!("Path not a UTF-8 string: {:?}", path))
    }
}

fn get_error_message(err: &Error) -> &str {
    match err.message() {
        Some(details) => details,
        None => "Undefined error message",
    }
}
<commit_msg>Cleaning up the DBusApi::call_with_args method<commit_after>use dbus::Connection as DBusConnection;
use dbus::{BusType, Path, ConnPath, Message};
use dbus::arg::{Variant, Iter, Array, Get, RefArg};
use dbus::stdintf::OrgFreedesktopDBusProperties;
use dbus::Error;

pub const DEFAULT_TIMEOUT: u64 = 10;
pub const RETRIES_ALLOWED: usize = 10;

pub struct DBusApi {
    connection: DBusConnection,
    method_timeout: u64,
    base: &'static str,
    method_retry_error_names: Vec<&'static str>,
}

impl DBusApi {
    pub fn new(base: &'static str, method_retry_error_names: Vec<&'static str>) -> Self {
        let connection = DBusConnection::get_private(BusType::System).unwrap();

        DBusApi {
            connection: connection,
            method_timeout: DEFAULT_TIMEOUT,
            base: base,
            method_retry_error_names: method_retry_error_names,
        }
    }

    pub fn method_timeout(&self) -> u64 {
        self.method_timeout
    }

    pub fn set_method_timeout(&mut self, timeout: u64) {
        self.method_timeout = timeout;
    }

    pub fn call(&self, path: &str, interface: &str, method: &str) -> Result<Message, String> {
        self.call_with_args(path, interface, method, &[])
    }

    pub fn call_with_args(&self,
                          path: &str,
                          interface: &str,
                          method: &str,
                          args: &[&RefArg])
                          -> Result<Message, String> {
        self.call_with_args_retry(path, interface, method, args)
            .map_err(|error| {
                         format!("D-Bus '{}'::'{}' method call failed on '{}': {}",
                                 interface,
                                 method,
                                 path,
                                 error)
                     })
    }

    fn call_with_args_retry(&self,
                            path: &str,
                            interface: &str,
                            method: &str,
                            args: &[&RefArg])
                            -> Result<Message, String> {
        let mut retries = 0;

        loop {
            if let Ok(result) = self.create_and_send_message(path, interface, method, args) {
                return result;
            }

            retries += 1;

            if retries == RETRIES_ALLOWED {
                return Err(format!("method failed after {} retries", RETRIES_ALLOWED));
            }

            ::std::thread::sleep(::std::time::Duration::from_secs(1));
        }
    }

    fn create_and_send_message(&self,
                               path: &str,
                               interface: &str,
                               method: &str,
                               args: &[&RefArg])
                               -> Result<Result<Message, String>, String> {
        match Message::new_method_call(self.base, path, interface, method) {
            Ok(mut message) => {
                if args.len() > 0 {
                    message = message.append_ref(args);
                }

                self.send_message_checked(message)
            }
            Err(details) => Ok(Err(details)),
        }
    }

    fn send_message_checked(&self, message: Message) -> Result<Result<Message, String>, String> {
        match self.connection
                  .send_with_reply_and_block(message, self.method_timeout as i32 * 1000) {
            Ok(response) => Ok(Ok(response)),
            Err(err) => {
                let message = get_error_message(&err).to_string();

                let name = err.name();
                for error_name in &self.method_retry_error_names {
                    if name == Some(error_name) {
                        return Err(message);
                    }
                }

                Ok(Err(message))
            }
        }
    }

    pub fn property<T>(&self, path: &str, interface: &str, name: &str) -> Result<T, String>
        where DBusApi: VariantTo<T>
    {
        let property_error = |details: &str| {
            Err(format!("D-Bus get '{}'::'{}' property failed on '{}': {}",
                        interface,
                        name,
                        path,
                        details))
        };

        let path = self.with_path(path);

        match path.get(interface, name) {
            Ok(variant) => {
                match DBusApi::variant_to(variant) {
                    Some(data) => Ok(data),
                    None => property_error("wrong property type"),
                }
            }
            Err(err) => {
                match err.message() {
                    Some(details) => property_error(details),
                    None => property_error("no details"),
                }
            }
        }
    }

    pub fn extract<'a, T>(&self, response: &'a Message) -> Result<T, String>
        where T: Get<'a>
    {
        response
            .get1()
            .ok_or("D-Bus wrong response type".to_string())
    }

    pub fn extract_two<'a, T1, T2>(&self, response: &'a Message) -> Result<(T1, T2), String>
        where T1: Get<'a>,
              T2: Get<'a>
    {
        let (first, second) = response.get2();

        if let Some(first) = first {
            if let Some(second) = second {
                return Ok((first, second));
            }
        }

        Err("D-Bus wrong response type".to_string())
    }

    fn with_path<'a, P: Into<Path<'a>>>(&'a self, path: P) -> ConnPath<'a, &'a DBusConnection> {
        self.connection
            .with_path(self.base, path, self.method_timeout as i32 * 1000)
    }
}

pub trait VariantTo<T> {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<T>;
}

impl VariantTo<String> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<String> {
        variant_to_string(value)
    }
}

impl VariantTo<i64> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<i64> {
        variant_to_i64(value)
    }
}

impl VariantTo<u32> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<u32> {
        variant_to_u32(value)
    }
}

impl VariantTo<bool> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<bool> {
        variant_to_bool(value)
    }
}

impl VariantTo<Vec<String>> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<Vec<String>> {
        variant_to_string_vec(value)
    }
}

impl VariantTo<Vec<u8>> for DBusApi {
    fn variant_to(value: Variant<Box<RefArg>>) -> Option<Vec<u8>> {
        variant_to_u8_vec(value)
    }
}

fn variant_to_string_vec(value: Variant<Box<RefArg>>) -> Option<Vec<String>> {
    let mut result = Vec::new();

    if let Some(list) = value.0.as_iter() {
        for element in list {
            if let Some(string) = element.as_str() {
                result.push(string.to_string());
            } else {
                return None;
            }
        }

        Some(result)
    } else {
        None
    }
}

fn variant_to_u8_vec(value: Variant<Box<RefArg>>) -> Option<Vec<u8>> {
    let mut result = Vec::new();

    if let Some(list) = value.0.as_iter() {
        for element in list {
            if let Some(value) = element.as_i64() {
                result.push(value as u8);
            } else {
                return None;
            }
        }

        Some(result)
    } else {
        None
    }
}

fn variant_to_string(value: Variant<Box<RefArg>>) -> Option<String> {
    value.0.as_str().and_then(|v| Some(v.to_string()))
}

fn variant_to_i64(value: Variant<Box<RefArg>>) -> Option<i64> {
    value.0.as_i64()
}

fn variant_to_u32(value: Variant<Box<RefArg>>) -> Option<u32> {
    value.0.as_i64().and_then(|v| Some(v as u32))
}

fn variant_to_bool(value: Variant<Box<RefArg>>) -> Option<bool> {
    value.0.as_i64().and_then(|v| Some(v == 0))
}

pub fn extract<'a, T>(var: &'a Variant<Iter>) -> Result<T, String>
    where T: Get<'a>
{
    var.0
        .clone()
        .get::<T>()
        .ok_or(format!("D-Bus variant type does not match: {:?}", var))
}

pub fn utf8_vec_u8_to_string(var: Vec<u8>) -> Result<String, String> {
    String::from_utf8(var).or(Err(format!("D-Bus variant not a UTF-8 string")))
}

pub fn utf8_variant_to_string(var: &Variant<Iter>) -> Result<String, String> {
    let array_option = &var.0.clone().get::<Array<u8, _>>();

    if let Some(array) = *array_option {
        utf8_vec_u8_to_string(array.collect())
    } else {
        Err(format!("D-Bus variant not an array: {:?}", var))
    }
}

pub fn string_to_utf8_vec_u8(var: &String) -> Vec<u8> {
    var.as_bytes().to_vec()
}

pub fn path_to_string(path: &Path) -> Result<String, String> {
    if let Ok(slice) = path.as_cstr().to_str() {
        Ok(slice.to_string())
    } else {
        Err(format!("Path not a UTF-8 string: {:?}", path))
    }
}

fn get_error_message(err: &Error) -> &str {
    match err.message() {
        Some(details) => details,
        None => "Undefined error message",
    }
}
<|endoftext|>


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

use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::PathBuf;

use dbus::{Connection, NameFlag};
use dbus::tree::Factory;
use dbus::tree::Tree;
use dbus::tree::{MethodFn, MethodErr};
use dbus::MessageItem;

use froyo::Froyo;
use types::FroyoResult;

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let froyos = RefCell::borrow(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::create(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&format!("dude"))),
        };

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let p = iface.add_p_ref(f.property("Name", RefCell::borrow(&*froyo).name.to_owned()));
            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    RefCell::borrow_mut(&*froyo_closed_over).name = name.clone();
                    try!(p.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let path = format!("/org/freedesktop/froyo/{}", RefCell::borrow(&*froyo).id);
            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    println!("tree {:#?}", tree);
    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}

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

use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::PathBuf;

use dbus::{Connection, NameFlag};
use dbus::tree::Factory;
use dbus::tree::Tree;
use dbus::tree::{MethodFn, MethodErr};
use dbus::MessageItem;

use froyo::Froyo;
use types::FroyoResult;

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let froyos = RefCell::borrow(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::create(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let p = iface.add_p_ref(f.property("Name", RefCell::borrow(&*froyo).name.to_owned()));
            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

                    try!(p.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let path = format!("/org/freedesktop/froyo/{}", RefCell::borrow(&*froyo).id);
            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    println!("tree {:#?}", tree);
    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}

<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::commandinterpreter::SBCommandInterpreter;
use super::error::SBError;
use super::listener::SBListener;
use super::platform::SBPlatform;
use super::stream::SBStream;
use super::structureddata::SBStructuredData;
use super::target::SBTarget;
use std::ffi::{CStr, CString};
use std::fmt;
use std::iter;
use std::ptr;
use sys;

/// Creates [`SBTarget`]s, provides access to them and manages
/// the overall debugging experience.
///
/// # Initialization and Teardown
///
/// LLDB must be initialized before the functionality is used. This
/// is done with `SBDebugger::initialize()`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// ```
///
/// Similarly, it must be terminated after you are done using it:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// // Use LLDB functionality ...
/// SBDebugger::terminate();
/// ```
///
/// Once you've initialized LLDB, you're ready to create an instance
/// of `SBDebugger`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
///
/// let debugger = SBDebugger::create(false);
/// // ... configure the debugger if needed ...
/// // ... create a target and do stuff ...
///
/// SBDebugger::terminate();
/// ```
///
/// # Configuration
///
/// ## Async Mode
///
/// While it is best to use LLDB in asynchronous mode, it does offer a
/// synchronous mode, which can be easier to use for quick experiments
/// or scripts.
///
/// In synchronous mode, calls to the LLDB API do not return until the
/// underlying action has been completed. This means that the thread
/// from which you call LLDB will be blocked during that time, so this
/// is not an ideal way to use LLDB for building interactive tools
/// or a new user interface.
///
/// In asynchronous mode, calls to the LLDB API will return immediately
/// without waiting for the action to complete. This means that actions
/// like launching a target, continuing the execution of a process and
/// so on won't be completed immediately and you must process events
/// to see what the results of an action are.
///
/// Synchronous mode can be enabled by using [`set_async`] and passing it
/// a `false` value. You can see if you're in asynchronous mode or not
/// by calling [`async`].
///
/// # Target Management
///
/// The `SBDebugger` instance tracks the various targets that are
/// currently known to the debugger.
///
/// Typically, you create a target with [`create_target`],
/// [`create_target_simple`] or one of the related methods.
///
/// Sometimes, you'll want to create a target without an associated
/// executable. A common use case for this is to attach to a process
/// by name or process ID where you don't know the executable in advance.
/// The most convenient way to do this is:
///
/// ```
/// # use lldb::SBDebugger;
/// # SBDebugger::initialize();
/// let debugger = SBDebugger::create(false);
/// if let Some(target) = debugger.create_target_simple("") {
///     println!("Got a target: {:?}", target);
///     // Now, maybe we'd like to attach to something.
/// }
/// # SBDebugger::terminate();
/// ```
///
/// You can iterate over these targets which have been created by
/// using [`targets`]:
///
/// ```no_run
/// # use lldb::{SBDebugger, SBTarget};
/// # fn look_at_targets(debugger: SBDebugger) {
/// // Iterate over the targets...
/// for target in debugger.targets() {
///     println!("Hello {:?}!", target);
/// }
/// // Or collect them into a vector!
/// let targets = debugger.targets().collect::<Vec<SBTarget>>();
/// # }
/// ```
///
/// # Platform Management
///
/// ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`set_async`]: #method.set_async
/// [`async`]: #method.async
/// [`create_target`]: #method.create_target
/// [`create_target_simple`]: #method.create_target_simple
/// [`targets`]: #method.targets
pub struct SBDebugger {
    /// The underlying raw `SBDebuggerRef`.
    pub raw: sys::SBDebuggerRef,
}

impl SBDebugger {
    /// Initialize LLDB.
    ///
    /// This should be called before LLDB functionality is used.
    pub fn initialize() {
        unsafe { sys::SBDebuggerInitialize() };
    }

    /// Tear down LLDB.
    ///
    /// This should be called once the application no longer needs
    /// to use LLDB functionality. Typically, this is called as the
    /// application exits.
    pub fn terminate() {
        unsafe { sys::SBDebuggerTerminate() };
    }

    /// Create a new instance of `SBDebugger`.
    ///
    /// If `source_init_files` is `true`, then `~/.lldbinit` will
    /// be processed.
    pub fn create(source_init_files: bool) -> SBDebugger {
        SBDebugger {
            raw: unsafe { sys::SBDebuggerCreate2(source_init_files) },
        }
    }

    /// Get whether or not the debugger is in async mode.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn async(&self) -> bool {
        unsafe { sys::SBDebuggerGetAsync(self.raw) }
    }

    /// Set the debugger to be in async mode or not.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn set_async(&self, async: bool) {
        unsafe { sys::SBDebuggerSetAsync(self.raw, async) }
    }

    #[allow(missing_docs)]
    pub fn command_interpreter(&self) -> SBCommandInterpreter {
        SBCommandInterpreter::from(unsafe { sys::SBDebuggerGetCommandInterpreter(self.raw) })
    }

    /// Enable logging (defaults to `stderr`).
    ///
    /// `enable_log("lldb", &["default"])` is useful for troubleshooting in most
    /// cases. Include `"all"` in `categories` for extra verbosity.
    ///
    /// See invocations to `lldb_private::Log::Register` for more channels and
    /// categories.
    pub fn enable_log(&self, channel: &str, categories: &[&str]) -> bool {
        let channel = CString::new(channel).unwrap();
        let categories: Vec<_> = categories
            .iter()
            .map(|&s| CString::new(s).unwrap())
            .collect();
        let categories_ptr: Vec<_> = categories
            .iter()
            .map(|s| s.as_ptr())
            .chain(iter::once(ptr::null()))
            .collect();
        unsafe { sys::SBDebuggerEnableLog(self.raw, channel.as_ptr(), categories_ptr.as_ptr()) }
    }

    /// Get the LLDB version string.
    pub fn version() -> String {
        unsafe {
            match CStr::from_ptr(sys::SBDebuggerGetVersionString()).to_str() {
                Ok(s) => s.to_owned(),
                _ => panic!("No version string?"),
            }
        }
    }

    /// Create a target.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    pub fn create_target(
        &self,
        executable: &str,
        target_triple: Option<&str>,
        platform_name: Option<&str>,
        add_dependent_modules: bool,
    ) -> Result<SBTarget, SBError> {
        let executable = CString::new(executable).unwrap();
        let target_triple = target_triple.map(|s| CString::new(s).unwrap());
        let platform_name = platform_name.map(|s| CString::new(s).unwrap());
        let error = SBError::default();
        let target = unsafe {
            sys::SBDebuggerCreateTarget(
                self.raw,
                executable.as_ptr(),
                target_triple.map_or(ptr::null(), |s| s.as_ptr()),
                platform_name.map_or(ptr::null(), |s| s.as_ptr()),
                add_dependent_modules,
                error.raw,
            )
        };
        if error.is_success() {
            Ok(SBTarget::from(target))
        } else {
            Err(error)
        }
    }

    /// Create a target from just an executable name.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    ///
    /// Using [`create_target`] is preferred in most cases as
    /// that provides access to an `SBError` to inform the caller
    /// about what might have gone wrong.
    ///
    /// [`create_target`]: struct.SBDebugger.html#method.create_target
    pub fn create_target_simple(&self, executable: &str) -> Option<SBTarget> {
        let executable = CString::new(executable).unwrap();
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerCreateTarget2(self.raw, executable.as_ptr()) })
    }

    /// Get an iterator over the [targets] known to this debugger instance.
    ///
    /// [targets]: struct.SBTarget.html
    pub fn targets(&self) -> SBDebuggerTargetIter {
        SBDebuggerTargetIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get the default [SBListener] associated with the debugger.
    ///
    /// [SBListener]: struct.SBListener.html
    pub fn listener(&self) -> SBListener {
        SBListener::from(unsafe { sys::SBDebuggerGetListener(self.raw) })
    }

    /// Get the currently selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn selected_target(&self) -> Option<SBTarget> {
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerGetSelectedTarget(self.raw) })
    }

    /// Set the selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn set_selected_target(&self, target: &SBTarget) {
        unsafe { sys::SBDebuggerSetSelectedTarget(self.raw, target.raw) };
    }

    /// Get the currently selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn selected_platform(&self) -> SBPlatform {
        unsafe {
            SBPlatform {
                raw: sys::SBDebuggerGetSelectedPlatform(self.raw),
            }
        }
    }

    /// Set the selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn set_selected_platform(&self, platform: &SBPlatform) {
        unsafe { sys::SBDebuggerSetSelectedPlatform(self.raw, platform.raw) };
    }

    /// Get an iterator over the [platforms] known to this debugger instance.
    ///
    /// [platforms]: struct.SBPlatform.html
    pub fn platforms(&self) -> SBDebuggerPlatformIter {
        SBDebuggerPlatformIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get an iterator over the available platforms known to this debugger instance.
    pub fn available_platforms(&self) -> SBDebuggerAvailablePlatformIter {
        SBDebuggerAvailablePlatformIter {
            debugger: self,
            idx: 0,
        }
    }
}

/// Iterate over the [targets] known to a [debugger].
///
/// [targets]: struct.SBTarget.html
/// [debugger]: struct.SBDebugger.html
pub struct SBDebuggerTargetIter<'d> {
    debugger: &'d SBDebugger,
    idx: usize,
}

impl<'d> Iterator for SBDebuggerTargetIter<'d> {
    type Item = SBTarget;

    fn next(&mut self) -> Option<SBTarget> {
        if self.idx < unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) as usize } {
            let r = Some(SBTarget {
                raw: unsafe { sys::SBDebuggerGetTargetAtIndex(self.debugger.raw, self.idx as u32) },
            });
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) } as usize;
        (sz - self.idx, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerTargetIter<'d> {}

impl Clone for SBDebugger {
    fn clone(&self) -> SBDebugger {
        SBDebugger {
            raw: unsafe { sys::CloneSBDebugger(self.raw) },
        }
    }
}

impl fmt::Debug for SBDebugger {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBDebuggerGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBDebugger {{ {} }}", stream.data())
    }
}

impl Drop for SBDebugger {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBDebugger(self.raw) };
    }
}

impl From<sys::SBDebuggerRef> for SBDebugger {
    fn from(raw: sys::SBDebuggerRef) -> SBDebugger {
        SBDebugger { raw }
    }
}

unsafe impl Send for SBDebugger {}
unsafe impl Sync for SBDebugger {}

/// Iterate over the [platforms].
///
/// [platforms]: struct.SBPlatform.html
pub struct SBDebuggerPlatformIter<'d> {
    debugger: &'d SBDebugger,
    idx: u32,
}

impl<'d> Iterator for SBDebuggerPlatformIter<'d> {
    type Item = SBPlatform;

    fn next(&mut self) -> Option<SBPlatform> {
        if self.idx < unsafe { sys::SBDebuggerGetNumPlatforms(self.debugger.raw) } {
            let r = Some(SBPlatform::from(unsafe {
                sys::SBDebuggerGetPlatformAtIndex(self.debugger.raw, self.idx)
            }));
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumPlatforms(self.debugger.raw) } as usize;
        (sz - self.idx as usize, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerPlatformIter<'d> {}

/// Iterate over the available platforms.
pub struct SBDebuggerAvailablePlatformIter<'d> {
    debugger: &'d SBDebugger,
    idx: u32,
}

impl<'d> Iterator for SBDebuggerAvailablePlatformIter<'d> {
    type Item = SBStructuredData;

    fn next(&mut self) -> Option<SBStructuredData> {
        if self.idx < unsafe { sys::SBDebuggerGetNumAvailablePlatforms(self.debugger.raw) } {
            let r = Some(SBStructuredData::from(unsafe {
                sys::SBDebuggerGetAvailablePlatformInfoAtIndex(self.debugger.raw, self.idx)
            }));
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumAvailablePlatforms(self.debugger.raw) } as usize;
        (sz - self.idx as usize, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerAvailablePlatformIter<'d> {}

#[cfg(feature = "graphql")]
impl ::juniper::Context for SBDebugger {}

#[cfg(feature = "graphql")]
graphql_object!(SBDebugger: SBDebugger | &self | {
    field targets() -> Vec<SBTarget> {
        self.targets().collect()
    }

    field selected_target() -> Option<SBTarget> {
        self.selected_target()
    }

    field selected_platform() -> SBPlatform {
        self.selected_platform()
    }

    field platforms() -> Vec<SBPlatform> {
        self.platforms().collect()
    }

    field available_platforms() -> Vec<SBStructuredData> {
        self.available_platforms().collect()
    }
});

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

    #[test]
    fn it_works() {
        assert!(!SBDebugger::version().is_empty());
    }
}
<commit_msg>Expand on docs for SBDebugger::available_platforms().<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::commandinterpreter::SBCommandInterpreter;
use super::error::SBError;
use super::listener::SBListener;
use super::platform::SBPlatform;
use super::stream::SBStream;
use super::structureddata::SBStructuredData;
use super::target::SBTarget;
use std::ffi::{CStr, CString};
use std::fmt;
use std::iter;
use std::ptr;
use sys;

/// Creates [`SBTarget`]s, provides access to them and manages
/// the overall debugging experience.
///
/// # Initialization and Teardown
///
/// LLDB must be initialized before the functionality is used. This
/// is done with `SBDebugger::initialize()`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// ```
///
/// Similarly, it must be terminated after you are done using it:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
/// // Use LLDB functionality ...
/// SBDebugger::terminate();
/// ```
///
/// Once you've initialized LLDB, you're ready to create an instance
/// of `SBDebugger`:
///
/// ```
/// use lldb::SBDebugger;
///
/// SBDebugger::initialize();
///
/// let debugger = SBDebugger::create(false);
/// // ... configure the debugger if needed ...
/// // ... create a target and do stuff ...
///
/// SBDebugger::terminate();
/// ```
///
/// # Configuration
///
/// ## Async Mode
///
/// While it is best to use LLDB in asynchronous mode, it does offer a
/// synchronous mode, which can be easier to use for quick experiments
/// or scripts.
///
/// In synchronous mode, calls to the LLDB API do not return until the
/// underlying action has been completed. This means that the thread
/// from which you call LLDB will be blocked during that time, so this
/// is not an ideal way to use LLDB for building interactive tools
/// or a new user interface.
///
/// In asynchronous mode, calls to the LLDB API will return immediately
/// without waiting for the action to complete. This means that actions
/// like launching a target, continuing the execution of a process and
/// so on won't be completed immediately and you must process events
/// to see what the results of an action are.
///
/// Synchronous mode can be enabled by using [`set_async`] and passing it
/// a `false` value. You can see if you're in asynchronous mode or not
/// by calling [`async`].
///
/// # Target Management
///
/// The `SBDebugger` instance tracks the various targets that are
/// currently known to the debugger.
///
/// Typically, you create a target with [`create_target`],
/// [`create_target_simple`] or one of the related methods.
///
/// Sometimes, you'll want to create a target without an associated
/// executable. A common use case for this is to attach to a process
/// by name or process ID where you don't know the executable in advance.
/// The most convenient way to do this is:
///
/// ```
/// # use lldb::SBDebugger;
/// # SBDebugger::initialize();
/// let debugger = SBDebugger::create(false);
/// if let Some(target) = debugger.create_target_simple("") {
///     println!("Got a target: {:?}", target);
///     // Now, maybe we'd like to attach to something.
/// }
/// # SBDebugger::terminate();
/// ```
///
/// You can iterate over these targets which have been created by
/// using [`targets`]:
///
/// ```no_run
/// # use lldb::{SBDebugger, SBTarget};
/// # fn look_at_targets(debugger: SBDebugger) {
/// // Iterate over the targets...
/// for target in debugger.targets() {
///     println!("Hello {:?}!", target);
/// }
/// // Or collect them into a vector!
/// let targets = debugger.targets().collect::<Vec<SBTarget>>();
/// # }
/// ```
///
/// # Platform Management
///
/// ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`set_async`]: #method.set_async
/// [`async`]: #method.async
/// [`create_target`]: #method.create_target
/// [`create_target_simple`]: #method.create_target_simple
/// [`targets`]: #method.targets
pub struct SBDebugger {
    /// The underlying raw `SBDebuggerRef`.
    pub raw: sys::SBDebuggerRef,
}

impl SBDebugger {
    /// Initialize LLDB.
    ///
    /// This should be called before LLDB functionality is used.
    pub fn initialize() {
        unsafe { sys::SBDebuggerInitialize() };
    }

    /// Tear down LLDB.
    ///
    /// This should be called once the application no longer needs
    /// to use LLDB functionality. Typically, this is called as the
    /// application exits.
    pub fn terminate() {
        unsafe { sys::SBDebuggerTerminate() };
    }

    /// Create a new instance of `SBDebugger`.
    ///
    /// If `source_init_files` is `true`, then `~/.lldbinit` will
    /// be processed.
    pub fn create(source_init_files: bool) -> SBDebugger {
        SBDebugger {
            raw: unsafe { sys::SBDebuggerCreate2(source_init_files) },
        }
    }

    /// Get whether or not the debugger is in async mode.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn async(&self) -> bool {
        unsafe { sys::SBDebuggerGetAsync(self.raw) }
    }

    /// Set the debugger to be in async mode or not.
    ///
    /// When in async mode, the debugger returns immediately when
    /// stepping or continuing without waiting for the process
    /// to change state.
    pub fn set_async(&self, async: bool) {
        unsafe { sys::SBDebuggerSetAsync(self.raw, async) }
    }

    #[allow(missing_docs)]
    pub fn command_interpreter(&self) -> SBCommandInterpreter {
        SBCommandInterpreter::from(unsafe { sys::SBDebuggerGetCommandInterpreter(self.raw) })
    }

    /// Enable logging (defaults to `stderr`).
    ///
    /// `enable_log("lldb", &["default"])` is useful for troubleshooting in most
    /// cases. Include `"all"` in `categories` for extra verbosity.
    ///
    /// See invocations to `lldb_private::Log::Register` for more channels and
    /// categories.
    pub fn enable_log(&self, channel: &str, categories: &[&str]) -> bool {
        let channel = CString::new(channel).unwrap();
        let categories: Vec<_> = categories
            .iter()
            .map(|&s| CString::new(s).unwrap())
            .collect();
        let categories_ptr: Vec<_> = categories
            .iter()
            .map(|s| s.as_ptr())
            .chain(iter::once(ptr::null()))
            .collect();
        unsafe { sys::SBDebuggerEnableLog(self.raw, channel.as_ptr(), categories_ptr.as_ptr()) }
    }

    /// Get the LLDB version string.
    pub fn version() -> String {
        unsafe {
            match CStr::from_ptr(sys::SBDebuggerGetVersionString()).to_str() {
                Ok(s) => s.to_owned(),
                _ => panic!("No version string?"),
            }
        }
    }

    /// Create a target.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    pub fn create_target(
        &self,
        executable: &str,
        target_triple: Option<&str>,
        platform_name: Option<&str>,
        add_dependent_modules: bool,
    ) -> Result<SBTarget, SBError> {
        let executable = CString::new(executable).unwrap();
        let target_triple = target_triple.map(|s| CString::new(s).unwrap());
        let platform_name = platform_name.map(|s| CString::new(s).unwrap());
        let error = SBError::default();
        let target = unsafe {
            sys::SBDebuggerCreateTarget(
                self.raw,
                executable.as_ptr(),
                target_triple.map_or(ptr::null(), |s| s.as_ptr()),
                platform_name.map_or(ptr::null(), |s| s.as_ptr()),
                add_dependent_modules,
                error.raw,
            )
        };
        if error.is_success() {
            Ok(SBTarget::from(target))
        } else {
            Err(error)
        }
    }

    /// Create a target from just an executable name.
    ///
    /// The executable name may be an empty string to create
    /// an empty target.
    ///
    /// Using [`create_target`] is preferred in most cases as
    /// that provides access to an `SBError` to inform the caller
    /// about what might have gone wrong.
    ///
    /// [`create_target`]: struct.SBDebugger.html#method.create_target
    pub fn create_target_simple(&self, executable: &str) -> Option<SBTarget> {
        let executable = CString::new(executable).unwrap();
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerCreateTarget2(self.raw, executable.as_ptr()) })
    }

    /// Get an iterator over the [targets] known to this debugger instance.
    ///
    /// [targets]: struct.SBTarget.html
    pub fn targets(&self) -> SBDebuggerTargetIter {
        SBDebuggerTargetIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get the default [SBListener] associated with the debugger.
    ///
    /// [SBListener]: struct.SBListener.html
    pub fn listener(&self) -> SBListener {
        SBListener::from(unsafe { sys::SBDebuggerGetListener(self.raw) })
    }

    /// Get the currently selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn selected_target(&self) -> Option<SBTarget> {
        SBTarget::maybe_wrap(unsafe { sys::SBDebuggerGetSelectedTarget(self.raw) })
    }

    /// Set the selected [`SBTarget`].
    ///
    /// [SBTarget]: struct.SBTarget.html
    pub fn set_selected_target(&self, target: &SBTarget) {
        unsafe { sys::SBDebuggerSetSelectedTarget(self.raw, target.raw) };
    }

    /// Get the currently selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn selected_platform(&self) -> SBPlatform {
        unsafe {
            SBPlatform {
                raw: sys::SBDebuggerGetSelectedPlatform(self.raw),
            }
        }
    }

    /// Set the selected [`SBPlatform`].
    ///
    /// [`SBPlatform`]: struct.SBPlatform.html
    pub fn set_selected_platform(&self, platform: &SBPlatform) {
        unsafe { sys::SBDebuggerSetSelectedPlatform(self.raw, platform.raw) };
    }

    /// Get an iterator over the [platforms] known to this debugger instance.
    ///
    /// [platforms]: struct.SBPlatform.html
    pub fn platforms(&self) -> SBDebuggerPlatformIter {
        SBDebuggerPlatformIter {
            debugger: self,
            idx: 0,
        }
    }

    /// Get an iterator over the available platforms known to this debugger
    /// instance.
    ///
    /// These correspond to the available platform plugins within LLDB.
    ///
    /// The structured data will have 2 string keys:
    /// * `"name"` - Name of the platform plugin.
    /// * `"description"` - The description of the platform plugin.
    pub fn available_platforms(&self) -> SBDebuggerAvailablePlatformIter {
        SBDebuggerAvailablePlatformIter {
            debugger: self,
            idx: 0,
        }
    }
}

/// Iterate over the [targets] known to a [debugger].
///
/// [targets]: struct.SBTarget.html
/// [debugger]: struct.SBDebugger.html
pub struct SBDebuggerTargetIter<'d> {
    debugger: &'d SBDebugger,
    idx: usize,
}

impl<'d> Iterator for SBDebuggerTargetIter<'d> {
    type Item = SBTarget;

    fn next(&mut self) -> Option<SBTarget> {
        if self.idx < unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) as usize } {
            let r = Some(SBTarget {
                raw: unsafe { sys::SBDebuggerGetTargetAtIndex(self.debugger.raw, self.idx as u32) },
            });
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumTargets(self.debugger.raw) } as usize;
        (sz - self.idx, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerTargetIter<'d> {}

impl Clone for SBDebugger {
    fn clone(&self) -> SBDebugger {
        SBDebugger {
            raw: unsafe { sys::CloneSBDebugger(self.raw) },
        }
    }
}

impl fmt::Debug for SBDebugger {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBDebuggerGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBDebugger {{ {} }}", stream.data())
    }
}

impl Drop for SBDebugger {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBDebugger(self.raw) };
    }
}

impl From<sys::SBDebuggerRef> for SBDebugger {
    fn from(raw: sys::SBDebuggerRef) -> SBDebugger {
        SBDebugger { raw }
    }
}

unsafe impl Send for SBDebugger {}
unsafe impl Sync for SBDebugger {}

/// Iterate over the [platforms].
///
/// [platforms]: struct.SBPlatform.html
pub struct SBDebuggerPlatformIter<'d> {
    debugger: &'d SBDebugger,
    idx: u32,
}

impl<'d> Iterator for SBDebuggerPlatformIter<'d> {
    type Item = SBPlatform;

    fn next(&mut self) -> Option<SBPlatform> {
        if self.idx < unsafe { sys::SBDebuggerGetNumPlatforms(self.debugger.raw) } {
            let r = Some(SBPlatform::from(unsafe {
                sys::SBDebuggerGetPlatformAtIndex(self.debugger.raw, self.idx)
            }));
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumPlatforms(self.debugger.raw) } as usize;
        (sz - self.idx as usize, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerPlatformIter<'d> {}

/// Iterate over the available platforms.
pub struct SBDebuggerAvailablePlatformIter<'d> {
    debugger: &'d SBDebugger,
    idx: u32,
}

impl<'d> Iterator for SBDebuggerAvailablePlatformIter<'d> {
    type Item = SBStructuredData;

    fn next(&mut self) -> Option<SBStructuredData> {
        if self.idx < unsafe { sys::SBDebuggerGetNumAvailablePlatforms(self.debugger.raw) } {
            let r = Some(SBStructuredData::from(unsafe {
                sys::SBDebuggerGetAvailablePlatformInfoAtIndex(self.debugger.raw, self.idx)
            }));
            self.idx += 1;
            r
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let sz = unsafe { sys::SBDebuggerGetNumAvailablePlatforms(self.debugger.raw) } as usize;
        (sz - self.idx as usize, Some(sz))
    }
}

impl<'d> ExactSizeIterator for SBDebuggerAvailablePlatformIter<'d> {}

#[cfg(feature = "graphql")]
impl ::juniper::Context for SBDebugger {}

#[cfg(feature = "graphql")]
graphql_object!(SBDebugger: SBDebugger | &self | {
    field targets() -> Vec<SBTarget> {
        self.targets().collect()
    }

    field selected_target() -> Option<SBTarget> {
        self.selected_target()
    }

    field selected_platform() -> SBPlatform {
        self.selected_platform()
    }

    field platforms() -> Vec<SBPlatform> {
        self.platforms().collect()
    }

    field available_platforms() -> Vec<SBStructuredData> {
        self.available_platforms().collect()
    }
});

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

    #[test]
    fn it_works() {
        assert!(!SBDebugger::version().is_empty());
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

use error::PackageError;

/// Enum for representing the state of the package downloads.
pub enum PackageDownload<'a> {
    /// The state is switched to IsFinished when the download of the
    /// package is finished, leaving a `&Path` which is the location
    /// of the downloaded package, and a `u32` which is the file size.
    IsFinished(&'a Path, u32),
    /// The state is switched to InProgress when the download of the
    /// package is started, the `&Path` is the location where the
    /// half-downloaded package is located at, while the first `u32` is the
    /// progress of the current download process in bytes, the third `u32`
    /// is the total file size of the package.
    InProgress(&'a Path, u32, u32),
    /// This is the initial state of PackageDownload, where the package is yet
    /// to be downloaded. It has a `&str` for storing the URL, and a `&Path`
    /// of which the package is going to be located at, when the package is
    /// downloaded.
    NotStarted(&'a str, &'a Path)
}

impl<'a> PackageDownload<'a> {
    /// Create a new PackageDownload.
    fn new(url: &'a str, path: &'a Path) -> Self {
        PackageDownload::NotStarted(url, path)
    }

    /// A bool to determine whether the package is downloaded.
    fn is_downloaded(&self) -> bool {
        match self {
            &PackageDownload::IsFinished(_,_) => true,
            _ => false
        }
    }

    /// Returns the path of where the package is (or is going to be) located
    /// at.
    fn store_path(&self) -> &'a Path {
        match self {
            &PackageDownload::IsFinished(ref path, _) => path,
            &PackageDownload::InProgress(ref path, _, _) => path,
            &PackageDownload::NotStarted(_, ref path) => path,
        }
    }
}<commit_msg>Use struct-variant instead of tuple-variant<commit_after>use std::path::Path;

use error::PackageError;

/// Enum for representing the state of the package downloads.
pub enum PackageDownload<'a> {
    /// The state is switched to IsFinished when the download of the
    /// package is finished.
    IsFinished {
        /// The path where the downloaded package is located at.
        location: &'a Path,
        /// The total file size of the package.
        file_size: u32,
    },

    /// The state is switched to InProgress when the download of the
    /// package is started.
    InProgress {
        /// The path where the half-downloaded package is located at.
        location: &'a Path,
        /// The progress of the current download process.
        progress: u32,
        /// The total file size of the package.
        file_size: u32,
    },

    /// This is the initial state of PackageDownload, where the package is yet
    /// to be downloaded.
    NotStarted {
        /// The path where the package is going to be located at.
        location: &'a Path,
        /// The URL of where the package is going to be downloaded from.
        url: &'a str,
    }
}

impl<'a> PackageDownload<'a> {
    /// Create a new PackageDownload.
    fn new(url: &'a str, location: &'a Path) -> Self {
        PackageDownload::NotStarted { location, url }
    }

    /// A bool to determine whether the package is downloaded.
    fn is_downloaded(&self) -> bool {
        match self {
            &PackageDownload::IsFinished { location: _, file_size: _ } => true,
            _ => false
        }
    }

    /// Returns the path of where the package is (or is going to be) located
    /// at.
    fn store_path(&self) -> &'a Path {
        match self {
            &PackageDownload::IsFinished { location: path, file_size: _ } => path,
            &PackageDownload::InProgress { location: path, progress: _ , file_size: _} => path,
            &PackageDownload::NotStarted { location: path, url: _ } => path,
        }
    }
}<|endoftext|>


<commit_before>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; 2048];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(500);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;

        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes-last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes-last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = false;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<commit_msg>Show speed per chunk in human readable Byte/s<commit_after>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; 2048];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(500);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;

        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes-last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes-last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = true;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.set_units(Units::Bytes);
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<|endoftext|>


<commit_before>use super::respan::respan;
use proc_macro2::Span;
use std::mem;
use syn::{
    parse_quote,
    punctuated::Punctuated,
    visit_mut::{self, VisitMut},
    DeriveInput, ExprPath, Path, PathArguments, QSelf, Type, TypePath,
};

pub fn replace_receiver(input: &mut DeriveInput) {
    let self_ty = {
        let ident = &input.ident;
        let ty_generics = input.generics.split_for_impl().1;
        parse_quote!(#ident #ty_generics)
    };
    let mut visitor = ReplaceReceiver(&self_ty);
    visitor.visit_generics_mut(&mut input.generics);
    visitor.visit_data_mut(&mut input.data);
}

struct ReplaceReceiver<'a>(&'a TypePath);

impl ReplaceReceiver<'_> {
    fn self_ty(&self, span: Span) -> TypePath {
        respan(self.0, span)
    }

    fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) {
        if path.leading_colon.is_some() {
            return;
        }

        // Make borrow checker happy
        {
            let first = &path.segments[0];
            if first.ident != "Self" || !first.arguments.is_empty() {
                return;
            }
        }

        if path.segments.len() == 1 {
            self.self_to_expr_path(path);
            return;
        }

        let span = path.segments[0].ident.span();
        *qself = Some(QSelf {
            lt_token: Token![<](span),
            ty: Box::new(self.self_ty(span).into()),
            position: 0,
            as_token: None,
            gt_token: Token![>](span),
        });

        path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap());

        let segments = mem::replace(&mut path.segments, Punctuated::new());
        path.segments = segments.into_pairs().skip(1).collect();
    }

    fn self_to_expr_path(&self, path: &mut Path) {
        if path.leading_colon.is_some() {
            return;
        }

        // Make borrow checker happy
        {
            let first = &path.segments[0];
            if first.ident != "Self" || !first.arguments.is_empty() {
                return;
            }
        }

        let self_ty = self.self_ty(path.segments[0].ident.span());
        let variant = mem::replace(path, self_ty.path);
        for segment in &mut path.segments {
            if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
                if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
                    bracketed.colon2_token = Some(<Token![::]>::default());
                }
            }
        }
        if variant.segments.len() > 1 {
            path.segments.push_punct(<Token![::]>::default());
            path.segments.extend(variant.segments.into_pairs().skip(1));
        }
    }
}

impl VisitMut for ReplaceReceiver<'_> {
    // `Self` -> `Receiver`
    fn visit_type_mut(&mut self, ty: &mut Type) {
        let span = if let Type::Path(node) = ty {
            if node.qself.is_none() && node.path.is_ident("Self") {
                node.path.segments[0].ident.span()
            } else {
                self.visit_type_path_mut(node);
                return;
            }
        } else {
            visit_mut::visit_type_mut(self, ty);
            return;
        };
        *ty = self.self_ty(span).into();
    }

    // `Self::Assoc` -> `<Receiver>::Assoc`
    fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
        if ty.qself.is_none() {
            self.self_to_qself(&mut ty.qself, &mut ty.path);
        }
        visit_mut::visit_type_path_mut(self, ty);
    }

    // `Self::method` -> `<Receiver>::method`
    fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
        if expr.qself.is_none() {
            self.self_to_qself(&mut expr.qself, &mut expr.path);
        }
        visit_mut::visit_expr_path_mut(self, expr);
    }
}
<commit_msg>Touch up PR 1830<commit_after>use super::respan::respan;
use proc_macro2::Span;
use std::mem;
use syn::punctuated::Punctuated;
use syn::visit_mut::{self, VisitMut};
use syn::{parse_quote, DeriveInput, ExprPath, Path, PathArguments, QSelf, Type, TypePath};

pub fn replace_receiver(input: &mut DeriveInput) {
    let self_ty = {
        let ident = &input.ident;
        let ty_generics = input.generics.split_for_impl().1;
        parse_quote!(#ident #ty_generics)
    };
    let mut visitor = ReplaceReceiver(&self_ty);
    visitor.visit_generics_mut(&mut input.generics);
    visitor.visit_data_mut(&mut input.data);
}

struct ReplaceReceiver<'a>(&'a TypePath);

impl ReplaceReceiver<'_> {
    fn self_ty(&self, span: Span) -> TypePath {
        respan(self.0, span)
    }

    fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) {
        if path.leading_colon.is_some() || path.segments[0].ident != "Self" {
            return;
        }

        if path.segments.len() == 1 {
            self.self_to_expr_path(path);
            return;
        }

        let span = path.segments[0].ident.span();
        *qself = Some(QSelf {
            lt_token: Token![<](span),
            ty: Box::new(Type::Path(self.self_ty(span))),
            position: 0,
            as_token: None,
            gt_token: Token![>](span),
        });

        path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap());

        let segments = mem::replace(&mut path.segments, Punctuated::new());
        path.segments = segments.into_pairs().skip(1).collect();
    }

    fn self_to_expr_path(&self, path: &mut Path) {
        let self_ty = self.self_ty(path.segments[0].ident.span());
        let variant = mem::replace(path, self_ty.path);
        for segment in &mut path.segments {
            if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
                if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
                    bracketed.colon2_token = Some(<Token![::]>::default());
                }
            }
        }
        if variant.segments.len() > 1 {
            path.segments.push_punct(<Token![::]>::default());
            path.segments.extend(variant.segments.into_pairs().skip(1));
        }
    }
}

impl VisitMut for ReplaceReceiver<'_> {
    // `Self` -> `Receiver`
    fn visit_type_mut(&mut self, ty: &mut Type) {
        let span = if let Type::Path(node) = ty {
            if node.qself.is_none() && node.path.is_ident("Self") {
                node.path.segments[0].ident.span()
            } else {
                self.visit_type_path_mut(node);
                return;
            }
        } else {
            visit_mut::visit_type_mut(self, ty);
            return;
        };
        *ty = self.self_ty(span).into();
    }

    // `Self::Assoc` -> `<Receiver>::Assoc`
    fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
        if ty.qself.is_none() {
            self.self_to_qself(&mut ty.qself, &mut ty.path);
        }
        visit_mut::visit_type_path_mut(self, ty);
    }

    // `Self::method` -> `<Receiver>::method`
    fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
        if expr.qself.is_none() {
            self.self_to_qself(&mut expr.qself, &mut expr.path);
        }
        visit_mut::visit_expr_path_mut(self, expr);
    }
}
<|endoftext|>


<commit_before>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

// There doesn't seem to be a convenient way to get the "size" or
// "range" of an enum's possible values, so we'll make a trait for
// that here.
//
// Ideally we could populate it automatically through a macro, but
// for now we'll just implement it manually for all our enums.
trait SizedEnum {
  fn size() -> usize;
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

impl SizedEnum for RoomId {
  fn size() -> usize { NUM_ROOMS }
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

impl SizedEnum for MonsterId {
  fn size() -> usize { NUM_MONSTERS }
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot<F>(&mut self, num_rooms: usize, allotter: F)
    where F: Fn() -> RoomContents
  {
    for _ in 0..num_rooms {
      loop {
        let room_id = random_enum::<RoomId>();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let contents = allotter();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", contents, room_id);
            }
            room.contents = Some(contents);
            break;
          }
        }
      }
    }
  }

  fn allot_terror(&mut self) {
    self.allot(NUM_ROOMS_WITH_TERROR, || Terror(random_enum::<MonsterId>()))
  }

  fn allot_treasure(&mut self) {
    self.allot(NUM_ROOMS_WITH_TREASURE,
               || Treasure(random_treasure_amount()))
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.room(GreatHall).describe(
      "Great Hall",
      "You are in the great hall, an L-shaped room. \
       There are doors to the east and to the north. \
       In the alcove is a door to the west."
    );

    self.room(PrivateMeeting).describe(
      "Private Meeting Room",
      "This is the monarch's private meeting room. \
       There is a single exit to the south."
    );

    self.room(InnerHallway).describe(
      "Inner Hallway",
      "This inner hallway contains a door to the north, \
       and one to the west, and a circular stairwell \
       passes through the room. \
       You can see an ornamental lake through the \
       windows to the south."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
    self.connect(GreatHall, North, AudienceChamber);
    self.connect(GreatHall, West, AudienceChamber);
    self.connect(PrivateMeeting, South, InnerHallway);
    self.connect(InnerHallway, West, GreatHall);
    self.connect(InnerHallway, Up, UpperHallway);
  }
}

fn random_enum<T: FromPrimitive + SizedEnum>() -> T {
  loop {
    let r = random_i32(0, T::size() as i32);
    match T::from_i32(r) {
      Some(t) => { return t; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<commit_msg>Add kitchen.<commit_after>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

// There doesn't seem to be a convenient way to get the "size" or
// "range" of an enum's possible values, so we'll make a trait for
// that here.
//
// Ideally we could populate it automatically through a macro, but
// for now we'll just implement it manually for all our enums.
trait SizedEnum {
  fn size() -> usize;
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

impl SizedEnum for RoomId {
  fn size() -> usize { NUM_ROOMS }
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

impl SizedEnum for MonsterId {
  fn size() -> usize { NUM_MONSTERS }
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot<F>(&mut self, num_rooms: usize, allotter: F)
    where F: Fn() -> RoomContents
  {
    for _ in 0..num_rooms {
      loop {
        let room_id = random_enum::<RoomId>();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let contents = allotter();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", contents, room_id);
            }
            room.contents = Some(contents);
            break;
          }
        }
      }
    }
  }

  fn allot_terror(&mut self) {
    self.allot(NUM_ROOMS_WITH_TERROR, || Terror(random_enum::<MonsterId>()))
  }

  fn allot_treasure(&mut self) {
    self.allot(NUM_ROOMS_WITH_TREASURE,
               || Treasure(random_treasure_amount()))
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.room(GreatHall).describe(
      "Great Hall",
      "You are in the great hall, an L-shaped room. \
       There are doors to the east and to the north. \
       In the alcove is a door to the west."
    );

    self.room(PrivateMeeting).describe(
      "Private Meeting Room",
      "This is the monarch's private meeting room. \
       There is a single exit to the south."
    );

    self.room(InnerHallway).describe(
      "Inner Hallway",
      "This inner hallway contains a door to the north, \
       and one to the west, and a circular stairwell \
       passes through the room. \
       You can see an ornamental lake through the \
       windows to the south."
    );

    self.room(Kitchen).describe(
      "Kitchen",
      "This is the castle's kitchen. Through windows in \
       the north wall you can see a secret herb garden. \
       A door leaves the kitchen to the south."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
    self.connect(GreatHall, North, AudienceChamber);
    self.connect(GreatHall, West, AudienceChamber);
    self.connect(PrivateMeeting, South, InnerHallway);
    self.connect(InnerHallway, West, GreatHall);
    self.connect(InnerHallway, Up, UpperHallway);
    self.connect(Kitchen, South, StoreRoom);
  }
}

fn random_enum<T: FromPrimitive + SizedEnum>() -> T {
  loop {
    let r = random_i32(0, T::size() as i32);
    match T::from_i32(r) {
      Some(t) => { return t; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<|endoftext|>