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

use bincode::serde::DeserializeError;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::collections::hash_map::HashMap;
use std::cell::{RefCell};
use std::io::{Error, ErrorKind};
use std::slice;
use std::fmt::{self, Debug, Formatter};
use std::cmp::{PartialEq};
use std::ops::Deref;
use std::mem;
use std::usize;

use uuid::Uuid;

#[derive(Clone)]
struct ServerRecord {
    sender: OsIpcSender,
    conn_sender: mpsc::Sender<bool>,
    conn_receiver: Arc<Mutex<mpsc::Receiver<bool>>>,
}

impl ServerRecord {
    fn new(sender: OsIpcSender) -> ServerRecord {
        let (tx, rx) = mpsc::channel::<bool>();
        ServerRecord {
            sender: sender,
            conn_sender: tx,
            conn_receiver: Arc::new(Mutex::new(rx)),
        }
    }

    fn accept(&self) {
        self.conn_receiver.lock().unwrap().recv().unwrap();
    }

    fn connect(&self) {
        self.conn_sender.send(true).unwrap();
    }
}

lazy_static! {
    static ref ONE_SHOT_SERVERS: Mutex<HashMap<String,ServerRecord>> = Mutex::new(HashMap::new());
}

struct MpscChannelMessage(Vec<u8>, Vec<OsIpcChannel>, Vec<OsIpcSharedMemory>);

pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver),MpscError> {
    let (base_sender, base_receiver) = mpsc::channel::<MpscChannelMessage>();
    Ok((OsIpcSender::new(base_sender), OsIpcReceiver::new(base_receiver)))
}

#[derive(Debug)]
pub struct OsIpcReceiver {
    receiver: RefCell<Option<mpsc::Receiver<MpscChannelMessage>>>,
}

impl PartialEq for OsIpcReceiver {
    fn eq(&self, other: &OsIpcReceiver) -> bool {
        self.receiver.borrow().as_ref().map(|rx| rx as *const _) ==
            other.receiver.borrow().as_ref().map(|rx| rx as *const _)
    }
}

impl OsIpcReceiver {
    fn new(receiver: mpsc::Receiver<MpscChannelMessage>) -> OsIpcReceiver {
        OsIpcReceiver {
            receiver: RefCell::new(Some(receiver)),
        }
    }

    pub fn consume(&self) -> OsIpcReceiver {
        let receiver = self.receiver.borrow_mut().take();
        OsIpcReceiver::new(receiver.unwrap())
    }

    pub fn recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
        let r = self.receiver.borrow();
        match r.as_ref().unwrap().recv() {
            Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                 c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                 s)),
            Err(_) => Err(MpscError::ChannelClosedError),
        }
    }

    pub fn try_recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
        let r = self.receiver.borrow();
        match r.as_ref().unwrap().try_recv() {
            Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                 c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                 s)),
            Err(_) => Err(MpscError::ChannelClosedError),
        }
    }
}

#[derive(Clone, Debug)]
pub struct OsIpcSender {
    sender: Arc<Mutex<mpsc::Sender<MpscChannelMessage>>>,
}

impl PartialEq for OsIpcSender {
    fn eq(&self, other: &OsIpcSender) -> bool {
        &*self.sender.lock().unwrap() as *const _ ==
            &*other.sender.lock().unwrap() as *const _
    }
}

impl OsIpcSender {
    fn new(sender: mpsc::Sender<MpscChannelMessage>) -> OsIpcSender {
        OsIpcSender {
            sender: Arc::new(Mutex::new(sender)),
        }
    }

    pub fn connect(name: String) -> Result<OsIpcSender,MpscError> {
        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&name).unwrap().clone();
        record.connect();
        Ok(record.sender)
    }

    pub fn get_max_fragment_size() -> usize {
        usize::MAX
    }

    pub fn send(&self,
                data: &[u8],
                ports: Vec<OsIpcChannel>,
                shared_memory_regions: Vec<OsIpcSharedMemory>)
                -> Result<(),MpscError>
    {
        match self.sender.lock().unwrap().send(MpscChannelMessage(data.to_vec(), ports, shared_memory_regions)) {
            Err(_) => Err(MpscError::ChannelClosedError),
            Ok(_) => Ok(()),
        }
    }
}

pub struct OsIpcReceiverSet {
    last_index: usize,
    receiver_ids: Vec<usize>,
    receivers: Vec<OsIpcReceiver>,
}

impl OsIpcReceiverSet {
    pub fn new() -> Result<OsIpcReceiverSet,MpscError> {
        Ok(OsIpcReceiverSet {
            last_index: 0,
            receiver_ids: vec![],
            receivers: vec![],
        })
    }

    pub fn add(&mut self, receiver: OsIpcReceiver) -> Result<i64,MpscError> {
        self.last_index += 1;
        self.receiver_ids.push(self.last_index);
        self.receivers.push(receiver.consume());
        Ok(self.last_index as i64)
    }

    pub fn select(&mut self) -> Result<Vec<OsIpcSelectionResult>,MpscError> {
        let mut receivers: Vec<Option<mpsc::Receiver<MpscChannelMessage>>> = Vec::with_capacity(self.receivers.len());
        let mut r_id: i64 = -1;
        let mut r_index: usize = 0;

        {
            let select = mpsc::Select::new();
            // we *must* allocate exact capacity for this, because the Handles *can't move*
            let mut handles: Vec<mpsc::Handle<MpscChannelMessage>> = Vec::with_capacity(self.receivers.len());

            for r in &self.receivers {
                let inner_r = mem::replace(&mut *r.receiver.borrow_mut(), None);
                receivers.push(inner_r);
            }

            for r in &receivers {
                unsafe {
                    handles.push(select.handle(r.as_ref().unwrap()));
                    handles.last_mut().unwrap().add();
                }
            }

            let id = select.wait();

            for (index,h) in handles.iter().enumerate() {
                if h.id() == id {
                    r_index = index;
                    r_id = self.receiver_ids[index] as i64;
                    break;
                }
            }
        }

        // put the receivers back
        for (index,r) in self.receivers.iter().enumerate() {
            mem::replace(&mut *r.receiver.borrow_mut(), mem::replace(&mut receivers[index], None));
        }

        if r_id == -1 {
            return Err(MpscError::UnknownError);
        }

        let receivers = &mut self.receivers;
        match receivers[r_index].recv() {
            Ok((data, channels, shmems)) =>
                Ok(vec![OsIpcSelectionResult::DataReceived(r_id, data, channels, shmems)]),
            Err(MpscError::ChannelClosedError) => {
                receivers.remove(r_index);
                self.receiver_ids.remove(r_index);
                Ok(vec![OsIpcSelectionResult::ChannelClosed(r_id)])
            },
            Err(err) => Err(err),
        }
    }
}

pub enum OsIpcSelectionResult {
    DataReceived(i64, Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),
    ChannelClosed(i64),
}

impl OsIpcSelectionResult {
    pub fn unwrap(self) -> (i64, Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>) {
        match self {
            OsIpcSelectionResult::DataReceived(id, data, channels, shared_memory_regions) => {
                (id, data, channels, shared_memory_regions)
            }
            OsIpcSelectionResult::ChannelClosed(id) => {
                panic!("OsIpcSelectionResult::unwrap(): receiver ID {} was closed!", id)
            }
        }
    }
}

pub struct OsIpcOneShotServer {
    receiver: RefCell<Option<OsIpcReceiver>>,
    name: String,
}

impl OsIpcOneShotServer {
    pub fn new() -> Result<(OsIpcOneShotServer, String),MpscError> {
        let (sender, receiver) = match channel() {
            Ok((s,r)) => (s,r),
            Err(err) => return Err(err),
        };

        let name = Uuid::new_v4().to_string();
        let record = ServerRecord::new(sender);
        ONE_SHOT_SERVERS.lock().unwrap().insert(name.clone(), record);
        Ok((OsIpcOneShotServer {
            receiver: RefCell::new(Some(receiver)),
            name: name.clone(),
        },name.clone()))
    }

    pub fn accept(&self) -> Result<(OsIpcReceiver,
                                    Vec<u8>,
                                    Vec<OsOpaqueIpcChannel>,
                                    Vec<OsIpcSharedMemory>),MpscError>
    {
        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&self.name).unwrap().clone();
        record.accept();
        ONE_SHOT_SERVERS.lock().unwrap().remove(&self.name).unwrap();
        let receiver = self.receiver.borrow_mut().take().unwrap();
        let (data, channels, shmems) = receiver.recv().unwrap();
        Ok((receiver, data, channels, shmems))
    }
}

#[derive(PartialEq, Debug)]
pub enum OsIpcChannel {
    Sender(OsIpcSender),
    Receiver(OsIpcReceiver),
}

#[derive(PartialEq, Debug)]
pub struct OsOpaqueIpcChannel {
    channel: RefCell<Option<OsIpcChannel>>,
}

impl OsOpaqueIpcChannel {
    fn new(channel: OsIpcChannel) -> OsOpaqueIpcChannel {
        OsOpaqueIpcChannel {
            channel: RefCell::new(Some(channel))
        }
    }

    pub fn to_receiver(&self) -> OsIpcReceiver {
        match self.channel.borrow_mut().take().unwrap() {
            OsIpcChannel::Sender(_) => panic!("Opaque channel is not a receiver!"),
            OsIpcChannel::Receiver(r) => r
        }
    }

