<commit_before>//! # Config
//!
//! Just parse a toml file to extract
//! authentification tokens and
//! load into a credential struct for request signing.
//!
extern crate toml;

use std::fs::File;
use std::path::Path;
use std::io::{Read, Error};

const DEFAULT_CONFIG_PATH: &'static str = "Config.toml";

/// OVH API application credentials, including application key, application secret key,
/// consumer key, a temporary access token with access control to user API.
#[derive(Debug,Clone)]
pub struct Credential {
    path: Option<String>,
    toml: Option<toml::Value>,
    pub host: String,
    pub application_key: String,
    pub application_secret: String,
    pub consumer_key: String,
}

/// Utility fonction to read toml file by path
/// Currently only considere api on subsidiary : ovh-eu, ovh-ca.
fn read_from_path<'a, P: AsRef<Path>>(owner: &'a mut String,
                                      path: P)
                                      -> Result<(String, toml::Value), Error> {
    let mut fd = match File::open(path) {
        Err(_) => panic!("Cannot open given path"),
        Ok(fd) => fd,
    };
    match fd.read_to_string(owner) {
        Err(_) => panic!("Cannot read file"),
        Ok(s) => s,
    };
    let mut parser = toml::Parser::new(owner);
    let toml = match parser.parse() {
        None => panic!("Cannot parse toml content"),
        Some(_toml) => _toml,
    };
    let endpoint: toml::Value = toml.get("default")
        .unwrap()
        .lookup("endpoint")
        .unwrap()
        .clone();
    let _endpoint: &str = &endpoint.as_str().unwrap();
    let host = endpoint2host(_endpoint);

    Ok((host, toml.get(_endpoint).unwrap().clone()))
}

impl Credential {
    /// Initialize a new `Credential` from default path a App Key, App secret, Consumer token.
    pub fn new() -> Credential {
        let toml = &mut String::new();
        let (host, auth): (String, toml::Value) =
            match read_from_path(toml, DEFAULT_CONFIG_PATH.to_owned()) {
                Err(_) => panic!("Could not read auth"),
                Ok(_auth) => _auth,
            };
        let r_app_key = auth.lookup("application_key").unwrap().clone();
        let app_key = String::from(r_app_key.as_str().unwrap().clone());
        let r_app_secret = auth.lookup("application_secret").unwrap().clone();
        let app_secret = String::from(r_app_secret.as_str().unwrap().clone());
        let r_cons_key = auth.lookup("consumer_key").unwrap().clone();
        let cons_key = String::from(r_cons_key.as_str().unwrap().clone());

        Credential {
            toml: Some(auth),
            path: Some(DEFAULT_CONFIG_PATH.to_owned()),
            host: host,
            application_key: app_key,
            application_secret: app_secret,
            consumer_key: cons_key,
        }
    }

    /// Initialize a new `Credential` from given path a App Key, App secret, Consumer token.
    pub fn new_from_file<P: AsRef<Path>>(path: P) -> Credential {

        let toml = &mut String::new();
        let (host, auth): (String, toml::Value) = match read_from_path(toml, path) {
            Err(_) => panic!("Could not read auth"),
            Ok(_auth) => _auth,
        };
        let r_app_key = auth.lookup("application_key").unwrap().clone();
        let app_key = String::from(r_app_key.as_str().unwrap().clone());
        let r_app_secret = auth.lookup("application_secret").unwrap().clone();
        let app_secrets = String::from(r_app_secret.as_str().unwrap().clone());
        let r_cons_key = auth.lookup("consumer_key").unwrap().clone();
        let cons_key = String::from(r_cons_key.as_str().unwrap().clone());

        Credential {
            toml: Some(auth),
            path: Some("".to_string()),
            host: host,
            application_key: app_key,
            application_secret: app_secrets,
            consumer_key: cons_key,
        }
    }

    /// Initialize a new `Credential` from given an App Key and App secret.
    pub fn new_with_application(endpoint: &str,
                                application_key: &str,
                                application_secret: &str)
                                -> Credential {
        let host = endpoint2host(endpoint);
        Credential {
            toml: None,
            path: None,
            host: String::from(host),
            application_key: String::from(application_key),
            application_secret: String::from(application_secret),
            consumer_key: String::from(""),
        }
    }

    /// Initialize a new `Credential` from given an App Key, App Secret, and Consumer Key.
    pub fn new_with_credential(endpoint: &str,
                               application_key: &str,
                               application_secret: &str,
                               consumer_key: &str)
                               -> Credential {
        let host = endpoint2host(endpoint);
        Credential {
            toml: None,
            path: None,
            host: host,
            application_key: String::from(application_key),
            application_secret: String::from(application_secret),
            consumer_key: String::from(consumer_key),
        }
    }
}

fn endpoint2host(endpoint: &str) -> String {
    match endpoint.as_ref() {
        "ovh-eu" => "eu.api.ovh.com".to_string(),
        "ovh-ca" => "ca.api.ovh.com".to_string(),
        _ => "api.ovh.com".to_string(),
    }
}

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

    #[test]
    fn test_application_key() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.application_key;
        assert_eq!("ak", res);
    }

    #[test]
    fn test_application_secret() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.application_secret;
        assert_eq!("as", res);
    }

    #[test]
    fn test_consumer_key() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.consumer_key;
        assert_eq!("ck", res);
    }

    #[test]
    fn test_host() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.host;
        assert_eq!("eu.api.ovh.com", res);
    }

}
<commit_msg>Add missing OVH/Soyoustart/Kimsufi endpoints<commit_after>//! # Config
//!
//! Just parse a toml file to extract
//! authentification tokens and
//! load into a credential struct for request signing.
//!
extern crate toml;

use std::fs::File;
use std::path::Path;
use std::io::{Read, Error};

const DEFAULT_CONFIG_PATH: &'static str = "Config.toml";

/// OVH API application credentials, including application key, application secret key,
/// consumer key, a temporary access token with access control to user API.
#[derive(Debug,Clone)]
pub struct Credential {
    path: Option<String>,
    toml: Option<toml::Value>,
    pub host: String,
    pub application_key: String,
    pub application_secret: String,
    pub consumer_key: String,
}

/// Utility fonction to read toml file by path
/// Currently only considere api on subsidiary : ovh-eu, ovh-ca.
fn read_from_path<'a, P: AsRef<Path>>(owner: &'a mut String,
                                      path: P)
                                      -> Result<(String, toml::Value), Error> {
    let mut fd = match File::open(path) {
        Err(_) => panic!("Cannot open given path"),
        Ok(fd) => fd,
    };
    match fd.read_to_string(owner) {
        Err(_) => panic!("Cannot read file"),
        Ok(s) => s,
    };
    let mut parser = toml::Parser::new(owner);
    let toml = match parser.parse() {
        None => panic!("Cannot parse toml content"),
        Some(_toml) => _toml,
    };
    let endpoint: toml::Value = toml.get("default")
        .unwrap()
        .lookup("endpoint")
        .unwrap()
        .clone();
    let _endpoint: &str = &endpoint.as_str().unwrap();
    let host = endpoint2host(_endpoint);

    Ok((host, toml.get(_endpoint).unwrap().clone()))
}

impl Credential {
    /// Initialize a new `Credential` from default path a App Key, App secret, Consumer token.
    pub fn new() -> Credential {
        let toml = &mut String::new();
        let (host, auth): (String, toml::Value) =
            match read_from_path(toml, DEFAULT_CONFIG_PATH.to_owned()) {
                Err(_) => panic!("Could not read auth"),
                Ok(_auth) => _auth,
            };
        let r_app_key = auth.lookup("application_key").unwrap().clone();
        let app_key = String::from(r_app_key.as_str().unwrap().clone());
        let r_app_secret = auth.lookup("application_secret").unwrap().clone();
        let app_secret = String::from(r_app_secret.as_str().unwrap().clone());
        let r_cons_key = auth.lookup("consumer_key").unwrap().clone();
        let cons_key = String::from(r_cons_key.as_str().unwrap().clone());

        Credential {
            toml: Some(auth),
            path: Some(DEFAULT_CONFIG_PATH.to_owned()),
            host: host,
            application_key: app_key,
            application_secret: app_secret,
            consumer_key: cons_key,
        }
    }

    /// Initialize a new `Credential` from given path a App Key, App secret, Consumer token.
    pub fn new_from_file<P: AsRef<Path>>(path: P) -> Credential {

        let toml = &mut String::new();
        let (host, auth): (String, toml::Value) = match read_from_path(toml, path) {
            Err(_) => panic!("Could not read auth"),
            Ok(_auth) => _auth,
        };
        let r_app_key = auth.lookup("application_key").unwrap().clone();
        let app_key = String::from(r_app_key.as_str().unwrap().clone());
        let r_app_secret = auth.lookup("application_secret").unwrap().clone();
        let app_secrets = String::from(r_app_secret.as_str().unwrap().clone());
        let r_cons_key = auth.lookup("consumer_key").unwrap().clone();
        let cons_key = String::from(r_cons_key.as_str().unwrap().clone());

        Credential {
            toml: Some(auth),
            path: Some("".to_string()),
            host: host,
            application_key: app_key,
            application_secret: app_secrets,
            consumer_key: cons_key,
        }
    }

    /// Initialize a new `Credential` from given an App Key and App secret.
    pub fn new_with_application(endpoint: &str,
                                application_key: &str,
                                application_secret: &str)
                                -> Credential {
        let host = endpoint2host(endpoint);
        Credential {
            toml: None,
            path: None,
            host: String::from(host),
            application_key: String::from(application_key),
            application_secret: String::from(application_secret),
            consumer_key: String::from(""),
        }
    }

    /// Initialize a new `Credential` from given an App Key, App Secret, and Consumer Key.
    pub fn new_with_credential(endpoint: &str,
                               application_key: &str,
                               application_secret: &str,
                               consumer_key: &str)
                               -> Credential {
        let host = endpoint2host(endpoint);
        Credential {
            toml: None,
            path: None,
            host: host,
            application_key: String::from(application_key),
            application_secret: String::from(application_secret),
            consumer_key: String::from(consumer_key),
        }
    }
}

fn endpoint2host(endpoint: &str) -> String {
    match endpoint.as_ref() {
        "ovh-ca" => "ca.api.ovh.com".to_string(), // OVH North America
        "ovh-eu" => "eu.api.ovh.com".to_string(), // OVH Europe
        "ovh-us" => "us.api.ovh.com".to_string(), // OVH US

        "soyoustart-ca" => "ca.api.soyoustart.com".to_string(), // So you Start North America
        "soyoustart-eu" => "eu.api.soyoustart.com".to_string(), // So you Start Europe

        "kimsufi-ca" => "ca.api.kimsufi.com".to_string(), // Kimsufi North America
        "kimsufi-eu" => "eu.api.kimsufi.com".to_string(), // Kimsufi Europe

        _ => "api.ovh.com".to_string(),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use super::Credential;

    #[test]
    fn test_application_key() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.application_key;
        assert_eq!("ak", res);
    }

    #[test]
    fn test_application_secret() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.application_secret;
        assert_eq!("as", res);
    }

    #[test]
    fn test_consumer_key() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.consumer_key;
        assert_eq!("ck", res);
    }

    #[test]
    fn test_host() {
        let cred = Credential::new_from_file("Config.toml.dist");
        let res = cred.host;
        assert_eq!("eu.api.ovh.com", res);
    }

    #[test]
    fn test_endpoint2host() {

        let mut test_hosts: HashMap<&str, String> = HashMap::new();
        test_hosts.insert("ovh-ca", "ca.api.ovh.com".to_string());
        test_hosts.insert("ovh-eu", "eu.api.ovh.com".to_string());
        test_hosts.insert("ovh-us", "us.api.ovh.com".to_string());

        test_hosts.insert("soyoustart-ca", "ca.api.soyoustart.com".to_string());
        test_hosts.insert("soyoustart-eu", "eu.api.soyoustart.com".to_string());

        test_hosts.insert("kimsufi-ca", "ca.api.kimsufi.com".to_string());
        test_hosts.insert("kimsufi-eu", "eu.api.kimsufi.com".to_string());

        test_hosts.insert("idontexist-nw", "api.ovh.com".to_string());

        for (endpoint, expected_host) in test_hosts {
            let cred = Credential::new_with_application(endpoint, "test", "test");
            assert_eq!(expected_host.to_string(), cred.host);
        }

    }

}
<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::alloc::Layout;
use std::mem::MaybeUninit;
use std::os::unix::io::AsRawFd;

use data_model::DataInit;
use libc::EINVAL;

use crate::{errno_result, Error, FromRawDescriptor, LayoutAllocation, Result, SafeDescriptor};

// Custom nlmsghdr struct that can be declared DataInit.
#[repr(C)]
#[derive(Copy, Clone)]
struct NlMsgHdr {
    pub nlmsg_len: u32,
    pub nlmsg_type: u16,
    pub nlmsg_flags: u16,
    pub nlmsg_seq: u32,
    pub nlmsg_pid: u32,
}
unsafe impl DataInit for NlMsgHdr {}

/// A single netlink message, including its header and data.
pub struct NetlinkMessage<'a> {
    pub _type: u16,
    pub flags: u16,
    pub seq: u32,
    pub pid: u32,
    pub data: &'a [u8],
}

/// Iterator over `struct nlmsghdr` as received from a netlink socket.
pub struct NetlinkMessageIter<'a> {
    // `data` must be properly aligned for nlmsghdr.
    data: &'a [u8],
}

impl<'a> Iterator for NetlinkMessageIter<'a> {
    type Item = NetlinkMessage<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        const HDR_SIZE: usize = std::mem::size_of::<NlMsgHdr>();
        if self.data.len() < HDR_SIZE {
            return None;
        }
        let hdr = NlMsgHdr::from_slice(&self.data[..HDR_SIZE])?;

        // NLMSG_OK
        let hdr_len = hdr.nlmsg_len as usize;
        if hdr_len < HDR_SIZE || hdr_len > self.data.len() {
            return None;
        }

        // NLMSG_DATA
        let data_start = HDR_SIZE;
        let data_end = hdr_len - data_start;
        let data = &self.data[data_start..data_end];

        // NLMSG_NEXT
        let align_to = std::mem::align_of::<NlMsgHdr>();
        let next_hdr = (hdr_len + align_to - 1) & !(align_to - 1);
        if next_hdr >= self.data.len() {
            self.data = &[];
        } else {
            self.data = &self.data[next_hdr..];
        }

        Some(NetlinkMessage {
            _type: hdr.nlmsg_type,
            flags: hdr.nlmsg_flags,
            seq: hdr.nlmsg_seq,
            pid: hdr.nlmsg_pid,
            data,
        })
    }
}

/// Safe wrapper for `NETLINK_GENERIC` netlink sockets.
pub struct NetlinkGenericSocket {
    sock: SafeDescriptor,
}