    pub fn to_sender(&mut self) -> OsIpcSender {
        match self.channel.borrow_mut().take().unwrap() {
            OsIpcChannel::Sender(s) => s,
            OsIpcChannel::Receiver(_) => panic!("Opaque channel is not a sender!"),
        }
    }
}

pub struct OsIpcSharedMemory {
    ptr: *mut u8,
    length: usize,
    data: Arc<Vec<u8>>,
}

unsafe impl Send for OsIpcSharedMemory {}
unsafe impl Sync for OsIpcSharedMemory {}

impl Clone for OsIpcSharedMemory {
    fn clone(&self) -> OsIpcSharedMemory {
        OsIpcSharedMemory {
            ptr: self.ptr,
            length: self.length,
            data: self.data.clone(),
        }
    }
}

impl PartialEq for OsIpcSharedMemory {
    fn eq(&self, other: &OsIpcSharedMemory) -> bool {
        **self == **other
    }
}

impl Debug for OsIpcSharedMemory {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        (**self).fmt(formatter)
    }
}

impl Deref for OsIpcSharedMemory {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        if self.ptr.is_null() {
            panic!("attempted to access a consumed `OsIpcSharedMemory`")
        }
        unsafe {
            slice::from_raw_parts(self.ptr, self.length)
        }
    }
}

impl OsIpcSharedMemory {
    pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory {
        let mut v = Arc::new(vec![byte; length]);
        OsIpcSharedMemory {
            ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(),
            length: length,
            data: v
        }
    }

    pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory {
        let mut v = Arc::new(bytes.to_vec());
        OsIpcSharedMemory {
            ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(),
            length: v.len(),
            data: v
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum MpscError {
    ChannelClosedError,
    UnknownError,
}

impl MpscError {
    #[allow(dead_code)]
    pub fn channel_is_closed(&self) -> bool {
        *self == MpscError::ChannelClosedError
    }
}

impl From<MpscError> for DeserializeError {
    fn from(mpsc_error: MpscError) -> DeserializeError {
        DeserializeError::IoError(mpsc_error.into())
    }
}

impl From<MpscError> for Error {
    fn from(mpsc_error: MpscError) -> Error {
        match mpsc_error {
            MpscError::ChannelClosedError => {
                Error::new(ErrorKind::BrokenPipe, "MPSC channel closed")
            }
            MpscError::UnknownError => Error::new(ErrorKind::Other, "Other MPSC channel error"),
        }
    }
}

<commit_msg>Cleanup: inprocess: Use `try!` rather than manual `match`<commit_after>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use bincode::serde::DeserializeError;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::collections::hash_map::HashMap;
use std::cell::{RefCell};
use std::io::{Error, ErrorKind};
use std::slice;
use std::fmt::{self, Debug, Formatter};
use std::cmp::{PartialEq};
use std::ops::Deref;
use std::mem;
use std::usize;

use uuid::Uuid;

#[derive(Clone)]
struct ServerRecord {
    sender: OsIpcSender,
    conn_sender: mpsc::Sender<bool>,
    conn_receiver: Arc<Mutex<mpsc::Receiver<bool>>>,
}

impl ServerRecord {
    fn new(sender: OsIpcSender) -> ServerRecord {
        let (tx, rx) = mpsc::channel::<bool>();
        ServerRecord {
            sender: sender,
            conn_sender: tx,
            conn_receiver: Arc::new(Mutex::new(rx)),
        }
    }

    fn accept(&self) {
        self.conn_receiver.lock().unwrap().recv().unwrap();
    }

    fn connect(&self) {
        self.conn_sender.send(true).unwrap();
    }
}

lazy_static! {
    static ref ONE_SHOT_SERVERS: Mutex<HashMap<String,ServerRecord>> = Mutex::new(HashMap::new());
}

struct MpscChannelMessage(Vec<u8>, Vec<OsIpcChannel>, Vec<OsIpcSharedMemory>);

pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver),MpscError> {
    let (base_sender, base_receiver) = mpsc::channel::<MpscChannelMessage>();
    Ok((OsIpcSender::new(base_sender), OsIpcReceiver::new(base_receiver)))
}

#[derive(Debug)]
pub struct OsIpcReceiver {
    receiver: RefCell<Option<mpsc::Receiver<MpscChannelMessage>>>,
}

impl PartialEq for OsIpcReceiver {
    fn eq(&self, other: &OsIpcReceiver) -> bool {
        self.receiver.borrow().as_ref().map(|rx| rx as *const _) ==
            other.receiver.borrow().as_ref().map(|rx| rx as *const _)
    }
}

impl OsIpcReceiver {
    fn new(receiver: mpsc::Receiver<MpscChannelMessage>) -> OsIpcReceiver {
        OsIpcReceiver {
            receiver: RefCell::new(Some(receiver)),
        }
    }

    pub fn consume(&self) -> OsIpcReceiver {
        let receiver = self.receiver.borrow_mut().take();
        OsIpcReceiver::new(receiver.unwrap())
    }

    pub fn recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
        let r = self.receiver.borrow();
        match r.as_ref().unwrap().recv() {
            Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                 c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                 s)),
            Err(_) => Err(MpscError::ChannelClosedError),
        }
    }

    pub fn try_recv(&self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),MpscError> {
        let r = self.receiver.borrow();
        match r.as_ref().unwrap().try_recv() {
            Ok(MpscChannelMessage(d,c,s)) => Ok((d,
                                                 c.into_iter().map(OsOpaqueIpcChannel::new).collect(),
                                                 s)),
            Err(_) => Err(MpscError::ChannelClosedError),
        }
    }
}

#[derive(Clone, Debug)]
pub struct OsIpcSender {
    sender: Arc<Mutex<mpsc::Sender<MpscChannelMessage>>>,
}

impl PartialEq for OsIpcSender {
    fn eq(&self, other: &OsIpcSender) -> bool {
        &*self.sender.lock().unwrap() as *const _ ==
            &*other.sender.lock().unwrap() as *const _
    }
}

impl OsIpcSender {
    fn new(sender: mpsc::Sender<MpscChannelMessage>) -> OsIpcSender {
        OsIpcSender {
            sender: Arc::new(Mutex::new(sender)),
        }
    }

    pub fn connect(name: String) -> Result<OsIpcSender,MpscError> {
        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&name).unwrap().clone();
        record.connect();
        Ok(record.sender)
    }

    pub fn get_max_fragment_size() -> usize {
        usize::MAX
    }

    pub fn send(&self,
                data: &[u8],
                ports: Vec<OsIpcChannel>,
                shared_memory_regions: Vec<OsIpcSharedMemory>)
                -> Result<(),MpscError>
    {
        match self.sender.lock().unwrap().send(MpscChannelMessage(data.to_vec(), ports, shared_memory_regions)) {
            Err(_) => Err(MpscError::ChannelClosedError),
            Ok(_) => Ok(()),
        }
    }
}

pub struct OsIpcReceiverSet {
    last_index: usize,
    receiver_ids: Vec<usize>,
    receivers: Vec<OsIpcReceiver>,
}

impl OsIpcReceiverSet {
    pub fn new() -> Result<OsIpcReceiverSet,MpscError> {
        Ok(OsIpcReceiverSet {
            last_index: 0,
            receiver_ids: vec![],
            receivers: vec![],
        })
    }

    pub fn add(&mut self, receiver: OsIpcReceiver) -> Result<i64,MpscError> {
        self.last_index += 1;
        self.receiver_ids.push(self.last_index);
        self.receivers.push(receiver.consume());
        Ok(self.last_index as i64)
    }

    pub fn select(&mut self) -> Result<Vec<OsIpcSelectionResult>,MpscError> {
        let mut receivers: Vec<Option<mpsc::Receiver<MpscChannelMessage>>> = Vec::with_capacity(self.receivers.len());
        let mut r_id: i64 = -1;
        let mut r_index: usize = 0;

        {
            let select = mpsc::Select::new();
            // we *must* allocate exact capacity for this, because the Handles *can't move*
            let mut handles: Vec<mpsc::Handle<MpscChannelMessage>> = Vec::with_capacity(self.receivers.len());

            for r in &self.receivers {
                let inner_r = mem::replace(&mut *r.receiver.borrow_mut(), None);
                receivers.push(inner_r);
            }

            for r in &receivers {
                unsafe {
                    handles.push(select.handle(r.as_ref().unwrap()));
                    handles.last_mut().unwrap().add();
                }
            }

            let id = select.wait();

            for (index,h) in handles.iter().enumerate() {
                if h.id() == id {
                    r_index = index;
                    r_id = self.receiver_ids[index] as i64;
                    break;
                }
            }
        }

        // put the receivers back
        for (index,r) in self.receivers.iter().enumerate() {
            mem::replace(&mut *r.receiver.borrow_mut(), mem::replace(&mut receivers[index], None));
        }

        if r_id == -1 {
            return Err(MpscError::UnknownError);
        }

        let receivers = &mut self.receivers;
        match receivers[r_index].recv() {
            Ok((data, channels, shmems)) =>
                Ok(vec![OsIpcSelectionResult::DataReceived(r_id, data, channels, shmems)]),
            Err(MpscError::ChannelClosedError) => {
                receivers.remove(r_index);
                self.receiver_ids.remove(r_index);
                Ok(vec![OsIpcSelectionResult::ChannelClosed(r_id)])
            },
            Err(err) => Err(err),
        }
    }
}

pub enum OsIpcSelectionResult {
    DataReceived(i64, Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),
    ChannelClosed(i64),
}

impl OsIpcSelectionResult {
    pub fn unwrap(self) -> (i64, Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>) {
        match self {
            OsIpcSelectionResult::DataReceived(id, data, channels, shared_memory_regions) => {
                (id, data, channels, shared_memory_regions)
            }
            OsIpcSelectionResult::ChannelClosed(id) => {
                panic!("OsIpcSelectionResult::unwrap(): receiver ID {} was closed!", id)
            }
        }
    }
}

pub struct OsIpcOneShotServer {
    receiver: RefCell<Option<OsIpcReceiver>>,
    name: String,
}

impl OsIpcOneShotServer {
    pub fn new() -> Result<(OsIpcOneShotServer, String),MpscError> {
        let (sender, receiver) = try!(channel());

        let name = Uuid::new_v4().to_string();
        let record = ServerRecord::new(sender);
        ONE_SHOT_SERVERS.lock().unwrap().insert(name.clone(), record);
        Ok((OsIpcOneShotServer {
            receiver: RefCell::new(Some(receiver)),
            name: name.clone(),
        },name.clone()))
    }

    pub fn accept(&self) -> Result<(OsIpcReceiver,
                                    Vec<u8>,
                                    Vec<OsOpaqueIpcChannel>,
                                    Vec<OsIpcSharedMemory>),MpscError>
    {
        let record = ONE_SHOT_SERVERS.lock().unwrap().get(&self.name).unwrap().clone();
        record.accept();
        ONE_SHOT_SERVERS.lock().unwrap().remove(&self.name).unwrap();
        let receiver = self.receiver.borrow_mut().take().unwrap();
        let (data, channels, shmems) = receiver.recv().unwrap();
        Ok((receiver, data, channels, shmems))
    }
}

#[derive(PartialEq, Debug)]
pub enum OsIpcChannel {
    Sender(OsIpcSender),
    Receiver(OsIpcReceiver),
}

#[derive(PartialEq, Debug)]
pub struct OsOpaqueIpcChannel {
    channel: RefCell<Option<OsIpcChannel>>,
}

impl OsOpaqueIpcChannel {
    fn new(channel: OsIpcChannel) -> OsOpaqueIpcChannel {
        OsOpaqueIpcChannel {
            channel: RefCell::new(Some(channel))
        }
    }

    pub fn to_receiver(&self) -> OsIpcReceiver {
        match self.channel.borrow_mut().take().unwrap() {
            OsIpcChannel::Sender(_) => panic!("Opaque channel is not a receiver!"),
            OsIpcChannel::Receiver(r) => r
        }
    }

    pub fn to_sender(&mut self) -> OsIpcSender {
        match self.channel.borrow_mut().take().unwrap() {
            OsIpcChannel::Sender(s) => s,
            OsIpcChannel::Receiver(_) => panic!("Opaque channel is not a sender!"),
        }
    }
}

pub struct OsIpcSharedMemory {
    ptr: *mut u8,
    length: usize,
    data: Arc<Vec<u8>>,
}

unsafe impl Send for OsIpcSharedMemory {}
unsafe impl Sync for OsIpcSharedMemory {}

impl Clone for OsIpcSharedMemory {
    fn clone(&self) -> OsIpcSharedMemory {
        OsIpcSharedMemory {
            ptr: self.ptr,
            length: self.length,
            data: self.data.clone(),
        }
    }
}

impl PartialEq for OsIpcSharedMemory {
    fn eq(&self, other: &OsIpcSharedMemory) -> bool {
        **self == **other
    }
}

impl Debug for OsIpcSharedMemory {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        (**self).fmt(formatter)
    }
}

impl Deref for OsIpcSharedMemory {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        if self.ptr.is_null() {
            panic!("attempted to access a consumed `OsIpcSharedMemory`")
        }
        unsafe {
            slice::from_raw_parts(self.ptr, self.length)
        }
    }
}

impl OsIpcSharedMemory {
    pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory {
        let mut v = Arc::new(vec![byte; length]);
        OsIpcSharedMemory {
            ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(),
            length: length,
            data: v
        }
    }

    pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory {
        let mut v = Arc::new(bytes.to_vec());
        OsIpcSharedMemory {
            ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(),
            length: v.len(),
            data: v
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum MpscError {
    ChannelClosedError,
    UnknownError,
}

impl MpscError {
    #[allow(dead_code)]
    pub fn channel_is_closed(&self) -> bool {
        *self == MpscError::ChannelClosedError
    }
}

impl From<MpscError> for DeserializeError {
    fn from(mpsc_error: MpscError) -> DeserializeError {
        DeserializeError::IoError(mpsc_error.into())
    }
}

impl From<MpscError> for Error {
    fn from(mpsc_error: MpscError) -> Error {
        match mpsc_error {
            MpscError::ChannelClosedError => {
                Error::new(ErrorKind::BrokenPipe, "MPSC channel closed")
            }
            MpscError::UnknownError => Error::new(ErrorKind::Other, "Other MPSC channel error"),
        }
    }
}

<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

#[crate_type = "bin"];

#[allow(non_camel_case_types)];
#[allow(unrecognized_lint)]; // NOTE: remove after snapshot
#[deny(warnings)];

extern mod extra;

use std::os;
use std::f64;

use extra::getopts;
use extra::getopts::groups::{optopt, optflag, reqopt};
use extra::test;

use common::config;
use common::mode_run_pass;
use common::mode_run_fail;
use common::mode_compile_fail;
use common::mode_pretty;
use common::mode_debug_info;
use common::mode_codegen;
use common::mode;
use util::logv;

pub mod procsrv;
pub mod util;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;

pub fn main() {
    let args = os::args();
    let config = parse_config(args);
    log_config(&config);
    run_tests(&config);
}

pub fn parse_config(args: ~[~str]) -> config {

    let groups : ~[getopts::groups::OptGroup] =
        ~[reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
          reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
          reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
          optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
          optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
          reqopt("", "src-base", "directory to scan for test files", "PATH"),
          reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
          reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
          reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
          reqopt("", "mode", "which sort of compile tests to run",
                 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
          optflag("", "ignored", "run tests marked as ignored / xfailed"),
          optopt("", "runtool", "supervisor program to run tests under \
                                 (eg. emulator, valgrind)", "PROGRAM"),
          optopt("", "rustcflags", "flags to pass to rustc", "FLAGS"),
          optflag("", "verbose", "run tests verbosely, showing all output"),
          optopt("", "logfile", "file to log test execution to", "FILE"),
          optopt("", "save-metrics", "file to save metrics to", "FILE"),
          optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
          optopt("", "ratchet-noise-percent",
                 "percent change in metrics to consider noise", "N"),
          optflag("", "jit", "run tests under the JIT"),
          optflag("", "newrt", "run tests on the new runtime / scheduler"),
          optopt("", "target", "the target to build for", "TARGET"),
          optopt("", "adb-path", "path to the android debugger", "PATH"),
          optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
          optflag("h", "help", "show this message"),
         ];

    assert!(!args.is_empty());
    let argv0 = args[0].clone();
    let args_ = args.tail();
    if args[1] == ~"-h" || args[1] == ~"--help" {
        let message = fmt!("Usage: %s [OPTIONS] [TESTNAME...]", argv0);
        println(getopts::groups::usage(message, groups));
        fail!()
    }

    let matches =
        &match getopts::groups::getopts(args_, groups) {
          Ok(m) => m,
          Err(f) => fail!(getopts::fail_str(f))
        };

    if getopts::opt_present(matches, "h") || getopts::opt_present(matches, "help") {
        let message = fmt!("Usage: %s [OPTIONS]  [TESTNAME...]", argv0);
        println(getopts::groups::usage(message, groups));
        fail!()
    }

    fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
        Path(getopts::opt_str(m, nm))
    }

    config {
        compile_lib_path: getopts::opt_str(matches, "compile-lib-path"),
        run_lib_path: getopts::opt_str(matches, "run-lib-path"),
        rustc_path: opt_path(matches, "rustc-path"),
        clang_path: getopts::opt_maybe_str(matches, "clang-path").map(|s| Path(*s)),
        llvm_bin_path: getopts::opt_maybe_str(matches, "llvm-bin-path").map(|s| Path(*s)),
        src_base: opt_path(matches, "src-base"),
        build_base: opt_path(matches, "build-base"),
        aux_base: opt_path(matches, "aux-base"),
        stage_id: getopts::opt_str(matches, "stage-id"),
        mode: str_mode(getopts::opt_str(matches, "mode")),
        run_ignored: getopts::opt_present(matches, "ignored"),
        filter:
            if !matches.free.is_empty() {
                 Some(matches.free[0].clone())
            } else {
                None
            },
        logfile: getopts::opt_maybe_str(matches, "logfile").map(|s| Path(*s)),
        save_metrics: getopts::opt_maybe_str(matches, "save-metrics").map(|s| Path(*s)),
        ratchet_metrics:
            getopts::opt_maybe_str(matches, "ratchet-metrics").map(|s| Path(*s)),
        ratchet_noise_percent:
            getopts::opt_maybe_str(matches,
                                   "ratchet-noise-percent").map(|s|
                                                                f64::from_str(*s).get()),
        runtool: getopts::opt_maybe_str(matches, "runtool"),
        rustcflags: getopts::opt_maybe_str(matches, "rustcflags"),
        jit: getopts::opt_present(matches, "jit"),
        newrt: getopts::opt_present(matches, "newrt"),
        target: opt_str2(getopts::opt_maybe_str(matches, "target")).to_str(),
        adb_path: opt_str2(getopts::opt_maybe_str(matches, "adb-path")).to_str(),
        adb_test_dir:
            opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")).to_str(),
        adb_device_status:
            if (opt_str2(getopts::opt_maybe_str(matches, "target")) ==
                ~"arm-linux-androideabi") {
                if (opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) !=
                    ~"(none)" &&
                    opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) !=
                    ~"") { true }
                else { false }
            } else { false },
        verbose: getopts::opt_present(matches, "verbose")
    }
}

pub fn log_config(config: &config) {
    let c = config;
    logv(c, fmt!("configuration:"));
    logv(c, fmt!("compile_lib_path: %s", config.compile_lib_path));
    logv(c, fmt!("run_lib_path: %s", config.run_lib_path));
    logv(c, fmt!("rustc_path: %s", config.rustc_path.to_str()));
    logv(c, fmt!("src_base: %s", config.src_base.to_str()));
    logv(c, fmt!("build_base: %s", config.build_base.to_str()));
    logv(c, fmt!("stage_id: %s", config.stage_id));
    logv(c, fmt!("mode: %s", mode_str(config.mode)));
    logv(c, fmt!("run_ignored: %b", config.run_ignored));
    logv(c, fmt!("filter: %s", opt_str(&config.filter)));
    logv(c, fmt!("runtool: %s", opt_str(&config.runtool)));
    logv(c, fmt!("rustcflags: %s", opt_str(&config.rustcflags)));
    logv(c, fmt!("jit: %b", config.jit));
    logv(c, fmt!("newrt: %b", config.newrt));
    logv(c, fmt!("target: %s", config.target));
    logv(c, fmt!("adb_path: %s", config.adb_path));
    logv(c, fmt!("adb_test_dir: %s", config.adb_test_dir));
    logv(c, fmt!("adb_device_status: %b", config.adb_device_status));
    logv(c, fmt!("verbose: %b", config.verbose));
    logv(c, fmt!("\n"));
}

pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
    match *maybestr {
        None => "(none)",
        Some(ref s) => {
            let s: &'a str = *s;
            s
        }
    }
}