impl NetlinkGenericSocket {
    /// Create and bind a new `NETLINK_GENERIC` socket.
    pub fn new(nl_groups: u32) -> Result<Self> {
        // Safe because we check the return value and convert the raw fd into a SafeDescriptor.
        let sock = unsafe {
            let fd = libc::socket(
                libc::AF_NETLINK,
                libc::SOCK_RAW | libc::SOCK_CLOEXEC,
                libc::NETLINK_GENERIC,
            );
            if fd < 0 {
                return errno_result();
            }

            SafeDescriptor::from_raw_descriptor(fd)
        };

        // This MaybeUninit dance is needed because sockaddr_nl has a private padding field and
        // doesn't implement Default. Safe because all 0s is valid data for sockaddr_nl.
        let mut sa = unsafe { MaybeUninit::<libc::sockaddr_nl>::zeroed().assume_init() };
        sa.nl_family = libc::AF_NETLINK as libc::sa_family_t;
        sa.nl_groups = nl_groups;

        // Safe because we pass a descriptor that we own and valid pointer/size for sockaddr.
        unsafe {
            let res = libc::bind(
                sock.as_raw_fd(),
                &sa as *const libc::sockaddr_nl as *const libc::sockaddr,
                std::mem::size_of_val(&sa) as libc::socklen_t,
            );
            if res < 0 {
                return errno_result();
            }
        }

        Ok(NetlinkGenericSocket { sock })
    }

    /// Receive messages from the netlink socket.
    pub fn recv(&self) -> Result<NetlinkGenericRead> {
        let buf_size = 8192; // TODO(dverkamp): make this configurable?

        // Create a buffer with sufficient alignment for nlmsghdr.
        let layout = Layout::from_size_align(buf_size, std::mem::align_of::<NlMsgHdr>())
            .map_err(|_| Error::new(EINVAL))?;
        let allocation = LayoutAllocation::uninitialized(layout);

        // Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
        let bytes_read = unsafe {
            let res = libc::recv(
                self.sock.as_raw_fd(),
                allocation.as_ptr() as *mut libc::c_void,
                buf_size,
                0,
            );
            if res < 0 {
                return errno_result();
            }
            res as usize
        };

        Ok(NetlinkGenericRead {
            allocation,
            len: bytes_read,
        })
    }
}

pub struct NetlinkGenericRead {
    allocation: LayoutAllocation,
    len: usize,
}

impl NetlinkGenericRead {
    pub fn iter(&self) -> NetlinkMessageIter {
        // Safe because the data in allocation was initialized up to `self.len` by `recv()` and is
        // sufficiently aligned.
        let data = unsafe { &self.allocation.as_slice(self.len) };
        NetlinkMessageIter { data }
    }
}
<commit_msg>sys_util: netlink: use better naming for netlink message length<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::alloc::Layout;
use std::mem::MaybeUninit;
use std::os::unix::io::AsRawFd;

use data_model::DataInit;
use libc::EINVAL;

use crate::{errno_result, Error, FromRawDescriptor, LayoutAllocation, Result, SafeDescriptor};

// Custom nlmsghdr struct that can be declared DataInit.
#[repr(C)]
#[derive(Copy, Clone)]
struct NlMsgHdr {
    pub nlmsg_len: u32,
    pub nlmsg_type: u16,
    pub nlmsg_flags: u16,
    pub nlmsg_seq: u32,
    pub nlmsg_pid: u32,
}
unsafe impl DataInit for NlMsgHdr {}

/// A single netlink message, including its header and data.
pub struct NetlinkMessage<'a> {
    pub _type: u16,
    pub flags: u16,
    pub seq: u32,
    pub pid: u32,
    pub data: &'a [u8],
}

/// Iterator over `struct nlmsghdr` as received from a netlink socket.
pub struct NetlinkMessageIter<'a> {
    // `data` must be properly aligned for nlmsghdr.
    data: &'a [u8],
}

impl<'a> Iterator for NetlinkMessageIter<'a> {
    type Item = NetlinkMessage<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        const HDR_SIZE: usize = std::mem::size_of::<NlMsgHdr>();
        if self.data.len() < HDR_SIZE {
            return None;
        }
        let hdr = NlMsgHdr::from_slice(&self.data[..HDR_SIZE])?;

        // NLMSG_OK
        let msg_len = hdr.nlmsg_len as usize;
        if msg_len < HDR_SIZE || msg_len > self.data.len() {
            return None;
        }

        // NLMSG_DATA
        let data_start = HDR_SIZE;
        let data_end = msg_len - data_start;
        let data = &self.data[data_start..data_end];

        // NLMSG_NEXT
        let align_to = std::mem::align_of::<NlMsgHdr>();
        let next_hdr = (msg_len + align_to - 1) & !(align_to - 1);
        if next_hdr >= self.data.len() {
            self.data = &[];
        } else {
            self.data = &self.data[next_hdr..];
        }

        Some(NetlinkMessage {
            _type: hdr.nlmsg_type,
            flags: hdr.nlmsg_flags,
            seq: hdr.nlmsg_seq,
            pid: hdr.nlmsg_pid,
            data,
        })
    }
}

/// Safe wrapper for `NETLINK_GENERIC` netlink sockets.
pub struct NetlinkGenericSocket {
    sock: SafeDescriptor,
}

impl NetlinkGenericSocket {
    /// Create and bind a new `NETLINK_GENERIC` socket.
    pub fn new(nl_groups: u32) -> Result<Self> {
        // Safe because we check the return value and convert the raw fd into a SafeDescriptor.
        let sock = unsafe {
            let fd = libc::socket(
                libc::AF_NETLINK,
                libc::SOCK_RAW | libc::SOCK_CLOEXEC,
                libc::NETLINK_GENERIC,
            );
            if fd < 0 {
                return errno_result();
            }

            SafeDescriptor::from_raw_descriptor(fd)
        };

        // This MaybeUninit dance is needed because sockaddr_nl has a private padding field and
        // doesn't implement Default. Safe because all 0s is valid data for sockaddr_nl.
        let mut sa = unsafe { MaybeUninit::<libc::sockaddr_nl>::zeroed().assume_init() };
        sa.nl_family = libc::AF_NETLINK as libc::sa_family_t;
        sa.nl_groups = nl_groups;

        // Safe because we pass a descriptor that we own and valid pointer/size for sockaddr.
        unsafe {
            let res = libc::bind(
                sock.as_raw_fd(),
                &sa as *const libc::sockaddr_nl as *const libc::sockaddr,
                std::mem::size_of_val(&sa) as libc::socklen_t,
            );
            if res < 0 {
                return errno_result();
            }
        }

        Ok(NetlinkGenericSocket { sock })
    }

    /// Receive messages from the netlink socket.
    pub fn recv(&self) -> Result<NetlinkGenericRead> {
        let buf_size = 8192; // TODO(dverkamp): make this configurable?

        // Create a buffer with sufficient alignment for nlmsghdr.
        let layout = Layout::from_size_align(buf_size, std::mem::align_of::<NlMsgHdr>())
            .map_err(|_| Error::new(EINVAL))?;
        let allocation = LayoutAllocation::uninitialized(layout);

        // Safe because we pass a valid, owned socket fd and a valid pointer/size for the buffer.
        let bytes_read = unsafe {
            let res = libc::recv(
                self.sock.as_raw_fd(),
                allocation.as_ptr() as *mut libc::c_void,
                buf_size,
                0,
            );
            if res < 0 {
                return errno_result();
            }
            res as usize
        };

        Ok(NetlinkGenericRead {
            allocation,
            len: bytes_read,
        })
    }
}

pub struct NetlinkGenericRead {
    allocation: LayoutAllocation,
    len: usize,
}