pub fn opt_str2(maybestr: Option<~str>) -> ~str {
    match maybestr { None => ~"(none)", Some(s) => { s } }
}

pub fn str_opt(maybestr: ~str) -> Option<~str> {
    if maybestr != ~"(none)" { Some(maybestr) } else { None }
}

pub fn str_mode(s: ~str) -> mode {
    match s {
      ~"compile-fail" => mode_compile_fail,
      ~"run-fail" => mode_run_fail,
      ~"run-pass" => mode_run_pass,
      ~"pretty" => mode_pretty,
      ~"debug-info" => mode_debug_info,
      ~"codegen" => mode_codegen,
      _ => fail!("invalid mode")
    }
}

pub fn mode_str(mode: mode) -> ~str {
    match mode {
      mode_compile_fail => ~"compile-fail",
      mode_run_fail => ~"run-fail",
      mode_run_pass => ~"run-pass",
      mode_pretty => ~"pretty",
      mode_debug_info => ~"debug-info",
      mode_codegen => ~"codegen",
    }
}

pub fn run_tests(config: &config) {
    let opts = test_opts(config);
    let tests = make_tests(config);
    let res = test::run_tests_console(&opts, tests);
    if !res { fail!("Some tests failed"); }
}

pub fn test_opts(config: &config) -> test::TestOpts {
    test::TestOpts {
        filter: config.filter.clone(),
        run_ignored: config.run_ignored,
        logfile: config.logfile.clone(),
        run_tests: true,
        run_benchmarks: true,
        ratchet_metrics: copy config.ratchet_metrics,
        ratchet_noise_percent: copy config.ratchet_noise_percent,
        save_metrics: copy config.save_metrics,
    }
}

pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] {
    debug!("making tests from %s",
           config.src_base.to_str());
    let mut tests = ~[];
    let dirs = os::list_dir_path(&config.src_base);
    for dirs.iter().advance |file| {
        let file = (*file).clone();
        debug!("inspecting file %s", file.to_str());
        if is_test(config, file) {
            let t = do make_test(config, file) {
                match config.mode {
                    mode_codegen => make_metrics_test_closure(config, file),
                    _ => make_test_closure(config, file)
                }
            };
            tests.push(t)
        }
    }
    tests
}

pub fn is_test(config: &config, testfile: &Path) -> bool {
    // Pretty-printer does not work with .rc files yet
    let valid_extensions =
        match config.mode {
          mode_pretty => ~[~".rs"],
          _ => ~[~".rc", ~".rs"]
        };
    let invalid_prefixes = ~[~".", ~"#", ~"~"];
    let name = testfile.filename().get();

    let mut valid = false;

    for valid_extensions.iter().advance |ext| {
        if name.ends_with(*ext) { valid = true; }
    }

    for invalid_prefixes.iter().advance |pre| {
        if name.starts_with(*pre) { valid = false; }
    }

    return valid;
}

pub fn make_test(config: &config, testfile: &Path,
                 f: &fn()->test::TestFn) -> test::TestDescAndFn {
    test::TestDescAndFn {
        desc: test::TestDesc {
            name: make_test_name(config, testfile),
            ignore: header::is_test_ignored(config, testfile),
            should_fail: false
        },
        testfn: f(),
    }
}

pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {

    // Try to elide redundant long paths
    fn shorten(path: &Path) -> ~str {
        let filename = path.filename();
        let dir = path.pop().filename();
        fmt!("%s/%s", dir.get_or_default(~""), filename.get_or_default(~""))
    }

    test::DynTestName(fmt!("[%s] %s",
                           mode_str(config.mode),
                           shorten(testfile)))
}

pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn {
    use std::cell::Cell;
    let config = Cell::new((*config).clone());
    let testfile = Cell::new(testfile.to_str());
    test::DynTestFn(|| { runtest::run(config.take(), testfile.take()) })
}

pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
    use std::cell::Cell;
    let config = Cell::new(copy *config);
    let testfile = Cell::new(testfile.to_str());
    test::DynMetricFn(|mm| { runtest::run_metrics(config.take(), testfile.take(), mm) })
}
<commit_msg>compiletest: Remove stray copies.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

#[crate_type = "bin"];

#[allow(non_camel_case_types)];
#[allow(unrecognized_lint)]; // NOTE: remove after snapshot
#[deny(warnings)];

extern mod extra;

use std::os;
use std::f64;

use extra::getopts;
use extra::getopts::groups::{optopt, optflag, reqopt};
use extra::test;

use common::config;
use common::mode_run_pass;
use common::mode_run_fail;
use common::mode_compile_fail;
use common::mode_pretty;
use common::mode_debug_info;
use common::mode_codegen;
use common::mode;
use util::logv;

pub mod procsrv;
pub mod util;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;

pub fn main() {
    let args = os::args();
    let config = parse_config(args);
    log_config(&config);
    run_tests(&config);
}