impl NetlinkGenericRead {
    pub fn iter(&self) -> NetlinkMessageIter {
        // Safe because the data in allocation was initialized up to `self.len` by `recv()` and is
        // sufficiently aligned.
        let data = unsafe { &self.allocation.as_slice(self.len) };
        NetlinkMessageIter { data }
    }
}
<|endoftext|>


<commit_before>use num::Num;

#[inline(always)]
pub fn new<'a, 'b, T: Num>(x: T, y: T) -> [T; 2] {[x, y]}
#[inline(always)]
pub fn create<'a, 'b, T: Num>(x: T, y: T) -> [T; 2] {new(x, y)}
#[test]
fn test_new() {
    let v = new(1, 2);
    assert!(v[0] == 1);
    assert!(v[1] == 2);
}

#[inline(always)]
pub fn clone<'a, 'b, T: Num>(v: &'b [T; 2]) -> [T; 2] {new(v[0], v[1])}

#[inline(always)]
pub fn copy<'a, 'b, T: Num>(out: &'a mut [T; 2], a: &'b [T; 2]) -> &'a mut [T; 2] {
    out[0] = a[0];
    out[1] = a[1];
    out
}
#[test]
fn test_copy() {
    let mut v = [0, 0];
    copy(&mut v, &[1, 2]);
    assert!(v == [1, 2]);
}
<commit_msg>updates<commit_after>use num::Num;

#[inline(always)]
pub fn new<T: Num>(x: T, y: T) -> [T; 2] {[x, y]}
#[inline(always)]
pub fn create<T: Num>(x: T, y: T) -> [T; 2] {new(x, y)}
#[test]
fn test_new() {
    let v = new(1, 2);
    assert!(v[0] == 1);
    assert!(v[1] == 2);
}

#[inline(always)]
pub fn clone<'b, T: Num>(v: &'b [T; 2]) -> [T; 2] {new(v[0], v[1])}

#[inline(always)]
pub fn copy<'a, 'b, T: Num>(out: &'a mut [T; 2], a: &'b [T; 2]) -> &'a mut [T; 2] {
    out[0] = a[0];
    out[1] = a[1];
    out
}
#[test]
fn test_copy() {
    let mut v = [0, 0];
    copy(&mut v, &[1, 2]);
    assert!(v == [1, 2]);
}
<|endoftext|>


<commit_before>//! Defines valid types.

use std::fmt;
use std::collections::HashMap;

use petgraph::graph::{Graph, NodeIndex};

#[derive(Clone, Debug)]
pub enum RType {
    Overdefined,
    Reg8,
    Reg16,
    Reg32,
    Reg64,
    Num8,
    Num16,
    Num32,
    Num64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Int8,
    Int16,
    Int32,
    Int64,
    Bool,
    Undefined,
    Ptr(Box<RType>),
    Code,
}

impl fmt::Display for RType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let t = if let RType::Ptr(ref ty) = *self {
            format!("{}*", ty)
        } else {
            String::new()
        };

        let s = match *self {
            RType::Overdefined => "overdefined",
            RType::Reg8 => "reg8",
            RType::Reg16 => "reg16",
            RType::Reg32 => "reg32",
            RType::Reg64 => "reg64",
            RType::Num8 => "n8",
            RType::Num16 => "n16",
            RType::Num32 => "n32",
            RType::Num64 => "n64",
            RType::UInt8 => "u8",
            RType::UInt16 => "u16",
            RType::UInt32 => "u32",
            RType::UInt64 => "u64",
            RType::Int8 => "i8",
            RType::Int16 => "i16",
            RType::Int32 => "i32",
            RType::Int64 => "i64",
            RType::Bool => "bool",
            RType::Undefined => "underdefined",
            RType::Ptr(_) => t.as_ref(),
            RType::Code => "code",
        };
        write!(f, "{}", s)
    }
}

#[derive(Clone, Debug)]
pub enum ConstraintNode {
    Type(RType),
    TypeVar(usize),
    DisjunctiveJoin,
    ConjunctiveJoin,
    Equality,
    Union,
    Intersect,
    Subtype,
}

#[derive(Clone, Debug)]
pub enum ConstraintEdge {
    EdgeIdx(u8),
}

#[derive(Clone, Debug)]
pub enum SubTypeNode {
    Tmp
}

#[derive(Clone, Debug)]
pub enum SubTypeEdge {
    Tmp
}

#[derive(Clone, Debug)]
pub struct SubTypeSet {
    g: Graph<SubTypeNode, SubTypeEdge>,
}

impl SubTypeSet {
    pub fn new() -> SubTypeSet {
        SubTypeSet {
            g: Graph::new(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ConstraintSet {
    g: Graph<ConstraintNode, ConstraintEdge>,
    type_vars: Vec<NodeIndex>,
    subty: SubTypeSet,
    binding_map: HashMap<usize, RType>,
    upper_bound: HashMap<usize, RType>,
    lower_bound: HashMap<usize, RType>,
}

impl ConstraintSet {
    pub fn new() -> ConstraintSet {
        ConstraintSet {
            g: Graph::new(),
            type_vars: Vec::new(),
            subty: SubTypeSet::new(),
            binding_map: HashMap::new(),
            upper_bound: HashMap::new(),
            lower_bound: HashMap::new(),
        }
    }

    // Return the constraints contained by the set
    pub fn constraints(&self) -> ::std::slice::Iter<NodeIndex> {
        unimplemented!()
    }

    pub fn rhs(&self, n: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn lhs(&self, n: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_type_var(&mut self) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_type(&mut self, ty: RType) -> NodeIndex {
        unimplemented!()
    }

    pub fn disjunctive_join(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn conjunctive_join(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn equal(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn union(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn intersect(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }
}
<commit_msg>More framework for TIE<commit_after>//! Defines valid types.

use std::fmt;
use std::collections::HashMap;

use petgraph::graph::{Graph, NodeIndex, EdgeIndex};

#[derive(Clone, Debug)]
pub enum RType {
    Overdefined,
    Reg8,
    Reg16,
    Reg32,
    Reg64,
    Num8,
    Num16,
    Num32,
    Num64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Int8,
    Int16,
    Int32,
    Int64,
    Bool,
    Ptr(Box<RType>),
    Code,
    Union(Vec<RType>),
    Intersect(Vec<RType>),
    Undefined,
}

impl fmt::Display for RType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let RType::Ptr(ref ty) = *self {
            write!(f, "{}*", ty)
        } else if let RType::Union(ref tys) = *self {
            let mut s = String::from("union(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else if let RType::Intersect(ref tys) = *self {
            let mut s = String::from("intersect(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else {
            let s = match *self {
                RType::Overdefined => "overdefined",
                RType::Reg8 => "reg8",
                RType::Reg16 => "reg16",
                RType::Reg32 => "reg32",
                RType::Reg64 => "reg64",
                RType::Num8 => "n8",
                RType::Num16 => "n16",
                RType::Num32 => "n32",
                RType::Num64 => "n64",
                RType::UInt8 => "u8",
                RType::UInt16 => "u16",
                RType::UInt32 => "u32",
                RType::UInt64 => "u64",
                RType::Int8 => "i8",
                RType::Int16 => "i16",
                RType::Int32 => "i32",
                RType::Int64 => "i64",
                RType::Bool => "bool",
                RType::Undefined => "underdefined",
                RType::Code => "code",
                _ => unreachable!(),
            };
            write!(f, "{}", s)
        }
    }
}

#[derive(Clone, Debug)]
pub enum ConstraintNode {
    Type(RType),
    AbstractType,
    TypeVar,
    DisjunctiveJoin,
    ConjunctiveJoin,
    Union,
    Intersect,
}

#[derive(Clone, Debug)]
pub enum ConstraintEdge {
    EdgeIdx(u8),
    SubType,
    Equal,
}

#[derive(Clone, Debug)]
pub enum SubTypeNode {
    // Index in the original constaints set.
    OrigIdx(NodeIndex),
}

#[derive(Clone, Debug)]
pub enum SubTypeEdge {
    SubType
}

#[derive(Clone, Debug)]
pub struct SubTypeSet {
    g: Graph<SubTypeNode, SubTypeEdge>,
    // Map from NodeIndex in ConstraintSet to NodeIndex in SubTypeSet.
    map: HashMap<NodeIndex, NodeIndex>,
}

impl SubTypeSet {
    pub fn new() -> SubTypeSet {
        SubTypeSet {
            g: Graph::new(),
            map: HashMap::new(),
        }
    }

    // Insert LHS <: RHS
    pub fn insert_relation(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        unimplemented!()
    }

    // For all ( alpha <: S ) return alpha
    pub fn subtypes_of(&self, idx_s: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }

    // For all ( T <: beta ) return beta
    pub fn supertypes_of(&self, idx_t: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }
}

#[derive(Clone, Debug)]
pub struct ConstraintSet {
    g: Graph<ConstraintNode, ConstraintEdge>,
    type_vars: HashMap<String, NodeIndex>,
    // S <:
    subty: SubTypeSet,
    // S=
    binding_map: HashMap<NodeIndex, RType>,
    // B^
    upper_bound: HashMap<NodeIndex, RType>,
    // B_
    lower_bound: HashMap<NodeIndex, RType>,
}

impl ConstraintSet {
    pub fn new() -> ConstraintSet {
        ConstraintSet {
            g: Graph::new(),
            type_vars: HashMap::new(),
            subty: SubTypeSet::new(),
            binding_map: HashMap::new(),
            upper_bound: HashMap::new(),
            lower_bound: HashMap::new(),
        }
    }

    pub fn rhs(&self, n: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn lhs(&self, n: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_type_var(&mut self) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_abstract_type(&mut self) -> NodeIndex {
        unimplemented!()
    }

    pub fn insert_base_type(&mut self, ty: RType) -> NodeIndex {
        unimplemented!()
    }

    pub fn subtype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        unimplemented!()
    }

    pub fn supertype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        self.subtype(rhs, lhs)
    }

    pub fn disjunctive_join(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) -> NodeIndex {
        unimplemented!()
    }

    pub fn conjunctive_join(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn equal(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn union(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn intersect(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn solve(&mut self) {
        unimplemented!()
    }

    fn decompose(&mut self, subtype_edge: EdgeIndex) {
        unimplemented!()
    }

    fn meet(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }

    fn join(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use config;
use env::Env;
use library;
use nameutil::signal_to_snake;
use parser::is_empty_c_type;
use super::bounds::{Bounds, BoundType};
use super::conversion_type::ConversionType;
use super::ffi_type::used_ffi_type;
use super::parameter;
use super::ref_mode::RefMode;
use super::rust_type::{bounds_rust_type, rust_type, used_rust_type};
use traits::IntoString;
use version::Version;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<parameter::Parameter>,
    pub ret: library::Parameter,
    pub bounds: Bounds,
    pub version: Option<Version>,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, type_tid: library::TypeId, in_trait: bool,
               trampolines: &mut Trampolines, used_types: &mut Vec<String>,
               version: Option<Version>) -> Result<String, Vec<String>> {
    let errors = closure_errors(env, signal);
    if !errors.is_empty() {
        warn!("Can't generate {} trampoline for signal '{}'", type_tid.full_name(&env.library),
              signal.name);
        return Err(errors);
    }

    let name = format!("{}_trampoline", signal_to_snake(&signal.name));

    let owner = env.type_(type_tid);

    let c_type = format!("{}*", owner.get_glib_name().unwrap());

    //Fake
    let configured_functions: Vec<&config::functions::Function> = Vec::new();

    let mut bounds: Bounds = Default::default();

    let mut parameters: Vec<parameter::Parameter> = Vec::with_capacity(signal.parameters.len() + 1);

    let this = parameter::Parameter {
        name: "this".to_owned(),
        typ: type_tid,
        c_type: c_type,
        instance_parameter: false, //true,
        direction: library::ParameterDirection::In,
        transfer: library::Transfer::None,
        caller_allocates: false,
        nullable: library::Nullable(false),
        allow_none: false,
        is_error: false,
        ref_mode: RefMode::ByRef,
        to_glib_extra: String::new(),
    };
    parameters.push(this);

    if let Some(s) = used_ffi_type(env, type_tid) {
        used_types.push(s);
    }

    if in_trait {
        let type_name = bounds_rust_type(env, type_tid);
        bounds.add_parameter("this", &type_name.into_string(), BoundType::IsA);
    }

    for par in &signal.parameters {
        let analysis = parameter::analyze(env, par, &configured_functions);

        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, par.typ) {
            used_types.push(s);
        }

        parameters.push(analysis);
    }

    if signal.ret.typ != Default::default() {
        if let Some(s) = used_ffi_type(env, signal.ret.typ) {
            used_types.push(s);
        }
    }

    let trampoline = Trampoline {
        name: name.clone(),
        parameters: parameters,
        ret: signal.ret.clone(),
        bounds: bounds,
        version: version,
    };
    trampolines.push(trampoline);
    Ok(name)
}

fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
    let mut errors: Vec<String> = Vec::new();
    for par in &signal.parameters {
        if let Some(error) = type_error(env, par) {
            errors.push(format!("{} {}: {}", error, par.name,
                                par.typ.full_name(&env.library)));
        }
    }
    if signal.ret.typ != Default::default() {
        if let Some(error) = type_error(env, &signal.ret) {
            errors.push(format!("{} return value {}", error,
                                signal.ret.typ.full_name(&env.library)));
        }
    }
    errors
}

fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
    use super::rust_type::TypeError::*;
    if par.direction == library::ParameterDirection::Out {
        Some("Out")
    } else if par.direction == library::ParameterDirection::InOut {
        Some("InOut")
    } else if is_empty_c_type(&par.c_type) {
        Some("Empty ctype")
    } else if ConversionType::of(&env.library, par.typ) == ConversionType::Unknown {
        Some("Unknown conversion")
    } else {
        match rust_type(env, par.typ) {
            Err(Ignored(_)) => Some("Ignored"),
            Err(Mismatch(_)) => Some("Mismatch"),
            Err(Unimplemented(_)) => Some("Unimplemented"),
            Ok(_) => None,
        }
    }
}
<commit_msg>Add used types from signal return<commit_after>use config;
use env::Env;
use library;
use nameutil::signal_to_snake;
use parser::is_empty_c_type;
use super::bounds::{Bounds, BoundType};
use super::conversion_type::ConversionType;
use super::ffi_type::used_ffi_type;
use super::parameter;
use super::ref_mode::RefMode;
use super::rust_type::{bounds_rust_type, rust_type, used_rust_type};
use traits::IntoString;
use version::Version;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<parameter::Parameter>,
    pub ret: library::Parameter,
    pub bounds: Bounds,
    pub version: Option<Version>,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, type_tid: library::TypeId, in_trait: bool,
               trampolines: &mut Trampolines, used_types: &mut Vec<String>,
               version: Option<Version>) -> Result<String, Vec<String>> {
    let errors = closure_errors(env, signal);
    if !errors.is_empty() {
        warn!("Can't generate {} trampoline for signal '{}'", type_tid.full_name(&env.library),
              signal.name);
        return Err(errors);
    }

    let name = format!("{}_trampoline", signal_to_snake(&signal.name));

    let owner = env.type_(type_tid);

    let c_type = format!("{}*", owner.get_glib_name().unwrap());

    //Fake
    let configured_functions: Vec<&config::functions::Function> = Vec::new();

    let mut bounds: Bounds = Default::default();

    let mut parameters: Vec<parameter::Parameter> = Vec::with_capacity(signal.parameters.len() + 1);

    let this = parameter::Parameter {
        name: "this".to_owned(),
        typ: type_tid,
        c_type: c_type,
        instance_parameter: false, //true,
        direction: library::ParameterDirection::In,
        transfer: library::Transfer::None,
        caller_allocates: false,
        nullable: library::Nullable(false),
        allow_none: false,
        is_error: false,
        ref_mode: RefMode::ByRef,
        to_glib_extra: String::new(),
    };
    parameters.push(this);

    if let Some(s) = used_ffi_type(env, type_tid) {
        used_types.push(s);
    }

    if in_trait {
        let type_name = bounds_rust_type(env, type_tid);
        bounds.add_parameter("this", &type_name.into_string(), BoundType::IsA);
    }

    for par in &signal.parameters {
        let analysis = parameter::analyze(env, par, &configured_functions);

        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, par.typ) {
            used_types.push(s);
        }

        parameters.push(analysis);
    }

    if signal.ret.typ != Default::default() {
        if let Ok(s) = used_rust_type(env, signal.ret.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, signal.ret.typ) {
            used_types.push(s);
        }
    }

    let trampoline = Trampoline {
        name: name.clone(),
        parameters: parameters,
        ret: signal.ret.clone(),
        bounds: bounds,
        version: version,
    };
    trampolines.push(trampoline);
    Ok(name)
}

fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
    let mut errors: Vec<String> = Vec::new();
    for par in &signal.parameters {
        if let Some(error) = type_error(env, par) {
            errors.push(format!("{} {}: {}", error, par.name,
                                par.typ.full_name(&env.library)));
        }
    }
    if signal.ret.typ != Default::default() {
        if let Some(error) = type_error(env, &signal.ret) {
            errors.push(format!("{} return value {}", error,
                                signal.ret.typ.full_name(&env.library)));
        }
    }
    errors
}

fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
    use super::rust_type::TypeError::*;
    if par.direction == library::ParameterDirection::Out {
        Some("Out")
    } else if par.direction == library::ParameterDirection::InOut {
        Some("InOut")
    } else if is_empty_c_type(&par.c_type) {
        Some("Empty ctype")
    } else if ConversionType::of(&env.library, par.typ) == ConversionType::Unknown {
        Some("Unknown conversion")
    } else {
        match rust_type(env, par.typ) {
            Err(Ignored(_)) => Some("Ignored"),
            Err(Mismatch(_)) => Some("Mismatch"),
            Err(Unimplemented(_)) => Some("Unimplemented"),
            Ok(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::Book;
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;

use std::iter::Iterator;
use std::fs::File;
use std::borrow::Cow;
use std::io::Read;

use mustache;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::FileNotFound(self.source.clone(),
                                                                                "image".to_owned(),
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::Render("no output pdf file specified in book config".to_owned()))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("numbering").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("display_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i]);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v, true)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = mustache::compile_str(try!(self.book.get_template("tex.template")).as_ref());
        let mut data = self.book.get_mapbuilder("tex")
            .insert_str("content", content)
            .insert_str("tex_lang", tex_lang);
        if self.book.options.get_bool("tex.short") == Ok(true) {
            data = data.insert_bool("short", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }

    /// Transform a vector of `Token`s to LaTeX
    fn render_vec(&mut self, tokens: &[Token], escape: bool) -> Result<String> {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&try!(self.parse_token(&token, escape)));
        }
        Ok(res)
    }

    fn parse_token(&mut self, token: &Token, escape: bool) -> Result<String> {
        match *token {
            Token::Str(ref text) => if escape {
                Ok(self.book.clean(escape_tex(text), true))
            } else {
                Ok(text.clone())
            },
            Token::Paragraph(ref vec) => Ok(format!("{}\n\n",
                                                 try!(self.render_vec(vec, escape)))),
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 && self.current_chapter == Number::Hidden {
                    return Ok(String::new());
                }
                if n == 1 {
                    if let Number::Specified(n) = self.current_chapter {
                        content.push_str(r"\setcounter{chapter}{");
                        content.push_str(&format!("{}", n - 1));
                        content.push_str("}\n");
                    }
                }
                match n {
                    1 => content.push_str(r"\chapter"),
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec, true)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec, escape)))),
            Token::CodeBlock(_, ref vec) => Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", try!(self.render_vec(vec, false)))),
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec, escape)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec, escape)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec, escape)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec, escape));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url);
                    if content == url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          Cow::Borrowed(url)));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        Cow::Borrowed(url)))))
                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec, escape))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.parse_token(v, escape)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec, escape),
        }
    }
}
<commit_msg>Fix Image/StandaloneImage (they were switched) rendering in Latex<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use book::Book;
use number::Number;
use error::{Error,Result, Source};
use token::Token;
use zipper::Zipper;
use escape::escape_tex;
use resource_handler::ResourceHandler;