pub fn parse_config(args: ~[~str]) -> config {

    let groups : ~[getopts::groups::OptGroup] =
        ~[reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
          reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
          reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
          optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
          optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
          reqopt("", "src-base", "directory to scan for test files", "PATH"),
          reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
          reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
          reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
          reqopt("", "mode", "which sort of compile tests to run",
                 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
          optflag("", "ignored", "run tests marked as ignored / xfailed"),
          optopt("", "runtool", "supervisor program to run tests under \
                                 (eg. emulator, valgrind)", "PROGRAM"),
          optopt("", "rustcflags", "flags to pass to rustc", "FLAGS"),
          optflag("", "verbose", "run tests verbosely, showing all output"),
          optopt("", "logfile", "file to log test execution to", "FILE"),
          optopt("", "save-metrics", "file to save metrics to", "FILE"),
          optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
          optopt("", "ratchet-noise-percent",
                 "percent change in metrics to consider noise", "N"),
          optflag("", "jit", "run tests under the JIT"),
          optflag("", "newrt", "run tests on the new runtime / scheduler"),
          optopt("", "target", "the target to build for", "TARGET"),
          optopt("", "adb-path", "path to the android debugger", "PATH"),
          optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
          optflag("h", "help", "show this message"),
         ];

    assert!(!args.is_empty());
    let argv0 = args[0].clone();
    let args_ = args.tail();
    if args[1] == ~"-h" || args[1] == ~"--help" {
        let message = fmt!("Usage: %s [OPTIONS] [TESTNAME...]", argv0);
        println(getopts::groups::usage(message, groups));
        fail!()
    }

    let matches =
        &match getopts::groups::getopts(args_, groups) {
          Ok(m) => m,
          Err(f) => fail!(getopts::fail_str(f))
        };

    if getopts::opt_present(matches, "h") || getopts::opt_present(matches, "help") {
        let message = fmt!("Usage: %s [OPTIONS]  [TESTNAME...]", argv0);
        println(getopts::groups::usage(message, groups));
        fail!()
    }

    fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
        Path(getopts::opt_str(m, nm))
    }

    config {
        compile_lib_path: getopts::opt_str(matches, "compile-lib-path"),
        run_lib_path: getopts::opt_str(matches, "run-lib-path"),
        rustc_path: opt_path(matches, "rustc-path"),
        clang_path: getopts::opt_maybe_str(matches, "clang-path").map(|s| Path(*s)),
        llvm_bin_path: getopts::opt_maybe_str(matches, "llvm-bin-path").map(|s| Path(*s)),
        src_base: opt_path(matches, "src-base"),
        build_base: opt_path(matches, "build-base"),
        aux_base: opt_path(matches, "aux-base"),
        stage_id: getopts::opt_str(matches, "stage-id"),
        mode: str_mode(getopts::opt_str(matches, "mode")),
        run_ignored: getopts::opt_present(matches, "ignored"),
        filter:
            if !matches.free.is_empty() {
                 Some(matches.free[0].clone())
            } else {
                None
            },
        logfile: getopts::opt_maybe_str(matches, "logfile").map(|s| Path(*s)),
        save_metrics: getopts::opt_maybe_str(matches, "save-metrics").map(|s| Path(*s)),
        ratchet_metrics:
            getopts::opt_maybe_str(matches, "ratchet-metrics").map(|s| Path(*s)),
        ratchet_noise_percent:
            getopts::opt_maybe_str(matches,
                                   "ratchet-noise-percent").map(|s|
                                                                f64::from_str(*s).get()),
        runtool: getopts::opt_maybe_str(matches, "runtool"),
        rustcflags: getopts::opt_maybe_str(matches, "rustcflags"),
        jit: getopts::opt_present(matches, "jit"),
        newrt: getopts::opt_present(matches, "newrt"),
        target: opt_str2(getopts::opt_maybe_str(matches, "target")).to_str(),
        adb_path: opt_str2(getopts::opt_maybe_str(matches, "adb-path")).to_str(),
        adb_test_dir:
            opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")).to_str(),
        adb_device_status:
            if (opt_str2(getopts::opt_maybe_str(matches, "target")) ==
                ~"arm-linux-androideabi") {
                if (opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) !=
                    ~"(none)" &&
                    opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) !=
                    ~"") { true }
                else { false }
            } else { false },
        verbose: getopts::opt_present(matches, "verbose")
    }
}

pub fn log_config(config: &config) {
    let c = config;
    logv(c, fmt!("configuration:"));
    logv(c, fmt!("compile_lib_path: %s", config.compile_lib_path));
    logv(c, fmt!("run_lib_path: %s", config.run_lib_path));
    logv(c, fmt!("rustc_path: %s", config.rustc_path.to_str()));
    logv(c, fmt!("src_base: %s", config.src_base.to_str()));
    logv(c, fmt!("build_base: %s", config.build_base.to_str()));
    logv(c, fmt!("stage_id: %s", config.stage_id));
    logv(c, fmt!("mode: %s", mode_str(config.mode)));
    logv(c, fmt!("run_ignored: %b", config.run_ignored));
    logv(c, fmt!("filter: %s", opt_str(&config.filter)));
    logv(c, fmt!("runtool: %s", opt_str(&config.runtool)));
    logv(c, fmt!("rustcflags: %s", opt_str(&config.rustcflags)));
    logv(c, fmt!("jit: %b", config.jit));
    logv(c, fmt!("newrt: %b", config.newrt));
    logv(c, fmt!("target: %s", config.target));
    logv(c, fmt!("adb_path: %s", config.adb_path));
    logv(c, fmt!("adb_test_dir: %s", config.adb_test_dir));
    logv(c, fmt!("adb_device_status: %b", config.adb_device_status));
    logv(c, fmt!("verbose: %b", config.verbose));
    logv(c, fmt!("\n"));
}

pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
    match *maybestr {
        None => "(none)",
        Some(ref s) => {
            let s: &'a str = *s;
            s
        }
    }
}

pub fn opt_str2(maybestr: Option<~str>) -> ~str {
    match maybestr { None => ~"(none)", Some(s) => { s } }
}

pub fn str_opt(maybestr: ~str) -> Option<~str> {
    if maybestr != ~"(none)" { Some(maybestr) } else { None }
}

pub fn str_mode(s: ~str) -> mode {
    match s {
      ~"compile-fail" => mode_compile_fail,
      ~"run-fail" => mode_run_fail,
      ~"run-pass" => mode_run_pass,
      ~"pretty" => mode_pretty,
      ~"debug-info" => mode_debug_info,
      ~"codegen" => mode_codegen,
      _ => fail!("invalid mode")
    }
}

pub fn mode_str(mode: mode) -> ~str {
    match mode {
      mode_compile_fail => ~"compile-fail",
      mode_run_fail => ~"run-fail",
      mode_run_pass => ~"run-pass",
      mode_pretty => ~"pretty",
      mode_debug_info => ~"debug-info",
      mode_codegen => ~"codegen",
    }
}

pub fn run_tests(config: &config) {
    let opts = test_opts(config);
    let tests = make_tests(config);
    let res = test::run_tests_console(&opts, tests);
    if !res { fail!("Some tests failed"); }
}

pub fn test_opts(config: &config) -> test::TestOpts {
    test::TestOpts {
        filter: config.filter.clone(),
        run_ignored: config.run_ignored,
        logfile: config.logfile.clone(),
        run_tests: true,
        run_benchmarks: true,
        ratchet_metrics: config.ratchet_metrics.clone(),
        ratchet_noise_percent: config.ratchet_noise_percent.clone(),
        save_metrics: config.save_metrics.clone(),
    }
}

pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] {
    debug!("making tests from %s",
           config.src_base.to_str());
    let mut tests = ~[];
    let dirs = os::list_dir_path(&config.src_base);
    for dirs.iter().advance |file| {
        let file = (*file).clone();
        debug!("inspecting file %s", file.to_str());
        if is_test(config, file) {
            let t = do make_test(config, file) {
                match config.mode {
                    mode_codegen => make_metrics_test_closure(config, file),
                    _ => make_test_closure(config, file)
                }
            };
            tests.push(t)
        }
    }
    tests
}

pub fn is_test(config: &config, testfile: &Path) -> bool {
    // Pretty-printer does not work with .rc files yet
    let valid_extensions =
        match config.mode {
          mode_pretty => ~[~".rs"],
          _ => ~[~".rc", ~".rs"]
        };
    let invalid_prefixes = ~[~".", ~"#", ~"~"];
    let name = testfile.filename().get();

    let mut valid = false;

    for valid_extensions.iter().advance |ext| {
        if name.ends_with(*ext) { valid = true; }
    }

    for invalid_prefixes.iter().advance |pre| {
        if name.starts_with(*pre) { valid = false; }
    }

    return valid;
}

pub fn make_test(config: &config, testfile: &Path,
                 f: &fn()->test::TestFn) -> test::TestDescAndFn {
    test::TestDescAndFn {
        desc: test::TestDesc {
            name: make_test_name(config, testfile),
            ignore: header::is_test_ignored(config, testfile),
            should_fail: false
        },
        testfn: f(),
    }
}

pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {

    // Try to elide redundant long paths
    fn shorten(path: &Path) -> ~str {
        let filename = path.filename();
        let dir = path.pop().filename();
        fmt!("%s/%s", dir.get_or_default(~""), filename.get_or_default(~""))
    }

    test::DynTestName(fmt!("[%s] %s",
                           mode_str(config.mode),
                           shorten(testfile)))
}

pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn {
    use std::cell::Cell;
    let config = Cell::new((*config).clone());
    let testfile = Cell::new(testfile.to_str());
    test::DynTestFn(|| { runtest::run(config.take(), testfile.take()) })
}

pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
    use std::cell::Cell;
    let config = Cell::new((*config).clone());
    let testfile = Cell::new(testfile.to_str());
    test::DynMetricFn(|mm| { runtest::run_metrics(config.take(), testfile.take(), mm) })
}
<|endoftext|>


<commit_before>use conduit::{Handler, Method};

use cargo_registry::category::{Category, EncodableCategory, EncodableCategoryWithSubcategories};

#[derive(Deserialize)]
struct CategoryList {
    categories: Vec<EncodableCategory>,
    meta: CategoryMeta,
}
#[derive(Deserialize)]
struct CategoryMeta {
    total: i32,
}
#[derive(Deserialize)]
struct GoodCategory {
    category: EncodableCategory,
}
#[derive(Deserialize)]
struct CategoryWithSubcategories {
    category: EncodableCategoryWithSubcategories,
}

#[test]
fn index() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories");

    // List 0 categories if none exist
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);
    assert_eq!(json.categories.len(), 0);
    assert_eq!(json.meta.total, 0);

    // Create a category and a subcategory
    {
        let conn = t!(app.diesel_database.get());
        ::new_category("foo", "foo")
            .create_or_update(&conn)
            .unwrap();
        ::new_category("foo::bar", "foo::bar")
            .create_or_update(&conn)
            .unwrap();
    }

    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);

    // Only the top-level categories should be on the page
    assert_eq!(json.categories.len(), 1);
    assert_eq!(json.meta.total, 1);
    assert_eq!(json.categories[0].category, "foo");
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();

    // Return not found if a category doesn't exist
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories/foo-bar");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 404);

    // Create a category and a subcategory
    {
        let conn = t!(app.diesel_database.get());

        t!(::new_category("Foo Bar", "foo-bar").create_or_update(&conn));
        t!(::new_category("Foo Bar::Baz", "foo-bar::baz").create_or_update(&conn));
    }

    // The category and its subcategories should be in the json
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryWithSubcategories = ::json(&mut response);
    assert_eq!(json.category.category, "Foo Bar");
    assert_eq!(json.category.slug, "foo-bar");
    assert_eq!(json.category.subcategories.len(), 1);
    assert_eq!(json.category.subcategories[0].category, "Foo Bar::Baz");
}