use std::iter::Iterator;
use std::fs::File;
use std::borrow::Cow;
use std::io::Read;

use mustache;

/// LaTeX renderer
pub struct LatexRenderer<'a> {
    book: &'a Book,
    current_chapter: Number,
    handler: ResourceHandler<'a>,
    source: Source,
}

impl<'a> LatexRenderer<'a> {
    /// Creates new LatexRenderer
    pub fn new(book: &'a Book) -> LatexRenderer<'a> {
        let mut handler = ResourceHandler::new(&book.logger);
        handler.set_images_mapping(true);
        LatexRenderer {
            book: book,
            current_chapter: Number::Default,
            handler: handler,
            source: Source::empty(),
        }
    }

    /// Render pdf in a file
    pub fn render_pdf(&mut self) -> Result<String> {
        if let Ok(pdf_file) = self.book.options.get_path("output.pdf") {
            let content = try!(self.render_book());
            let mut zipper = try!(Zipper::new(&self.book.options.get_path("temp_dir").unwrap()));
            try!(zipper.write("result.tex", &content.as_bytes(), false));

            // write image files
            for (source, dest) in self.handler.images_mapping() {
                let mut f = try!(File::open(source).map_err(|_| Error::FileNotFound(self.source.clone(),
                                                                                "image".to_owned(),
                                                                                source.to_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::Render(format!("error while reading image file: {}", e))));
                try!(zipper.write(dest, &content, true));
            }

            zipper.generate_pdf(&self.book.options.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
        } else {
            Err(Error::Render("no output pdf file specified in book config".to_owned()))
        }
    }

    /// Render latex in a string
    pub fn render_book(&mut self) -> Result<String> {
        let mut content = String::from("");
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("chapter-{}", i));
        }

        // set tex numbering and toc display to book's parameters
        let numbering = self.book.options.get_i32("numbering").unwrap() - 1;
        content.push_str(&format!("\\setcounter{{tocdepth}}{{{}}}
\\setcounter{{secnumdepth}}{{{}}}\n",
                                  numbering, numbering));

        if self.book.options.get_bool("display_toc").unwrap() {
            content.push_str("\\tableofcontents\n");
        }

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            self.source = Source::new(&self.book.filenames[i]);
            content.push_str(&format!("\\label{{chapter-{}}}", i));
            self.current_chapter = n;
            content.push_str(&try!(self.render_vec(v, true)));
            i += 1;
        }
        self.source = Source::empty();

        let tex_lang = String::from(match self.book.options.get_str("lang").unwrap() {
            "af" => "afrikaans",
            "sq" => "albanian",
            "eu" => "basque",
            "bg" => "bulgarian",
            "ca" => "catalan",
            "hr" => "croatian",
            "cs" => "czech",
            "da" => "danish",
            "nl" => "dutch",
            "en" => "english",
            "eo" => "esperanto",
            "et" => "estonian",
            "fi" => "finnish",
            "fr" => "francais",
            "gl" => "galician",
            "el" => "greek",
            "de" => "ngerman",
            "he" => "hebrew",
            "hu" => "hungarian",
            "it" => "italian",
            "is" => "icelandic",
            "id" => "indonesian",
            "ga" => "irish",
            "la" => "latin",
            "ms" => "malay",
            "nn" => "norsk",
            "pl" => "polish",
            "pt" => "portuguese",
            "ro" => "romanian",
            "ru" => "russian",
            "gd" => "scottish",
            "sr" => "serbian",
            "sk" => "slovak",
            "sl" => "slovene",
            "es" => "spanish",
            "sw" => "swedish",
            "tr" => "turkish",
            "uk" => "ukrainian",
            "cy" => "welsh",
            _ => {
                self.book.logger.error(format!("LaTeX: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.options.get_str("lang").unwrap()));
                "english"
            }
        });

        let template = mustache::compile_str(try!(self.book.get_template("tex.template")).as_ref());
        let mut data = self.book.get_mapbuilder("tex")
            .insert_str("content", content)
            .insert_str("tex_lang", tex_lang);
        if self.book.options.get_bool("tex.short") == Ok(true) {
            data = data.insert_bool("short", true);
        }
        let data = data.build();
        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => panic!("generated LaTeX was not valid utf-8"),
            Ok(res) => Ok(res)
        }
    }

    /// Transform a vector of `Token`s to LaTeX
    fn render_vec(&mut self, tokens: &[Token], escape: bool) -> Result<String> {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&try!(self.parse_token(&token, escape)));
        }
        Ok(res)
    }

    fn parse_token(&mut self, token: &Token, escape: bool) -> Result<String> {
        match *token {
            Token::Str(ref text) => if escape {
                Ok(self.book.clean(escape_tex(text), true))
            } else {
                Ok(text.clone())
            },
            Token::Paragraph(ref vec) => Ok(format!("{}\n\n",
                                                 try!(self.render_vec(vec, escape)))),
            Token::Header(n, ref vec) => {
                let mut content = String::new();
                if n == 1 && self.current_chapter == Number::Hidden {
                    return Ok(String::new());
                }
                if n == 1 {
                    if let Number::Specified(n) = self.current_chapter {
                        content.push_str(r"\setcounter{chapter}{");
                        content.push_str(&format!("{}", n - 1));
                        content.push_str("}\n");
                    }
                }
                match n {
                    1 => content.push_str(r"\chapter"),
                    2 => content.push_str(r"\section"),
                    3 => content.push_str(r"\subsection"),
                    4 => content.push_str(r"\subsubsection"),
                    _ => content.push_str(r"\paragraph"),
                }
                if self.current_chapter == Number::Unnumbered {
                    content.push_str("*");
                }
                content.push_str(r"{");
                content.push_str(&try!(self.render_vec(vec, true)));
                content.push_str("}\n");
                Ok(content)
            },
            Token::Emphasis(ref vec) => Ok(format!("\\emph{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Strong(ref vec) => Ok(format!("\\textbf{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Code(ref vec) => Ok(format!("\\texttt{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::BlockQuote(ref vec) => Ok(format!("\\begin{{quotation}}\n{}\\end{{quotation}}\n", try!(self.render_vec(vec, escape)))),
            Token::CodeBlock(_, ref vec) => Ok(format!("\\begin{{spverbatim}}{}\\end{{spverbatim}}\n\\vspace{{1em}}\n", try!(self.render_vec(vec, false)))),
            Token::Rule => Ok(String::from("\\HRule\n")),
            Token::SoftBreak => Ok(String::from(" ")),
            Token::HardBreak => Ok(String::from("\n")),
            Token::List(ref vec) => Ok(format!("\\begin{{itemize}}\n{}\\end{{itemize}}", try!(self.render_vec(vec, escape)))),
            Token::OrderedList(_, ref vec) => Ok(format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", try!(self.render_vec(vec, escape)))),
            Token::Item(ref vec) => Ok(format!("\\item {}\n", try!(self.render_vec(vec, escape)))),
            Token::Link(ref url, _, ref vec) => {
                let content = try!(self.render_vec(vec, escape));

                if ResourceHandler::is_local(url) {
                    Ok(format!("\\hyperref[{}]{{{}}}",
                            self.handler.get_link(url),
                            content))
                }
                else {
                    let url = escape_tex(url);
                    if content == url {
                        Ok(format!("\\url{{{}}}", content))
                    } else {
                        if self.book.options.get_bool("tex.links_as_footnotes").unwrap() {
                            Ok(format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", url, content, url))
                        } else {
                            Ok(format!("\\href{{{}}}{{{}}}", url, content))
                        }
                    }
                }
            },
            Token::StandaloneImage(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    let img = try!(self.handler.map_image(&self.source,
                                                          Cow::Borrowed(url)));
                    Ok(format!("\\begin{{center}}
  \\includegraphics[width=0.8\\linewidth]{{{}}}
\\end{{center}}",
                            img))

                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::Image(ref url, _, _) => {
                if ResourceHandler::is_local(url) {
                    Ok(format!("\\includegraphics{{{}}}",
                            try!(self.handler.map_image(&self.source,
                                                        Cow::Borrowed(url)))))
                } else {
                    self.book.logger.error(&format!("LaTeX: image '{}' doesn't seem to be local; ignoring it in Latex output.", url));
                    Ok(String::new())
                }
            },
            Token::Footnote(ref vec) => Ok(format!("\\footnote{{{}}}", try!(self.render_vec(vec, escape)))),
            Token::Table(n, ref vec) => {
                let mut cols = String::new();
                for _ in 0..n {
                    cols.push_str("|c");
                }
                cols.push_str("|");
                Ok(format!("\\begin{{center}}
\\begin{{tabular}}{{{}}}
\\hline
{}
\\hline
\\end{{tabular}}
\\end{{center}}\n\n", cols, try!(self.render_vec(vec, escape))))
            },
            Token::TableRow(ref vec) | Token::TableHead(ref vec) => {
                let mut res:String = try!(vec.iter()
                    .map(|v| {self.parse_token(v, escape)})
                    .collect::<Result<Vec<_>>>())
                    .join(" & ");
                res.push_str("\\\\ \n");
                if let Token::TableHead(_) = *token {
                    res.push_str("\\hline\n");
                }
                Ok(res)
            }
            Token::TableCell(ref vec) => self.render_vec(vec, escape),
        }
    }
}
<|endoftext|>


<commit_before>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::{FutureExt};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::mem;
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    /// Will be 'None' only briefly when the data has been taken to be dropped
    data:   Option<Pin<Box<T>>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Some(Pin::new(Box::new(data)))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            try_sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future returned is a `BoxFuture`, which you can create using `.boxed()` or `Box::pin()` on a future. This is
    /// solely to work around a limitation in Rust's type system (it's not presently possible to introduce the lifetime
    /// from for<'a> into the return type of a function)
    ///
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    pub fn future_sync<'a, TFn, TOutput>(&'a self, job: TFn) -> impl 'a+Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'a+Send+for<'b> FnOnce(&'b mut T) -> BoxFuture<'b, TOutput>,
            TOutput:    'a+Send {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        self.future(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }.boxed()
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = self.data.take();

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                mem::drop(data);
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                mem::drop(data);
            });
        }
    }
}
<commit_msg>Update documentation on future() and future_desync() in Desync<commit_after>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::{FutureExt};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::mem;
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    /// Will be 'None' only briefly when the data has been taken to be dropped
    data:   Option<Pin<Box<T>>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Some(Pin::new(Box::new(data)))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(&**self.data.as_ref().unwrap());

            try_sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future will be scheduled in the background, so it will make progress even if the current scheduler is
    /// blocked for any reason. Additionally, it's not necessary to await the returned future, which can be discarded
    /// if necessary.
    ///
    /// The future returned is a `BoxFuture`, which you can create using `.boxed()` or `Box::pin()` on a future. This is
    /// solely to work around a limitation in Rust's type system (it's not presently possible to introduce the lifetime
    /// from for<'a> into the return type of a function)
    ///
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    /// The future will be scheduled in the current execution context, so it will only make progress if the current
    /// scheduler is running. The task will be cancelled and will not complete execution if the future is dropped before
    /// completion, so it's usually necessary to await the result of this function for the task to behave correctly.
    ///
    /// The future returned is a `BoxFuture`, which you can create using `.boxed()` or `Box::pin()` on a future. This is
    /// solely to work around a limitation in Rust's type system (it's not presently possible to introduce the lifetime
    /// from for<'a> into the return type of a function)
    ///
    pub fn future_sync<'a, TFn, TOutput>(&'a self, job: TFn) -> impl 'a+Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'a+Send+for<'b> FnOnce(&'b mut T) -> BoxFuture<'b, TOutput>,
            TOutput:    'a+Send {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(&**self.data.as_ref().unwrap());

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        self.future(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }.boxed()
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = self.data.take();

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                mem::drop(data);
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                mem::drop(data);
            });
        }
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;