#[test]
fn update_crate() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories/foo");
    macro_rules! cnt {
        ($req: expr, $cat: expr) => {{
            $req.with_path(&format!("/api/v1/categories/{}", $cat));
            let mut response = ok_resp!(middle.call($req));
            ::json::<GoodCategory>(&mut response).category.crates_cnt as usize
        }}
    }

    let krate = {
        let conn = t!(app.diesel_database.get());
        let user = t!(::new_user("foo").create_or_update(&conn));
        t!(::new_category("cat1", "cat1").create_or_update(&conn));
        t!(::new_category("Category 2", "category-2").create_or_update(&conn));
        ::CrateBuilder::new("foo_crate", user.id).expect_build(&conn)
    };

    // Updating with no categories has no effect
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Happy path adding one category
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["cat1"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Replacing one category with another
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["category-2"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 1);

    // Removing one category
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Adding 2 categories
    Category::update_crate(
        &app.diesel_database.get().unwrap(),
        &krate,
        &["cat1", "category-2"],
    ).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 1);

    // Removing all categories
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Attempting to add one valid category and one invalid category
    let invalid_categories = Category::update_crate(
        &app.diesel_database.get().unwrap(),
        &krate,
        &["cat1", "catnope"],
    ).unwrap();
    assert_eq!(invalid_categories, vec!["catnope"]);
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Does not add the invalid category to the category list
    // (unlike the behavior of keywords)
    req.with_path("/api/v1/categories");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);
    assert_eq!(json.categories.len(), 2);
    assert_eq!(json.meta.total, 2);

    // Attempting to add a category by display text; must use slug
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["Category 2"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Add a category and its subcategory
    {
        let conn = t!(app.diesel_database.get());
        t!(::new_category("cat1::bar", "cat1::bar").create_or_update(
            &conn,
        ));
        Category::update_crate(&conn, &krate, &["cat1", "cat1::bar"]).unwrap();
    }
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "cat1::bar"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);
}

#[test]
fn category_slugs_returns_all_slugs_in_alphabetical_order() {
    let (_b, app, middle) = ::app();
    {
        let conn = app.diesel_database.get().unwrap();
        ::new_category("Foo", "foo").create_or_update(&conn).unwrap();
        ::new_category("Bar", "bar").create_or_update(&conn).unwrap();
    }

    let mut req = ::req(app, Method::Get, "/api/v1/category_slugs");

    #[derive(Deserialize, Debug, PartialEq)]
    struct Slug {
        id: String,
        slug: String,
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Slugs {
        category_slugs: Vec<Slug>,
    }

    let response = ::json(&mut ok_resp!(middle.call(&mut req)));
    let expected_response = Slugs {
        category_slugs: vec![
            Slug {
                id: "bar".into(),
                slug: "bar".into(),
            },
            Slug {
                id: "foo".into(),
                slug: "foo".into(),
            },
        ],
    };
    assert_eq!(expected_response, response);
}
<commit_msg>Make rustfmt happy<commit_after>use conduit::{Handler, Method};

use cargo_registry::category::{Category, EncodableCategory, EncodableCategoryWithSubcategories};

#[derive(Deserialize)]
struct CategoryList {
    categories: Vec<EncodableCategory>,
    meta: CategoryMeta,
}
#[derive(Deserialize)]
struct CategoryMeta {
    total: i32,
}
#[derive(Deserialize)]
struct GoodCategory {
    category: EncodableCategory,
}
#[derive(Deserialize)]
struct CategoryWithSubcategories {
    category: EncodableCategoryWithSubcategories,
}

#[test]
fn index() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories");

    // List 0 categories if none exist
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);
    assert_eq!(json.categories.len(), 0);
    assert_eq!(json.meta.total, 0);

    // Create a category and a subcategory
    {
        let conn = t!(app.diesel_database.get());
        ::new_category("foo", "foo")
            .create_or_update(&conn)
            .unwrap();
        ::new_category("foo::bar", "foo::bar")
            .create_or_update(&conn)
            .unwrap();
    }

    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);

    // Only the top-level categories should be on the page
    assert_eq!(json.categories.len(), 1);
    assert_eq!(json.meta.total, 1);
    assert_eq!(json.categories[0].category, "foo");
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();

    // Return not found if a category doesn't exist
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories/foo-bar");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 404);

    // Create a category and a subcategory
    {
        let conn = t!(app.diesel_database.get());

        t!(::new_category("Foo Bar", "foo-bar").create_or_update(&conn));
        t!(::new_category("Foo Bar::Baz", "foo-bar::baz").create_or_update(&conn));
    }

    // The category and its subcategories should be in the json
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryWithSubcategories = ::json(&mut response);
    assert_eq!(json.category.category, "Foo Bar");
    assert_eq!(json.category.slug, "foo-bar");
    assert_eq!(json.category.subcategories.len(), 1);
    assert_eq!(json.category.subcategories[0].category, "Foo Bar::Baz");
}

#[test]
fn update_crate() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories/foo");
    macro_rules! cnt {
        ($req: expr, $cat: expr) => {{
            $req.with_path(&format!("/api/v1/categories/{}", $cat));
            let mut response = ok_resp!(middle.call($req));
            ::json::<GoodCategory>(&mut response).category.crates_cnt as usize
        }}
    }

    let krate = {
        let conn = t!(app.diesel_database.get());
        let user = t!(::new_user("foo").create_or_update(&conn));
        t!(::new_category("cat1", "cat1").create_or_update(&conn));
        t!(::new_category("Category 2", "category-2").create_or_update(&conn));
        ::CrateBuilder::new("foo_crate", user.id).expect_build(&conn)
    };

    // Updating with no categories has no effect
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Happy path adding one category
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["cat1"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Replacing one category with another
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["category-2"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 1);

    // Removing one category
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Adding 2 categories
    Category::update_crate(
        &app.diesel_database.get().unwrap(),
        &krate,
        &["cat1", "category-2"],
    ).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 1);

    // Removing all categories
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Attempting to add one valid category and one invalid category
    let invalid_categories = Category::update_crate(
        &app.diesel_database.get().unwrap(),
        &krate,
        &["cat1", "catnope"],
    ).unwrap();
    assert_eq!(invalid_categories, vec!["catnope"]);
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Does not add the invalid category to the category list
    // (unlike the behavior of keywords)
    req.with_path("/api/v1/categories");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: CategoryList = ::json(&mut response);
    assert_eq!(json.categories.len(), 2);
    assert_eq!(json.meta.total, 2);

    // Attempting to add a category by display text; must use slug
    Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["Category 2"]).unwrap();
    assert_eq!(cnt!(&mut req, "cat1"), 0);
    assert_eq!(cnt!(&mut req, "category-2"), 0);

    // Add a category and its subcategory
    {
        let conn = t!(app.diesel_database.get());
        t!(::new_category("cat1::bar", "cat1::bar").create_or_update(
            &conn,
        ));
        Category::update_crate(&conn, &krate, &["cat1", "cat1::bar"]).unwrap();
    }
    assert_eq!(cnt!(&mut req, "cat1"), 1);
    assert_eq!(cnt!(&mut req, "cat1::bar"), 1);
    assert_eq!(cnt!(&mut req, "category-2"), 0);
}

#[test]
fn category_slugs_returns_all_slugs_in_alphabetical_order() {
    let (_b, app, middle) = ::app();
    {
        let conn = app.diesel_database.get().unwrap();
        ::new_category("Foo", "foo")
            .create_or_update(&conn)
            .unwrap();
        ::new_category("Bar", "bar")
            .create_or_update(&conn)
            .unwrap();
    }

    let mut req = ::req(app, Method::Get, "/api/v1/category_slugs");

    #[derive(Deserialize, Debug, PartialEq)]
    struct Slug {
        id: String,
        slug: String,
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Slugs {
        category_slugs: Vec<Slug>,
    }

    let response = ::json(&mut ok_resp!(middle.call(&mut req)));
    let expected_response = Slugs {
        category_slugs: vec![
            Slug {
                id: "bar".into(),
                slug: "bar".into(),
            },
            Slug {
                id: "foo".into(),
                slug: "foo".into(),
            },
        ],
    };
    assert_eq!(expected_response, response);
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};

use toml;

fn search_template_in_dirs<'a>(dirs: &'a [PathBuf], path: &Path) -> Option<&'a PathBuf> {
    dirs.iter().find(|dir| dir.join(path).exists())
}