use ::context::Context;
use ::device_handle::DeviceHandle;
use ::device_descriptor::DeviceDescriptor;
use ::config_descriptor::ConfigDescriptor;
use ::fields::Speed;

/// A reference to a USB device.
pub struct Device<'a> {
    context: PhantomData<&'a Context>,
    device: *mut ::libusb::libusb_device
}

impl<'a> Drop for Device<'a> {
    /// Releases the device reference.
    fn drop(&mut self) {
        unsafe {
            ::libusb::libusb_unref_device(self.device);
        }
    }
}

impl<'a> Device<'a> {
    /// Reads the device descriptor.
    pub fn device_descriptor(&mut self) -> ::Result<DeviceDescriptor> {
        let mut descriptor: ::libusb::libusb_device_descriptor = unsafe { mem::uninitialized() };

        // since libusb 1.0.16, this function always succeeds
        try_unsafe!(::libusb::libusb_get_device_descriptor(self.device, &mut descriptor));

        Ok(::device_descriptor::from_libusb(descriptor))
    }

    /// Reads a configuration descriptor.
    pub fn config_descriptor(&mut self, config_index: u8) -> ::Result<ConfigDescriptor> {
        let mut config: *const ::libusb::libusb_config_descriptor = unsafe { mem::uninitialized() };

        try_unsafe!(::libusb::libusb_get_config_descriptor(self.device, config_index, &mut config));

        Ok(::config_descriptor::from_libusb(config))
    }

    /// Returns the number of the bus that the device is connected to.
    pub fn bus_number(&mut self) -> u8 {
        unsafe {
            ::libusb::libusb_get_bus_number(self.device)
        }
    }

    /// Returns the device's address on the bus that it's connected to.
    pub fn address(&mut self) -> u8 {
        unsafe {
            ::libusb::libusb_get_device_address(self.device)
        }
    }

    /// Returns the device's connection speed.
    pub fn speed(&mut self) -> Speed {
        ::fields::speed_from_libusb(unsafe {
            ::libusb::libusb_get_device_speed(self.device)
        })
    }

    /// Opens the device.
    pub fn open(&mut self) -> ::Result<DeviceHandle<'a>> {
        let mut handle: *mut ::libusb::libusb_device_handle = unsafe { mem::uninitialized() };

        try_unsafe!(::libusb::libusb_open(self.device, &mut handle));

        Ok(::device_handle::from_libusb(self.context, handle))
    }
}

#[doc(hidden)]
pub fn from_libusb<'a>(context: PhantomData<&'a Context>, device: *mut ::libusb::libusb_device) -> Device<'a> {
    unsafe { ::libusb::libusb_ref_device(device) };

    Device {
        context: context,
        device: device,
    }
}
<commit_msg>Add Device::active_config_descriptor<commit_after>use std::marker::PhantomData;
use std::mem;

use ::context::Context;
use ::device_handle::DeviceHandle;
use ::device_descriptor::DeviceDescriptor;
use ::config_descriptor::ConfigDescriptor;
use ::fields::Speed;

/// A reference to a USB device.
pub struct Device<'a> {
    context: PhantomData<&'a Context>,
    device: *mut ::libusb::libusb_device
}

impl<'a> Drop for Device<'a> {
    /// Releases the device reference.
    fn drop(&mut self) {
        unsafe {
            ::libusb::libusb_unref_device(self.device);
        }
    }
}

impl<'a> Device<'a> {
    /// Reads the device descriptor.
    pub fn device_descriptor(&mut self) -> ::Result<DeviceDescriptor> {
        let mut descriptor: ::libusb::libusb_device_descriptor = unsafe { mem::uninitialized() };

        // since libusb 1.0.16, this function always succeeds
        try_unsafe!(::libusb::libusb_get_device_descriptor(self.device, &mut descriptor));

        Ok(::device_descriptor::from_libusb(descriptor))
    }

    /// Reads a configuration descriptor.
    pub fn config_descriptor(&mut self, config_index: u8) -> ::Result<ConfigDescriptor> {
        let mut config: *const ::libusb::libusb_config_descriptor = unsafe { mem::uninitialized() };

        try_unsafe!(::libusb::libusb_get_config_descriptor(self.device, config_index, &mut config));

        Ok(::config_descriptor::from_libusb(config))
    }

    /// Reads the configuration descriptor for the current configuration
    pub fn active_config_descriptor(&self) -> ::Result<ConfigDescriptor> {
        let mut config: *const ::libusb::libusb_config_descriptor = unsafe { mem::uninitialized() };

        try_unsafe!(::libusb::libusb_get_active_config_descriptor(self.device, &mut config));

        Ok(::config_descriptor::from_libusb(config))
    }

    /// Returns the number of the bus that the device is connected to.
    pub fn bus_number(&mut self) -> u8 {
        unsafe {
            ::libusb::libusb_get_bus_number(self.device)
        }
    }

    /// Returns the device's address on the bus that it's connected to.
    pub fn address(&mut self) -> u8 {
        unsafe {
            ::libusb::libusb_get_device_address(self.device)
        }
    }

    /// Returns the device's connection speed.
    pub fn speed(&mut self) -> Speed {
        ::fields::speed_from_libusb(unsafe {
            ::libusb::libusb_get_device_speed(self.device)
        })
    }

    /// Opens the device.
    pub fn open(&mut self) -> ::Result<DeviceHandle<'a>> {
        let mut handle: *mut ::libusb::libusb_device_handle = unsafe { mem::uninitialized() };

        try_unsafe!(::libusb::libusb_open(self.device, &mut handle));

        Ok(::device_handle::from_libusb(self.context, handle))
    }
}

#[doc(hidden)]
pub fn from_libusb<'a>(context: PhantomData<&'a Context>, device: *mut ::libusb::libusb_device) -> Device<'a> {
    unsafe { ::libusb::libusb_ref_device(device) };

    Device {
        context: context,
        device: device,
    }
}
<|endoftext|>