pub fn get_template_source(tpl_path: &Path) -> String {
    let dirs = template_dirs();
    let path = search_template_in_dirs(&dirs, tpl_path)
        .map(|dir| dir.join(tpl_path))
        .expect(&format!(
            "template file '{}' does not exist",
            tpl_path.to_str().unwrap()
        ));

    let mut f = match File::open(&path) {
        Err(_) => panic!("unable to open template file '{}'", &path.to_str().unwrap()),
        Ok(f) => f,
    };

    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let dirs = template_dirs();
    if let Some(rel) = start_at {
        let root = search_template_in_dirs(&dirs, rel).expect(&format!(
            "unable to find previously available template file '{}'",
            rel.to_str().unwrap()
        ));
        let fs_rel_path = root.join(rel).with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
        }
    }

    let path = Path::new(path);
    search_template_in_dirs(&dirs, &path).expect(&format!(
        "template {:?} not found in directories {:?}",
        path.to_str().unwrap(),
        dirs
    ));
    path.to_owned()
}

pub fn template_dirs() -> Vec<PathBuf> {
    Config::from_file().dirs
}

struct Config {
    dirs: Vec<PathBuf>,
}

impl Config {
    fn from_file() -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let filename = root.join(CONFIG_FILE_NAME);

        let default = vec![root.join("templates")];
        let dirs = if filename.exists() {
            let config_str = fs::read_to_string(&filename)
                .expect(&format!("unable to read {}", filename.to_str().unwrap()));
            let raw: RawConfig = toml::from_str(&config_str)
                .expect(&format!("invalid TOML in {}", filename.to_str().unwrap()));
            raw.dirs
                .map(|dirs| dirs.into_iter().map(|dir| root.join(dir)).collect())
                .unwrap_or_else(|| default)
        } else {
            default
        };

        Config { dirs }
    }
}

#[derive(Deserialize)]
struct RawConfig {
    dirs: Option<Vec<String>>,
}

static CONFIG_FILE_NAME: &str = "askama.toml";

#[cfg(test)]
mod tests {
    use super::Path;
    use super::{find_template_from_path, get_template_source};

    #[test]
    fn get_source() {
        assert_eq!(get_template_source(Path::new("sub/b.html")), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<commit_msg>Get template sources directly from absolute paths<commit_after>use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};

use toml;

fn search_template_in_dirs<'a>(dirs: &'a [PathBuf], path: &Path) -> Option<&'a PathBuf> {
    dirs.iter().find(|dir| dir.join(path).exists())
}

pub fn get_template_source(tpl_path: &Path) -> String {
    let mut f = match File::open(tpl_path) {
        Err(_) => panic!("unable to open template file '{}'", tpl_path.to_str().unwrap()),
        Ok(f) => f,
    };

    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let dirs = template_dirs();
    if let Some(rel) = start_at {
        let root = search_template_in_dirs(&dirs, rel).expect(&format!(
            "unable to find previously available template file '{}'",
            rel.to_str().unwrap()
        ));
        let fs_rel_path = root.join(rel).with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.to_owned();
        }
    }

    let mut rooted = PathBuf::new();
    match search_template_in_dirs(&dirs, &Path::new(path)) {
        Some(dir) => rooted.push(&dir),
        None => panic!("template {:?} not found in directories {:?}", path, dirs),
    };
    rooted.push(path);
    rooted
}

pub fn template_dirs() -> Vec<PathBuf> {
    Config::from_file().dirs
}

struct Config {
    dirs: Vec<PathBuf>,
}

impl Config {
    fn from_file() -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let filename = root.join(CONFIG_FILE_NAME);

        let default = vec![root.join("templates")];
        let dirs = if filename.exists() {
            let config_str = fs::read_to_string(&filename)
                .expect(&format!("unable to read {}", filename.to_str().unwrap()));
            let raw: RawConfig = toml::from_str(&config_str)
                .expect(&format!("invalid TOML in {}", filename.to_str().unwrap()));
            raw.dirs
                .map(|dirs| dirs.into_iter().map(|dir| root.join(dir)).collect())
                .unwrap_or_else(|| default)
        } else {
            default
        };

        Config { dirs }
    }
}

#[derive(Deserialize)]
struct RawConfig {
    dirs: Option<Vec<String>>,
}

static CONFIG_FILE_NAME: &str = "askama.toml";

#[cfg(test)]
mod tests {
    use super::Path;
    use super::{find_template_from_path, get_template_source};

    #[test]
    fn get_source() {
        let path = find_template_from_path("sub/b.html", None);
        assert_eq!(get_template_source(&path), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert!(path.to_str().unwrap().ends_with("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert!(path.to_str().unwrap().ends_with("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert!(path.to_str().unwrap().ends_with("sub/sub1/d.html"));
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::io::Write;
use temp;
use toml;
use rustup_utils;
use manifest::Component;

error_chain! {
    links {
        rustup_utils::Error, rustup_utils::ErrorKind, Utils;
    }

    foreign_links {
        temp::Error, Temp;
    }

    errors {
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)
        }
        InvalidCustomToolchainName(t: String) {
            description("invalid custom toolchain name")
            display("invalid custom toolchain name: '{}'", t)
        }
        ChecksumFailed {
            url: String,
            expected: String,
            calculated: String,
        } {
            description("checksum failed")
            display("checksum failed, expected: '{}', calculated: '{}'",
                    expected,
                    calculated)
        }
        ComponentConflict {
            name: String,
            path: PathBuf,
        } {
            description("conflicting component")
            display("failed to install component: '{}', detected conflict: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingFile {
            name: String,
            path: PathBuf,
        } {
            description("missing file in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingDir {
            name: String,
            path: PathBuf,
        } {
            description("missing directory in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        CorruptComponent(name: String) {
            description("corrupt component manifest")
            display("component manifest for '{}' is corrupt", name)
        }
        ExtractingPackage {
            description("failed to extract package")
        }
        BadInstallerVersion(v: String) {
            description("unsupported installer version")
            display("unsupported installer version: {}", v)
        }
        BadInstalledMetadataVersion(v: String) {
            description("unsupported metadata version in existing installation")
            display("unsupported metadata version in existing installation: {}", v)
        }
        ComponentDirPermissionsFailed {
            description("I/O error walking directory during install")
        }
        ComponentFilePermissionsFailed {
            description("error setting file permissions during install")
        }
        ComponentDownloadFailed(c: Component) {
            description("component download failed")
            display("component download failed for {}{}", c.pkg, {
                if let Some(ref t) = c.target {
                    format!("-{}", t)
                } else {
                    "".to_owned()
                }
            })
        }
        Parsing(e: Vec<toml::ParserError>) {
            description("error parsing manifest")
        }
        UnsupportedVersion(v: String) {
            description("unsupported manifest version")
            display("manifest version '{}' is not supported", v)
        }
        MissingPackageForComponent(c: Component) {
            description("missing package for component")
            display("server sent a broken manifest: missing package for component {}", c.name())
        }
        RequestedComponentsUnavailable(c: Vec<Component>) {
            description("some requested components are unavailable to download")
            display("{}", component_unavailable_msg(&c))
        }
    }
}

fn component_unavailable_msg(cs: &[Component]) -> String {
    assert!(!cs.is_empty());

    let mut buf = vec![];

    fn format_component(c: &Component) -> String {
        if let Some(ref t) = c.target {
            format!("'{}' for '{}", c.pkg, t)
        } else {
            format!("'{}'", c.pkg)
        }
    }

    if cs.len() == 1 {
        let _ = write!(buf, "component {} is unavailable for download", format_component(&cs[0]));
    } else {
        use itertools::Itertools;
        let same_target = cs.iter().all(|c| c.target == cs[0].target || c.target.is_none());
        if same_target {
            let mut cs_strs = cs.iter().map(|c| format!("'{}'", c.pkg));
            let cs_str = cs_strs.join(", ");
            let _ = write!(buf, "some components unavailable for download: {}",
                           cs_str);
        } else {
            let mut cs_strs = cs.iter().map(format_component);
            let cs_str = cs_strs.join(", ");
            let _ = write!(buf, "some components unavailable for download: {}",
                           cs_str);
        }
    }

    String::from_utf8(buf).expect("")
}
<commit_msg>Add missing quote...<commit_after>use std::path::PathBuf;
use std::io::Write;
use temp;
use toml;
use rustup_utils;
use manifest::Component;

error_chain! {
    links {
        rustup_utils::Error, rustup_utils::ErrorKind, Utils;
    }

    foreign_links {
        temp::Error, Temp;
    }

    errors {
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)
        }
        InvalidCustomToolchainName(t: String) {
            description("invalid custom toolchain name")
            display("invalid custom toolchain name: '{}'", t)
        }
        ChecksumFailed {
            url: String,
            expected: String,
            calculated: String,
        } {
            description("checksum failed")
            display("checksum failed, expected: '{}', calculated: '{}'",
                    expected,
                    calculated)
        }
        ComponentConflict {
            name: String,
            path: PathBuf,
        } {
            description("conflicting component")
            display("failed to install component: '{}', detected conflict: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingFile {
            name: String,
            path: PathBuf,
        } {
            description("missing file in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        ComponentMissingDir {
            name: String,
            path: PathBuf,
        } {
            description("missing directory in component")
            display("failure removing component '{}', directory does not exist: '{:?}'",
                    name,
                    path)
        }
        CorruptComponent(name: String) {
            description("corrupt component manifest")
            display("component manifest for '{}' is corrupt", name)
        }
        ExtractingPackage {
            description("failed to extract package")
        }
        BadInstallerVersion(v: String) {
            description("unsupported installer version")
            display("unsupported installer version: {}", v)
        }
        BadInstalledMetadataVersion(v: String) {
            description("unsupported metadata version in existing installation")
            display("unsupported metadata version in existing installation: {}", v)
        }
        ComponentDirPermissionsFailed {
            description("I/O error walking directory during install")
        }
        ComponentFilePermissionsFailed {
            description("error setting file permissions during install")
        }
        ComponentDownloadFailed(c: Component) {
            description("component download failed")
            display("component download failed for {}{}", c.pkg, {
                if let Some(ref t) = c.target {
                    format!("-{}", t)
                } else {
                    "".to_owned()
                }
            })
        }
        Parsing(e: Vec<toml::ParserError>) {
            description("error parsing manifest")
        }
        UnsupportedVersion(v: String) {
            description("unsupported manifest version")
            display("manifest version '{}' is not supported", v)
        }
        MissingPackageForComponent(c: Component) {
            description("missing package for component")
            display("server sent a broken manifest: missing package for component {}", c.name())
        }
        RequestedComponentsUnavailable(c: Vec<Component>) {
            description("some requested components are unavailable to download")
            display("{}", component_unavailable_msg(&c))
        }
    }
}

fn component_unavailable_msg(cs: &[Component]) -> String {
    assert!(!cs.is_empty());

    let mut buf = vec![];

    fn format_component(c: &Component) -> String {
        if let Some(ref t) = c.target {
            format!("'{}' for '{}'", c.pkg, t)
        } else {
            format!("'{}'", c.pkg)
        }
    }

    if cs.len() == 1 {
        let _ = write!(buf, "component {} is unavailable for download", format_component(&cs[0]));
    } else {
        use itertools::Itertools;
        let same_target = cs.iter().all(|c| c.target == cs[0].target || c.target.is_none());
        if same_target {
            let mut cs_strs = cs.iter().map(|c| format!("'{}'", c.pkg));
            let cs_str = cs_strs.join(", ");
            let _ = write!(buf, "some components unavailable for download: {}",
                           cs_str);
        } else {
            let mut cs_strs = cs.iter().map(format_component);
            let cs_str = cs_strs.join(", ");
            let _ = write!(buf, "some components unavailable for download: {}",
                           cs_str);
        }
    }

    String::from_utf8(buf).expect("")
}
<|endoftext|>


<commit_before>// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::{Arc, Mutex, Condvar};
use std::sync::atomic::{Ordering, AtomicUsize};
use std::collections::VecDeque;
use std::thread;

/// Manages a collection of threads.
///
/// A new thread is created every time all the existing threads are full.
/// Any idle thread will automatically die after a few seconds.
pub struct TaskPool {
    sharing: Arc<Sharing>,
}

struct Sharing {
    // list of the tasks to be done by worker threads
    todo: Mutex<VecDeque<Box<FnMut() + Send>>>,

    // condvar that will be notified whenever a task is added to `todo`
    condvar: Condvar,

    // number of total worker threads running
    active_tasks: AtomicUsize,

    // number of idle worker threads
    waiting_tasks: AtomicUsize,
}

/// Minimum number of active threads.
static MIN_THREADS: usize = 4;

struct Registration<'a> {
    nb: &'a AtomicUsize
}

impl<'a> Registration<'a> {
    fn new(nb: &'a AtomicUsize) -> Registration<'a> {
        nb.fetch_add(1, Ordering::Release);
        Registration { nb: nb }
    }
}

impl<'a> Drop for Registration<'a> {
    fn drop(&mut self) {
        self.nb.fetch_sub(1, Ordering::Release);
    }
}

impl TaskPool {
    pub fn new() -> TaskPool {
        let pool = TaskPool {
            sharing: Arc::new(Sharing {
                todo: Mutex::new(VecDeque::new()),
                condvar: Condvar::new(),
                active_tasks: AtomicUsize::new(0),
                waiting_tasks: AtomicUsize::new(0),
            }),
        };

        for _ in 0..MIN_THREADS {
            pool.add_thread(None)
        }

        pool
    }

    /// Executes a function in a thread.
    /// If no thread is available, spawns a new one.
    pub fn spawn(&self, code: Box<FnMut() + Send>) {
        let mut queue = self.sharing.todo.lock().unwrap();

        if self.sharing.waiting_tasks.load(Ordering::Acquire) == 0 {
            self.add_thread(Some(code));

        } else {
            queue.push_back(code);
            self.sharing.condvar.notify_one();
        }
    }

    fn add_thread(&self, initial_fn: Option<Box<FnMut() + Send>>) {
        let sharing = self.sharing.clone();

        thread::spawn(move || {
            let sharing = sharing;
            let _active_guard = Registration::new(&sharing.active_tasks);

            if initial_fn.is_some() {
                let mut f = initial_fn.unwrap();
                f();
            }

            loop {
                let mut task: Box<FnMut() + Send> = {
                    let mut todo = sharing.todo.lock().unwrap();

                    let task;
                    loop {
                        if let Some(poped_task) = todo.pop_front() {
                            task = poped_task;
                            break;

                        } else {
                            let _waiting_guard = Registration::new(&sharing.waiting_tasks);

                            let received = if sharing.active_tasks.load(Ordering::Acquire)
                                                    <= MIN_THREADS
                            {
                                todo = sharing.condvar.wait(todo).unwrap();
                                true

                            } else {
                                let (new_lock, receved) = sharing.condvar
                                                                 .wait_timeout_ms(todo, 5000)
                                                                 .unwrap();
                                todo = new_lock;
                                receved
                            };

                            if !received && todo.is_empty() {
                                return;
                            }
                        }
                    }

                    task
                };

                task();
            }
        });
    }
}

impl Drop for TaskPool {
    fn drop(&mut self) {
        self.sharing.active_tasks.store(999999999, Ordering::Release);
        self.sharing.condvar.notify_all();
    }
}
<commit_msg>Remove unnecessary `else` block<commit_after>// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::{Arc, Mutex, Condvar};
use std::sync::atomic::{Ordering, AtomicUsize};
use std::collections::VecDeque;
use std::thread;

/// Manages a collection of threads.
///
/// A new thread is created every time all the existing threads are full.
/// Any idle thread will automatically die after a few seconds.
pub struct TaskPool {
    sharing: Arc<Sharing>,
}

struct Sharing {
    // list of the tasks to be done by worker threads
    todo: Mutex<VecDeque<Box<FnMut() + Send>>>,

    // condvar that will be notified whenever a task is added to `todo`
    condvar: Condvar,

    // number of total worker threads running
    active_tasks: AtomicUsize,

    // number of idle worker threads
    waiting_tasks: AtomicUsize,
}

/// Minimum number of active threads.
static MIN_THREADS: usize = 4;

struct Registration<'a> {
    nb: &'a AtomicUsize
}

impl<'a> Registration<'a> {
    fn new(nb: &'a AtomicUsize) -> Registration<'a> {
        nb.fetch_add(1, Ordering::Release);
        Registration { nb: nb }
    }
}

impl<'a> Drop for Registration<'a> {
    fn drop(&mut self) {
        self.nb.fetch_sub(1, Ordering::Release);
    }
}

impl TaskPool {
    pub fn new() -> TaskPool {
        let pool = TaskPool {
            sharing: Arc::new(Sharing {
                todo: Mutex::new(VecDeque::new()),
                condvar: Condvar::new(),
                active_tasks: AtomicUsize::new(0),
                waiting_tasks: AtomicUsize::new(0),
            }),
        };

        for _ in 0..MIN_THREADS {
            pool.add_thread(None)
        }

        pool
    }

    /// Executes a function in a thread.
    /// If no thread is available, spawns a new one.
    pub fn spawn(&self, code: Box<FnMut() + Send>) {
        let mut queue = self.sharing.todo.lock().unwrap();

        if self.sharing.waiting_tasks.load(Ordering::Acquire) == 0 {
            self.add_thread(Some(code));

        } else {
            queue.push_back(code);
            self.sharing.condvar.notify_one();
        }
    }

    fn add_thread(&self, initial_fn: Option<Box<FnMut() + Send>>) {
        let sharing = self.sharing.clone();

        thread::spawn(move || {
            let sharing = sharing;
            let _active_guard = Registration::new(&sharing.active_tasks);

            if initial_fn.is_some() {
                let mut f = initial_fn.unwrap();
                f();
            }

            loop {
                let mut task: Box<FnMut() + Send> = {
                    let mut todo = sharing.todo.lock().unwrap();

                    let task;
                    loop {
                        if let Some(poped_task) = todo.pop_front() {
                            task = poped_task;
                            break;
                        }
                        let _waiting_guard = Registration::new(&sharing.waiting_tasks);

                        let received = if sharing.active_tasks.load(Ordering::Acquire)
                                                <= MIN_THREADS
                        {
                            todo = sharing.condvar.wait(todo).unwrap();
                            true

                        } else {
                            let (new_lock, receved) = sharing.condvar
                                                             .wait_timeout_ms(todo, 5000)
                                                             .unwrap();
                            todo = new_lock;
                            receved
                        };

                        if !received && todo.is_empty() {
                            return;
                        }
                    }

                    task
                };

                task();
            }
        });
    }
}

impl Drop for TaskPool {
    fn drop(&mut self) {
        self.sharing.active_tasks.store(999999999, Ordering::Release);
        self.sharing.condvar.notify_all();
    }
}
<|endoftext|>