<commit_before>// Copyright 2015-2016 ZFilexfer Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use chunk::Chunk;
use czmq::{ZMsg, ZSock, ZSys};
use error::{Error, Result};
use std::sync::{Arc, RwLock};
use std::thread::{JoinHandle, spawn};
use std::time::Instant;

#[cfg(not(test))]
const CHUNK_TIMEOUT: u64 = 60;
#[cfg(test)]
const CHUNK_TIMEOUT: u64 = 1;

pub struct Arbitrator {
    router: ZSock,
    queue: Arc<RwLock<Vec<TimedChunk>>>,
    timer_handle: Option<JoinHandle<()>>,
    timer_comm: ZSock,
    slots: u32,
}

impl Drop for Arbitrator {
    fn drop(&mut self) {
        // Ignore failure as it means the thread has already
        // terminated.
        let _ = self.timer_comm.signal(0);
        if self.timer_handle.is_some() {
            self.timer_handle.take().unwrap().join().unwrap();
        }
    }
}

impl Arbitrator {
    pub fn new(router: ZSock, upload_slots: u32) -> Result<Arbitrator> {
        let (comm_front, comm_back) = try!(ZSys::create_pipe());
        comm_front.set_sndtimeo(Some(1000));
        comm_front.set_linger(0);
        comm_back.set_rcvtimeo(Some(1000)); // Remember that this timeout controls the Timer loop speed!
        comm_back.set_linger(0);

        let lock = Arc::new(RwLock::new(Vec::new()));
        let timer = try!(Timer::new(comm_back, lock.clone()));

        Ok(Arbitrator {
            router: router,
            queue: lock,
            timer_handle: Some(spawn(move|| timer.run())),
            timer_comm: comm_front,
            slots: upload_slots,
        })
    }

    pub fn queue(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let timed_chunk = TimedChunk::new(router_id, chunk.get_index());
        {
            let mut writer = self.queue.write().unwrap();
            writer.push(timed_chunk);
        }

        try!(self.request());
        Ok(())
    }

    pub fn release(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let router_id = router_id.to_vec();
        {
            let mut queue = self.queue.write().unwrap();
            let mut index: Option<usize> = None;
            let mut x = 0;
            for c in queue.iter_mut() {
                if c.router_id == router_id && c.index == chunk.get_index() {
                    index = Some(x);
                    break;
                }

                x += 1;
            }

            match index {
                Some(i) => {
                    queue.remove(i);
                    self.slots += 1;
                },
                None => return Err(Error::ChunkIndex),
            }
        }

        try!(self.request());
        Ok(())
    }

    fn request(&mut self) -> Result<()> {
        for chunk in self.queue.write().unwrap().iter_mut() {
            if self.slots == 0 {
                break;
            }

            if !chunk.is_started() {
                self.slots -= 1;

                let msg = ZMsg::new();
                try!(msg.addbytes(&chunk.router_id));
                try!(msg.addstr("CHUNK"));
                try!(msg.addstr(&chunk.index.to_string()));
                try!(msg.send(&mut self.router));

                chunk.start();
            }
        }

        Ok(())
    }
}

struct Timer {
    chunks: Arc<RwLock<Vec<TimedChunk>>>,
    sink: ZSock,
    comm: ZSock,
}

impl Timer {
    fn new(comm: ZSock, chunks: Arc<RwLock<Vec<TimedChunk>>>) -> Result<Timer> {
        Ok(Timer {
            chunks: chunks,
            sink: try!(ZSock::new_push(">inproc://zfilexfer_sink")),
            comm: comm,
        })
    }

    fn run(mut self) {
        loop {
            // Terminate on ZSock signal or system signal (SIGTERM)
            if self.comm.wait().is_ok() || ZSys::is_interrupted() {
                break;
            }

            for chunk in self.chunks.read().unwrap().iter() {
                if chunk.is_expired() {
                    let msg = ZMsg::new();
                    msg.addbytes(&chunk.router_id).unwrap();
                    msg.addstr(&chunk.index.to_string()).unwrap();
                    msg.addstr("0").unwrap();
                    msg.send(&mut self.sink).unwrap();
                }
            }
        }
    }
}

struct TimedChunk {
    router_id: Vec<u8>,
    index: u64,
    timestamp: Option<Instant>,
}

impl TimedChunk {
    fn new(router_id: &[u8], index: u64) -> TimedChunk {
        TimedChunk {
            router_id: router_id.to_vec(),
            index: index,
            timestamp: None,
        }
    }

    fn start(&mut self) {
        self.timestamp = Some(Instant::now());
    }

    fn is_started(&self) -> bool {
        self.timestamp.is_some()
    }

    fn is_expired(&self) -> bool {
        if self.timestamp.is_some() {
            self.timestamp.as_ref().unwrap().elapsed().as_secs() >= CHUNK_TIMEOUT
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use chunk::Chunk;
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use std::cell::RefCell;
    use std::rc::Rc;
    use std::sync::{Arc, RwLock};
    use std::thread::{sleep, spawn};
    use std::time::{Duration, Instant};
    use super::*;
    use super::{TimedChunk, Timer};
    use tempfile::tempfile;

    #[test]
    fn test_arbitrator_new() {
        ZSys::init();

        assert!(Arbitrator::new(ZSock::new(ZSockType::PAIR), 0).is_ok());
    }

    #[test]
    fn test_arbitrator_queue_release() {
        ZSys::init();

        let chunk = Chunk::new(Rc::new(RefCell::new(tempfile().unwrap())), 0);

        let mut arbitrator = Arbitrator::new(ZSock::new(ZSockType::ROUTER), 1).unwrap();
        assert!(arbitrator.queue(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 1);
        assert_eq!(arbitrator.slots, 0);
        assert!(arbitrator.release(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 0);
        assert_eq!(arbitrator.slots, 1);
    }

    #[test]
    fn test_arbitrator_request() {
        ZSys::init();

        let (mut client, router) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));

        let (comm, thread) = ZSys::create_pipe().unwrap();

        let chunks = vec![
            TimedChunk::new("abc".as_bytes(), 0),
            TimedChunk::new("abc".as_bytes(), 1),
            TimedChunk::new("abc".as_bytes(), 2),
            TimedChunk::new("def".as_bytes(), 0),
            TimedChunk::new("def".as_bytes(), 1),
            TimedChunk::new("def".as_bytes(), 2),
        ];

        {
            let mut arbitrator = Arbitrator {
                router: router,
                queue: Arc::new(RwLock::new(chunks)),
                timer_handle: None,
                timer_comm: comm,
                slots: 3,
            };

            arbitrator.request().unwrap();

            for x in 0..3 {
                let msg = ZMsg::recv(&mut client).unwrap();
                assert_eq!(&msg.popstr().unwrap().unwrap(), "abc");
                assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
                assert_eq!(msg.popstr().unwrap().unwrap(), x.to_string());
            }

            assert!(client.recv_str().is_err());

            let chunk = Chunk::new(Rc::new(RefCell::new(tempfile().unwrap())), 0);
            arbitrator.release(&chunk, "abc".as_bytes()).unwrap();
            arbitrator.request().unwrap();

            let msg = ZMsg::recv(&mut client).unwrap();
            assert_eq!(&msg.popstr().unwrap().unwrap(), "def");
            assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
            assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        }

        thread.wait().unwrap();
    }

    #[test]
    fn test_timer_new() {
        ZSys::init();

        assert!(Timer::new(ZSock::new(ZSockType::REQ), Arc::new(RwLock::new(Vec::new()))).is_ok());
    }

    #[test]
    fn test_timer_run() {
        ZSys::init();

        let (mut client, server) = ZSys::create_pipe().unwrap();
        let (comm, thread) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(1500));
        thread.set_rcvtimeo(Some(1000));

        let mut c = TimedChunk::new("abc".as_bytes(), 0);
        c.start();

        let timer = Timer {
            chunks: Arc::new(RwLock::new(vec![
                c,
            ])),
            sink: server,
            comm: thread,
        };
        let handle = spawn(|| timer.run());

        let msg = ZMsg::recv(&mut client).unwrap();
        assert_eq!(msg.popstr().unwrap().unwrap(), "abc");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");

        comm.signal(0).unwrap();
        handle.join().unwrap();
    }

    #[test]
    fn test_chunk_is_expired() {
        let timed = TimedChunk {
            router_id: vec![97, 98, 99],
            index: 0,
            timestamp: Some(Instant::now()),
        };

        sleep(Duration::from_secs(1));

        assert!(timed.is_expired());
    }
}
<commit_msg>Trivial cleanup of Arbitrator Drop trait imp<commit_after>// Copyright 2015-2016 ZFilexfer Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use chunk::Chunk;
use czmq::{ZMsg, ZSock, ZSys};
use error::{Error, Result};
use std::sync::{Arc, RwLock};
use std::thread::{JoinHandle, spawn};
use std::time::Instant;

#[cfg(not(test))]
const CHUNK_TIMEOUT: u64 = 60;
#[cfg(test)]
const CHUNK_TIMEOUT: u64 = 1;

pub struct Arbitrator {
    router: ZSock,
    queue: Arc<RwLock<Vec<TimedChunk>>>,
    timer_handle: Option<JoinHandle<()>>,
    timer_comm: ZSock,
    slots: u32,
}

impl Drop for Arbitrator {
    fn drop(&mut self) {
        // Ignore failure as it means the thread has already
        // terminated.
        let _ = self.timer_comm.signal(0);
        if let Some(h) = self.timer_handle.take() {
            h.join().unwrap();
        }
    }
}

impl Arbitrator {
    pub fn new(router: ZSock, upload_slots: u32) -> Result<Arbitrator> {
        let (comm_front, comm_back) = try!(ZSys::create_pipe());
        comm_front.set_sndtimeo(Some(1000));
        comm_front.set_linger(0);
        comm_back.set_rcvtimeo(Some(1000)); // Remember that this timeout controls the Timer loop speed!
        comm_back.set_linger(0);

        let lock = Arc::new(RwLock::new(Vec::new()));
        let timer = try!(Timer::new(comm_back, lock.clone()));

        Ok(Arbitrator {
            router: router,
            queue: lock,
            timer_handle: Some(spawn(move|| timer.run())),
            timer_comm: comm_front,
            slots: upload_slots,
        })
    }

    pub fn queue(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let timed_chunk = TimedChunk::new(router_id, chunk.get_index());
        {
            let mut writer = self.queue.write().unwrap();
            writer.push(timed_chunk);
        }

        try!(self.request());
        Ok(())
    }

    pub fn release(&mut self, chunk: &Chunk, router_id: &[u8]) -> Result<()> {
        let router_id = router_id.to_vec();
        {
            let mut queue = self.queue.write().unwrap();
            let mut index: Option<usize> = None;
            let mut x = 0;
            for c in queue.iter_mut() {
                if c.router_id == router_id && c.index == chunk.get_index() {
                    index = Some(x);
                    break;
                }

                x += 1;
            }

            match index {
                Some(i) => {
                    queue.remove(i);
                    self.slots += 1;
                },
                None => return Err(Error::ChunkIndex),
            }
        }

        try!(self.request());
        Ok(())
    }

    fn request(&mut self) -> Result<()> {
        for chunk in self.queue.write().unwrap().iter_mut() {
            if self.slots == 0 {
                break;
            }

            if !chunk.is_started() {
                self.slots -= 1;

                let msg = ZMsg::new();
                try!(msg.addbytes(&chunk.router_id));
                try!(msg.addstr("CHUNK"));
                try!(msg.addstr(&chunk.index.to_string()));
                try!(msg.send(&mut self.router));

                chunk.start();
            }
        }

        Ok(())
    }
}

struct Timer {
    chunks: Arc<RwLock<Vec<TimedChunk>>>,
    sink: ZSock,
    comm: ZSock,
}

impl Timer {
    fn new(comm: ZSock, chunks: Arc<RwLock<Vec<TimedChunk>>>) -> Result<Timer> {
        Ok(Timer {
            chunks: chunks,
            sink: try!(ZSock::new_push(">inproc://zfilexfer_sink")),
            comm: comm,
        })
    }

    fn run(mut self) {
        loop {
            // Terminate on ZSock signal or system signal (SIGTERM)
            if self.comm.wait().is_ok() || ZSys::is_interrupted() {
                break;
            }

            for chunk in self.chunks.read().unwrap().iter() {
                if chunk.is_expired() {
                    let msg = ZMsg::new();
                    msg.addbytes(&chunk.router_id).unwrap();
                    msg.addstr(&chunk.index.to_string()).unwrap();
                    msg.addstr("0").unwrap();
                    msg.send(&mut self.sink).unwrap();
                }
            }
        }
    }
}

struct TimedChunk {
    router_id: Vec<u8>,
    index: u64,
    timestamp: Option<Instant>,
}

impl TimedChunk {
    fn new(router_id: &[u8], index: u64) -> TimedChunk {
        TimedChunk {
            router_id: router_id.to_vec(),
            index: index,
            timestamp: None,
        }
    }

    fn start(&mut self) {
        self.timestamp = Some(Instant::now());
    }

    fn is_started(&self) -> bool {
        self.timestamp.is_some()
    }

    fn is_expired(&self) -> bool {
        if self.timestamp.is_some() {
            self.timestamp.as_ref().unwrap().elapsed().as_secs() >= CHUNK_TIMEOUT
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use chunk::Chunk;
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use std::cell::RefCell;
    use std::rc::Rc;
    use std::sync::{Arc, RwLock};
    use std::thread::{sleep, spawn};
    use std::time::{Duration, Instant};
    use super::*;
    use super::{TimedChunk, Timer};
    use tempfile::tempfile;

    #[test]
    fn test_arbitrator_new() {
        ZSys::init();

        assert!(Arbitrator::new(ZSock::new(ZSockType::PAIR), 0).is_ok());
    }

    #[test]
    fn test_arbitrator_queue_release() {
        ZSys::init();

        let chunk = Chunk::new(Rc::new(RefCell::new(tempfile().unwrap())), 0);

        let mut arbitrator = Arbitrator::new(ZSock::new(ZSockType::ROUTER), 1).unwrap();
        assert!(arbitrator.queue(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 1);
        assert_eq!(arbitrator.slots, 0);
        assert!(arbitrator.release(&chunk, "abc".as_bytes()).is_ok());
        assert_eq!(arbitrator.queue.read().unwrap().len(), 0);
        assert_eq!(arbitrator.slots, 1);
    }

    #[test]
    fn test_arbitrator_request() {
        ZSys::init();

        let (mut client, router) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));

        let (comm, thread) = ZSys::create_pipe().unwrap();

        let chunks = vec![
            TimedChunk::new("abc".as_bytes(), 0),
            TimedChunk::new("abc".as_bytes(), 1),
            TimedChunk::new("abc".as_bytes(), 2),
            TimedChunk::new("def".as_bytes(), 0),
            TimedChunk::new("def".as_bytes(), 1),
            TimedChunk::new("def".as_bytes(), 2),
        ];

        {
            let mut arbitrator = Arbitrator {
                router: router,
                queue: Arc::new(RwLock::new(chunks)),
                timer_handle: None,
                timer_comm: comm,
                slots: 3,
            };

            arbitrator.request().unwrap();

            for x in 0..3 {
                let msg = ZMsg::recv(&mut client).unwrap();
                assert_eq!(&msg.popstr().unwrap().unwrap(), "abc");
                assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
                assert_eq!(msg.popstr().unwrap().unwrap(), x.to_string());
            }

            assert!(client.recv_str().is_err());

            let chunk = Chunk::new(Rc::new(RefCell::new(tempfile().unwrap())), 0);
            arbitrator.release(&chunk, "abc".as_bytes()).unwrap();
            arbitrator.request().unwrap();

            let msg = ZMsg::recv(&mut client).unwrap();
            assert_eq!(&msg.popstr().unwrap().unwrap(), "def");
            assert_eq!(&msg.popstr().unwrap().unwrap(), "CHUNK");
            assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        }

        thread.wait().unwrap();
    }

    #[test]
    fn test_timer_new() {
        ZSys::init();

        assert!(Timer::new(ZSock::new(ZSockType::REQ), Arc::new(RwLock::new(Vec::new()))).is_ok());
    }

    #[test]
    fn test_timer_run() {
        ZSys::init();

        let (mut client, server) = ZSys::create_pipe().unwrap();
        let (comm, thread) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(1500));
        thread.set_rcvtimeo(Some(1000));

        let mut c = TimedChunk::new("abc".as_bytes(), 0);
        c.start();

        let timer = Timer {
            chunks: Arc::new(RwLock::new(vec![
                c,
            ])),
            sink: server,
            comm: thread,
        };
        let handle = spawn(|| timer.run());

        let msg = ZMsg::recv(&mut client).unwrap();
        assert_eq!(msg.popstr().unwrap().unwrap(), "abc");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");
        assert_eq!(msg.popstr().unwrap().unwrap(), "0");

        comm.signal(0).unwrap();
        handle.join().unwrap();
    }

    #[test]
    fn test_chunk_is_expired() {
        let timed = TimedChunk {
            router_id: vec![97, 98, 99],
            index: 0,
            timestamp: Some(Instant::now()),
        };

        sleep(Duration::from_secs(1));

        assert!(timed.is_expired());
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    pub fn rid(&self) -> Option<u32> {
        match unsafe { *self.inner }.rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        unsafe { *self.inner }.pos as u32
    }

    pub fn set_pos(&mut self, pos: i32) {
        unsafe { (*self.inner).pos = pos; }
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = unsafe { *self.inner }.n_allele as usize;
        let dec = unsafe { *self.inner }.d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        unsafe { *self.inner }.qual
    }

    pub fn set_qual(&mut self, qual: f32) {
        unsafe { (*self.inner).qual = qual; }
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        unsafe { *self.inner }.n_fmt_n_sample >> 8
    }

    pub fn allele_count(&self) -> u16 {
        unsafe { *self.inner }.n_allele
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }

    /// Add an integer format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_integer(&mut self, tag: &[u8], data: &[i32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_INT)
    }

    /// Add a float format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_float(&mut self, tag: &[u8], data: &[f32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_REAL)
    }

    /// Add a format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    fn push_format<T>(&mut self, tag: &[u8], data: &[T], ht: i32) -> Result<(), ()> {
        assert!(data.len() > 0);
        unsafe {
            if htslib::vcf::bcf_update_format(
                self.header,
                self.inner,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8,
                data.as_ptr() as *const ::libc::c_void,
                data.len() as i32,
                ht
            ) == 0 {
                Ok(())
            }
            else {
                Err(())
            }
        }
    }

    /// Remove unused alleles.
    pub fn trim_alleles(&mut self) -> Result<(), ()> {
        match unsafe { htslib::vcfutils::bcf_trim_alleles(self.header, self.inner) } {
            -1 => Err(()),
            _  => Ok(())
        }
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

unsafe impl Send for Record {}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&mut [i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&mut [f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&mut [u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&mut [i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut i32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&mut [f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut f32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<commit_msg>Add push_info to bcf::Record<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::ptr;
use std::slice;
use std::ffi;

use htslib;

pub struct Record {
    pub inner: *mut htslib::vcf::bcf1_t,
    pub header: *mut htslib::vcf::bcf_hdr_t,
    buffer: *mut ::libc::c_void,
}

impl Record {
    pub fn new() -> Self {
        let inner = unsafe { htslib::vcf::bcf_init() };
        Record { inner: inner, header: ptr::null_mut(), buffer: ptr::null_mut() }
    }

    pub fn rid(&self) -> Option<u32> {
        match unsafe { *self.inner }.rid {
            -1  => None,
            rid => Some(rid as u32)
        }
    }

    // 0-based position.
    pub fn pos(&self) -> u32 {
        unsafe { *self.inner }.pos as u32
    }

    pub fn set_pos(&mut self, pos: i32) {
        unsafe { (*self.inner).pos = pos; }
    }

    pub fn alleles(&self) -> Vec<&[u8]> {
        unsafe { htslib::vcf::bcf_unpack(self.inner, htslib::vcf::BCF_UN_STR) };
        let n = unsafe { *self.inner }.n_allele as usize;
        let dec = unsafe { *self.inner }.d;
        let alleles = unsafe { slice::from_raw_parts(dec.allele, n) };
        (0..n).map(|i| unsafe { ffi::CStr::from_ptr(alleles[i]).to_bytes() }).collect()
    }

    pub fn qual(&self) -> f32 {
        unsafe { *self.inner }.qual
    }

    pub fn set_qual(&mut self, qual: f32) {
        unsafe { (*self.inner).qual = qual; }
    }

    /// Get the value of the given info tag.
    pub fn info<'a>(&'a mut self, tag: &'a [u8]) -> Info {
        Info { record: self, tag: tag }
    }

    pub fn sample_count(&self) -> u32 {
        unsafe { *self.inner }.n_fmt_n_sample >> 8
    }

    pub fn allele_count(&self) -> u16 {
        unsafe { *self.inner }.n_allele
    }

    /// Get the value of the given format tag for each sample.
    pub fn format<'a>(&'a mut self, tag: &'a [u8]) -> Format {
        Format::new(self, tag)
    }

    /// Add an integer format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_integer(&mut self, tag: &[u8], data: &[i32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_INT)
    }

    /// Add a float format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    /// Returns error if tag is not present in header.
    pub fn push_format_float(&mut self, tag: &[u8], data: &[f32]) -> Result<(), ()> {
        self.push_format(tag, data, htslib::vcf::BCF_HT_REAL)
    }

    /// Add a format tag. Data is a flattened two-dimensional array.
    /// The first dimension contains one array for each sample.
    fn push_format<T>(&mut self, tag: &[u8], data: &[T], ht: i32) -> Result<(), ()> {
        assert!(data.len() > 0);
        unsafe {
            if htslib::vcf::bcf_update_format(
                self.header,
                self.inner,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8,
                data.as_ptr() as *const ::libc::c_void,
                data.len() as i32,
                ht
            ) == 0 {
                Ok(())
            }
            else {
                Err(())
            }
        }
    }

    /// Add a info tag.
    pub fn push_info<T>(&mut self, tag: &[u8], data: &[T], ht: i32) -> Result<(), ()> {
        assert!(data.len() > 0);
        unsafe {
            if htslib::vcf::bcf_update_info(
                self.header,
                self.inner,
                ffi::CString::new(tag).unwrap().as_ptr() as *mut i8,
                data.as_ptr() as *const ::libc::c_void,
                data.len() as i32,
                ht
            ) == 0 {
                Ok(())
            }
            else {
                Err(())
            }
        }
    }

    /// Remove unused alleles.
    pub fn trim_alleles(&mut self) -> Result<(), ()> {
        match unsafe { htslib::vcfutils::bcf_trim_alleles(self.header, self.inner) } {
            -1 => Err(()),
            _  => Ok(())
        }
    }
}

impl Drop for Record {
    fn drop(&mut self) {
        if !self.buffer.is_null() {
            unsafe { ::libc::free(self.buffer) };
        }
        unsafe { htslib::vcf::bcf_destroy(self.inner) };
    }
}

unsafe impl Send for Record {}

pub struct Info<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
}

impl<'a> Info<'a> {
    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_info_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<&mut [i32], TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut i32, n) }
        })
    }

    pub fn float(&mut self) -> Result<&mut [f32], TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut f32, n) }
        })
    }

    pub fn flag(&mut self) -> Result<bool, TagError> {
        self.data(htslib::vcf::BCF_HT_FLAG).map(|(_, ret)| {
            ret == 1
        })
    }

    pub fn string(&mut self) -> Result<&mut [u8], TagError> {
        self.data(htslib::vcf::BCF_HT_STR).map(|(_, ret)| {
            unsafe { slice::from_raw_parts_mut(self.record.buffer as *mut u8, ret as usize) }
        })
    }
}

// TODO implement format.
pub struct Format<'a> {
    record: &'a mut Record,
    tag: &'a [u8],
    inner: *mut htslib::vcf::bcf_fmt_t,
}

impl<'a> Format<'a> {
    fn new(record: &'a mut Record, tag: &'a [u8]) -> Format<'a> {
        let inner = unsafe { htslib::vcf::bcf_get_fmt(
            record.header,
            record.inner,
            ffi::CString::new(tag).unwrap().as_ptr() as *mut i8
        ) };
        Format { record: record, tag: tag, inner: inner }
    }

    fn values_per_sample(&self) -> usize {
        unsafe { (*self.inner).n as usize }
    }

    fn data(&mut self, data_type: i32) -> Result<(usize, i32), TagError> {
        let mut n: i32 = 0;
        match unsafe {
            htslib::vcf::bcf_get_format_values(
                self.record.header,
                self.record.inner,
                ffi::CString::new(self.tag).unwrap().as_ptr() as *mut i8,
                &mut self.record.buffer,
                &mut n,
                data_type
            )
        } {
            -1 => Err(TagError::UndefinedTag),
            -2 => Err(TagError::UnexpectedType),
            -3 => Err(TagError::MissingTag),
            ret  => Ok((n as usize, ret)),
        }
    }

    pub fn integer(&mut self) -> Result<Vec<&mut [i32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_INT).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut i32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }

    pub fn float(&mut self) -> Result<Vec<&mut [f32]>, TagError> {
        self.data(htslib::vcf::BCF_HT_REAL).map(|(n, _)| {
            unsafe {
                slice::from_raw_parts_mut(self.record.buffer as *mut f32, n)
            }.chunks_mut(self.values_per_sample()).collect()
        })
    }
}

pub enum TagError {
    UndefinedTag,
    UnexpectedType,
    MissingTag,
}
<|endoftext|>


<commit_before>extern crate zwreec;
extern crate getopts;
extern crate libc;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::{Read,Write};
use std::path::Path;
use std::process::exit;

use zwreec::config;
use zwreec::config::Config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Returns the primary options for zwreec. It is e.g. used to generate the
/// usage information.
fn short_options() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflagmulti("v", "verbose", "Be more verbose. Can be used multiple times.");
    opts.optflag("q", "quiet", "Be quiet");
    opts.optflag("w", "overwrite", "Overwrite output file if necessary.");
    opts.optflagopt("l", "logfile", "Specify log file (additionally to logging on stderr)", "LOGFILE");
    opts.optopt("o", "", "Name of the output file", "FILE");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("V", "version", "Display version");

    opts
}

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = short_options();

    let brief = format!("Usage: zwreec [-hV] [-vqwf] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}", config::zwreec_usage(verbose, options, &brief));
}

/// Parse command line arguments
///
/// Parses command line arguments to set up the logger and extract input and
/// output parameters. Will display the usage and exit depending on arguments.
///
/// Returns `getopts::Matches` and a `std::fs::File` for the input and output
/// file. The `getopts::Matches` can be used to
///
/// # Examples
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "display this help and exit");
///
/// let (matches, mut input, mut output) = parse_arguments(
///     env::args().collect(),
///     opts
/// );
/// ```
///
/// # Failures
///
/// Depending on encountered arguments or parsing errors this function will
/// print the usage and/or call `exit(1)`.
///
/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>, opts: getopts::Options) -> (getopts::Matches, config::Config) {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            println!("Error: logfile option specified but no logfile name given");
            usage(false);
            exit(1);
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    let cfg = Config::from_matches(&matches);
    (matches, cfg)
}

fn parse_input(matches: &getopts::Matches) -> Option<Box<Read>> {
    if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                    path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                Some(Box::new(file))
            }
        }
    } else if unsafe { libc::isatty(libc::STDIN_FILENO as i32) } == 0 {
        // Not connected to a terminal, assuming safe to read from stdin
        info!("Reading input from stdin");
        Some(Box::new(std::io::stdin()))
    } else {
        None
    }
}

fn parse_output(matches: &getopts::Matches) -> Option<Box<Write>> {
    let name = matches.opt_str("o").unwrap_or("a.z8".to_string());

    if name == "-" {
        // tty requested
        if unsafe { libc::isatty(libc::STDOUT_FILENO as i32)  } == 0 {
            // Not connected to a terminal, assuming safe to write to stdin
            // NOTE: this should be considered unsafe, as the library is *not*
            // guaranteed to only print to stderr
            warn!("Writing to stdout can lead to unusable output!");
            warn!("You should specify an output name using -o 'FILE'");
            info!("Writing output to stdout");
            Some(Box::new(std::io::stdout()))
        } else {
            error!("stdout is connected to a terminal.");
            error!("Zcode is a binary format and should not be printed to a tty.");
            None
        }
    } else {
        // opening file
        let path = Path::new(&name);

        if name == "a.z8" {
            debug!("No output file specified, using {}", path.display());
        }

        // Check if FILE exists and issue warning.
        if File::open(path).is_ok() {
            if !matches.opt_present("w") {
                error!("Output file {} already exists. Use '-o NAME' to use a different name or '-w' to overwrite!",
                       path.display());
                return None;
            } else {
                warn!("Overwriting output file {}", path.display());
            }
        }

        match File::create(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                       path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened output: {}", path.display());
                Some(Box::new(file))
            }
        }
    }
}

fn main() {
    // handle command line parameters
    let (matches, cfg) = parse_arguments(
        env::args().collect(),
        config::zwreec_options(short_options())
    );

    let mut input = parse_input(&matches);
    let mut output = parse_output(&matches);

    debug!("Parsed command line options");
    info!("Main started");

    // call library
    if !cfg.test_cases.is_empty() {
        zwreec::test_library(cfg, &mut input, &mut output);
    } else {
        // unwrap input and output
        let mut _input = match input {
            Some(i) => i,
            None => panic!("Missing input file! Compile aborted")
        };
        let mut _output = match output {
            Some(o) => o,
            None => panic!("Missing output file! Compile aborted")
        };
        zwreec::compile(cfg, &mut _input, &mut _output);
    }

    info!("Main finished");
}
<commit_msg>Remove output file in case of a panic<commit_after>extern crate zwreec;
extern crate getopts;
extern crate libc;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::{Read,Write};
use std::thread;
use std::path::Path;
use std::process::exit;

use zwreec::config;
use zwreec::config::Config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Returns the primary options for zwreec. It is e.g. used to generate the
/// usage information.
fn short_options() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflagmulti("v", "verbose", "Be more verbose. Can be used multiple times.");
    opts.optflag("q", "quiet", "Be quiet");
    opts.optflag("w", "overwrite", "Overwrite output file if necessary.");
    opts.optflagopt("l", "logfile", "Specify log file (additionally to logging on stderr)", "LOGFILE");
    opts.optopt("o", "", "Name of the output file", "FILE");
    opts.optflag("h", "help", "Display this help and exit");
    opts.optflag("V", "version", "Display version");

    opts
}

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = short_options();

    let brief = format!("Usage: zwreec [-hV] [-vqwf] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}", config::zwreec_usage(verbose, options, &brief));
}

/// Parse command line arguments
///
/// Parses command line arguments to set up the logger and extract input and
/// output parameters. Will display the usage and exit depending on arguments.
///
/// Returns `getopts::Matches` and a `std::fs::File` for the input and output
/// file. The `getopts::Matches` can be used to
///
/// # Examples
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "display this help and exit");
///
/// let (matches, mut input, mut output) = parse_arguments(
///     env::args().collect(),
///     opts
/// );
/// ```
///
/// # Failures
///
/// Depending on encountered arguments or parsing errors this function will
/// print the usage and/or call `exit(1)`.
///
/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>, opts: getopts::Options) -> (getopts::Matches, config::Config) {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            println!("Error: logfile option specified but no logfile name given");
            usage(false);
            exit(1);
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    let cfg = Config::from_matches(&matches);
    (matches, cfg)
}

fn parse_input(matches: &getopts::Matches) -> Option<Box<Read>> {
    if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                error!("Couldn't open {}: {}",
                    path.display(), Error::description(&why));
                None
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                Some(Box::new(file))
            }
        }
    } else if unsafe { libc::isatty(libc::STDIN_FILENO as i32) } == 0 {
        // Not connected to a terminal, assuming safe to read from stdin
        info!("Reading input from stdin");
        Some(Box::new(std::io::stdin()))
    } else {
        None
    }
}

fn parse_path<'a>(matches: &'a getopts::Matches) -> Option<String> {
    let name = matches.opt_str("o").unwrap_or("a.z8".to_string());

    if name == "-" {
        None
    } else {
        Some(name)
    }
}

fn parse_output(matches: &getopts::Matches, path: Option<String>) -> Option<Box<Write>> {
    match path {
        None => {
            // tty requested
            if unsafe { libc::isatty(libc::STDOUT_FILENO as i32)  } == 0 {
                // Not connected to a terminal, assuming safe to write to stdin
                // NOTE: this should be considered unsafe, as the library is *not*
                // guaranteed to only print to stderr
                warn!("Writing to stdout can lead to unusable output!");
                warn!("You should specify an output name using -o 'FILE'");
                info!("Writing output to stdout");
                Some(Box::new(std::io::stdout()))
            } else {
                error!("stdout is connected to a terminal.");
                error!("Zcode is a binary format and should not be printed to a tty.");
                None
            }
        },
        Some(path) => {
            let path = Path::new(&path);

            // opening file
            if path.to_str().unwrap_or("") == "a.z8" {
                debug!("No output file specified, using {}", path.display());
            }

            // Check if FILE exists and issue warning.
            if File::open(path).is_ok() {
                if !matches.opt_present("w") {
                    error!("Output file {} already exists. Use '-o NAME' to use a different name or '-w' to overwrite!",
                           path.display());
                    return None;
                } else {
                    warn!("Overwriting output file {}", path.display());
                }
            }

            match File::create(path) {
                Err(why) => {
                    error!("Couldn't open {}: {}",
                           path.display(), Error::description(&why));
                    None
                },
                Ok(file) => {
                    info!("Opened output: {}", path.display());
                    Some(Box::new(file))
                }
            }
        }
    }
}

fn main() {
    // handle command line parameters
    let (matches, cfg) = parse_arguments(
        env::args().collect(),
        config::zwreec_options(short_options())
    );

    let path = parse_path(&matches);

    let path_copy = path.clone();
    let code = match thread::spawn(move || {
        let mut input = parse_input(&matches);
        let mut output = parse_output(&matches, path_copy);

        debug!("Parsed command line options");
        info!("Compiler started");

        // call library
        if !cfg.test_cases.is_empty() {
            zwreec::test_library(cfg, &mut input, &mut output);
        } else {
            // unwrap input and output
            let mut _input = match input {
                Some(i) => i,
                None => panic!("Missing input file! Compile aborted")
            };
            let mut _output = match output {
                Some(o) => o,
                None => panic!("Missing output file! Compile aborted")
            };
            zwreec::compile(cfg, &mut _input, &mut _output);
        }
    }).join() {
        Err(_) => {
            error!("Compiler failed");
            match path {
                Some(path) => {
                    match std::fs::remove_file(Path::new(&path)) {
                        Err(_) => warn!("Failed to removed unfinished output file"),
                        _ => {},
                    }
                }
                _ => {},
            };
            1
        },
        _ => {
            info!("Compiler finished");
            0
        }
    };

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


<commit_before>use std::ascii::Ascii;
use std::string::String;
use api;
use parsing;

// This struct is used to keep a record of which
// optional commands have been implemented by
// the bot and which haven't

static basic_command_list : &'static str = "protocol_version
name
version
known_command
list_commands
quit
boardsize
clear_board
komi
play
genmove";

pub struct BotHandler {
    genmove_regression: bool,
    undo: bool,
    fixed_handicap: bool,
    place_free_handicap: bool,
    set_free_handicap: bool,
    time_settings: bool,
    final_status_list: bool,
    final_score: bool,
    showboard: bool
}

impl BotHandler {
    fn new() -> BotHandler{
        BotHandler{
            genmove_regression: false,
            undo: false,
            fixed_handicap: false,
            place_free_handicap: false,
            set_free_handicap: false,
            time_settings: false,
            final_status_list: false,
            final_score: false,
            showboard: false
        }
    }

    fn populate<T: api::GoBot>(&mut self, bot: &mut T) {
        match bot.gtp_genmove_regression(api::Black) {
            Err(api::NotImplemented) => self.genmove_regression = false,
            _ => self.genmove_regression = true
        }
        match bot.gtp_undo() {
            Err(api::NotImplemented) => self.undo = false,
            _ => self.undo = true
        }
        match bot.gtp_fixed_handicap(1) {
            Err(api::NotImplemented) => self.fixed_handicap = false,
            _ => self.fixed_handicap = true
        }
        match bot.gtp_place_free_handicap(1) {
            Err(api::NotImplemented) => self.place_free_handicap = false,
            _ => self.place_free_handicap = true
        }
        match bot.gtp_set_free_handicap([api::Vertex::from_coords(2,2).unwrap()]) {
            Err(api::NotImplemented) => self.set_free_handicap = false,
            _ => self.set_free_handicap = true
        }
        match bot.gtp_time_settings(5, 0, 0) {
            Err(api::NotImplemented) => self.time_settings = false,
            _ => self.time_settings = true
        }
        match bot.gtp_final_status_list(api::Alive) {
            Err(api::NotImplemented) => self.final_status_list = false,
            _ => self.final_status_list = true
        }
        match bot.gtp_final_score() {
            Err(api::NotImplemented) => self.final_score = false,
            _ => self.final_score = true
        }
        match bot.gtp_showboard() {
            Err(api::NotImplemented) => self.showboard = false,
            _ => self.showboard = true
        }
        // lets reset the bot
        bot.gtp_clear_board();
    }

    // implementations of GTP commands

    fn cmd_list_commands(&self) -> String {
        let mut list = String::from_str(basic_command_list);
        if self.genmove_regression {
            //list = list.append("\nreg_genmove\nload_sgf");
            list = list.append("\nreg_genmove");
        }
        if self.undo {
            list = list.append("\nundo");
        }
        if self.place_free_handicap {
            list = list.append("\nplace_free_handicap");
        }
        if self.fixed_handicap {
            list = list.append("\nfixed_handicap");
        }
        if self.set_free_handicap {
            list = list.append("\nset_free_handicap");
        }
        if self.time_settings {
            list = list.append("\ntime_settings");
        }
        if self.final_status_list {
            list = list.append("\nfinal_status_list");
        }
        if self.final_score {
            list = list.append("\nfinal_score");
        }
        if self.showboard {
            list = list.append("\nshowboard");
        }
        list
    }

    fn cmd_known_command(&self, cmd: &[Ascii]) -> String {
        format!("{:b}", match cmd.as_str_ascii() {
            "protocol_version" | "name" | "version" |
            "known_command" | "list_commands" | "quit" |
            "boardsize" | "clear_board" | "komi" |
            "play" | "genmove" => true,
            // load_sgf will not be available in a first stage
            // "reg_genmove" | "load_sgf" => self.genmove_regression,
            "reg_genmove" => self.genmove_regression,
            "undo" => self.undo,
            "place_free_handicap" => self.place_free_handicap,
            "set_free_handiciap" | "fixed_handicap" => self.set_free_handicap,
            "time_settings" => self.time_settings,
            "final_status_list" => self.final_status_list,
            "final_score" => self.final_score,
            "showboard" => self.showboard,
            _ => false
        })
    }

    fn cmd_boardsize<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_clear_board<T: api::GoBot>(&self, bot: &mut T) -> () {
        fail!("Not Implemented.");
    }

    fn cmd_komi<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_play<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_genmove<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    // optional functions, should not be called
    // if the bot does not implement their conterpart

    fn cmd_loadsgf<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_reg_genmove<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_fixed_handicap<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_place_free_handicap<T: api::GoBot>(&self, bot: &mut T,  args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_set_free_handicap<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_undo<T: api::GoBot>(&self, bot: &mut T) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_time_settings<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_final_status_list<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool,String) {
        fail!("Not Implemented.");
    }

    fn cmd_final_score<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_showboard<T: api::GoBot>(&self, bot: &mut T) -> String {
        fail!("Not Implemented.");
    }

    // dispatcher

    fn dispatch_cmd<T: api::GoBot>(&self, bot: &mut T, cmd: &[Ascii], args: &[Ascii]) -> (bool, String) {
        match cmd.as_str_ascii() {
            "protocol_version" => (true, String::from_str("2")),
            "name" => (true, bot.gtp_name()),
            "version" => (true, bot.gtp_version()),
            "known_command" => (true, self.cmd_known_command(args)),
            "list_commands" => (true, self.cmd_list_commands()),
            "boardsize" => self.cmd_boardsize(bot, args),
            "clear_board" => {self.cmd_clear_board(bot); (true, String::new())},
            "komi" => self.cmd_komi(bot, args),
            "play" => self.cmd_play(bot, args),
            "genmove" => self.cmd_genmove(bot, args),
            "loadsgf" => match self.genmove_regression {
                true => self.cmd_loadsgf(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "reg_genmove" => match self.genmove_regression {
                true => self.cmd_reg_genmove(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "fixed_handicap" => match self.set_free_handicap {
                true => self.cmd_fixed_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "set_free_handicap" => match self.set_free_handicap {
                true => self.cmd_set_free_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "place_free_handicap" => match self.place_free_handicap {
                true => self.cmd_place_free_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "undo" => match self.undo {
                true => self.cmd_undo(bot),
                false => (false, String::from_str("unknown command"))
            },
            "time_settings" => match self.time_settings {
                true => self.cmd_time_settings(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "time_left" => match self.genmove_regression {
                true => (true, String::new()), // noop for now
                false => (false, String::from_str("unknown command"))
            },
            "final_status_list" => match self.final_status_list {
                true => self.cmd_final_status_list(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "final_score" => match self.final_score {
                true => self.cmd_final_score(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "showboard" => match self.final_status_list {
                true => (true, self.cmd_showboard(bot)),
                false => (false, String::from_str("unknown command"))
            },
            _ => (false, String::from_str("unknown command"))
        }
    }
    // public functions

    pub fn from_bot<T: api::GoBot>(bot: &mut T) -> BotHandler {
        let mut handler = BotHandler::new();
        handler.populate(bot);
        handler
    }

    // handles content from ascii input
    // will parse and execute the first command encountered only
    // do nothing if no command is found
    #[allow(dead_code)]
    pub fn handle_command<T: api::GoBot>(&self, bot: &mut T, input: &[Ascii]) -> String {
        match parsing::parse_command(input) {
            Some(parsing::GTPCommand{id: id, command: command, args: args}) => {
                let (result, output) = self.dispatch_cmd(bot, command.as_slice(), args.as_slice());
                format!("{:c}{:s} {:s}",
                    match result {true => '=', false => '?'},
                    match id {Some(i) => format!("{:u}", i), _ => String::new()},
                    output)
                },
            _ => {String::new()}
        }
    }
}

<commit_msg>Handler implementation.<commit_after>use std::ascii::Ascii;
use std::string::String;
use api;
use parsing;

// This struct is used to keep a record of which
// optional commands have been implemented by
// the bot and which haven't

static basic_command_list : &'static str = "protocol_version
name
version
known_command
list_commands
quit
boardsize
clear_board
komi
play
genmove";

pub struct BotHandler {
    genmove_regression: bool,
    undo: bool,
    fixed_handicap: bool,
    place_free_handicap: bool,
    set_free_handicap: bool,
    time_settings: bool,
    final_status_list: bool,
    final_score: bool,
    showboard: bool
}

impl BotHandler {
    fn new() -> BotHandler{
        BotHandler{
            genmove_regression: false,
            undo: false,
            fixed_handicap: false,
            place_free_handicap: false,
            set_free_handicap: false,
            time_settings: false,
            final_status_list: false,
            final_score: false,
            showboard: false
        }
    }

    fn populate<T: api::GoBot>(&mut self, bot: &mut T) {
        match bot.gtp_genmove_regression(api::Black) {
            Err(api::NotImplemented) => self.genmove_regression = false,
            _ => self.genmove_regression = true
        }
        match bot.gtp_undo() {
            Err(api::NotImplemented) => self.undo = false,
            _ => self.undo = true
        }
        match bot.gtp_fixed_handicap(1) {
            Err(api::NotImplemented) => self.fixed_handicap = false,
            _ => self.fixed_handicap = true
        }
        match bot.gtp_place_free_handicap(1) {
            Err(api::NotImplemented) => self.place_free_handicap = false,
            _ => self.place_free_handicap = true
        }
        match bot.gtp_set_free_handicap([api::Vertex::from_coords(2,2).unwrap()]) {
            Err(api::NotImplemented) => self.set_free_handicap = false,
            _ => self.set_free_handicap = true
        }
        match bot.gtp_time_settings(5, 0, 0) {
            Err(api::NotImplemented) => self.time_settings = false,
            _ => self.time_settings = true
        }
        match bot.gtp_final_status_list(api::Alive) {
            Err(api::NotImplemented) => self.final_status_list = false,
            _ => self.final_status_list = true
        }
        match bot.gtp_final_score() {
            Err(api::NotImplemented) => self.final_score = false,
            _ => self.final_score = true
        }
        match bot.gtp_showboard() {
            Err(api::NotImplemented) => self.showboard = false,
            _ => self.showboard = true
        }
        // lets reset the bot
        bot.gtp_clear_board();
    }

    // implementations of GTP commands

    fn cmd_list_commands(&self) -> String {
        let mut list = String::from_str(basic_command_list);
        if self.genmove_regression {
            //list = list.append("\nreg_genmove\nload_sgf");
            list = list.append("\nreg_genmove");
        }
        if self.undo {
            list = list.append("\nundo");
        }
        if self.place_free_handicap {
            list = list.append("\nplace_free_handicap");
        }
        if self.fixed_handicap {
            list = list.append("\nfixed_handicap");
        }
        if self.set_free_handicap {
            list = list.append("\nset_free_handicap");
        }
        if self.time_settings {
            list = list.append("\ntime_settings");
        }
        if self.final_status_list {
            list = list.append("\nfinal_status_list");
        }
        if self.final_score {
            list = list.append("\nfinal_score");
        }
        if self.showboard {
            list = list.append("\nshowboard");
        }
        list
    }

    fn cmd_known_command(&self, cmd: &[Ascii]) -> String {
        format!("{:b}", match cmd.as_str_ascii() {
            "protocol_version" | "name" | "version" |
            "known_command" | "list_commands" | "quit" |
            "boardsize" | "clear_board" | "komi" |
            "play" | "genmove" => true,
            // load_sgf will not be available in a first stage
            // "reg_genmove" | "load_sgf" => self.genmove_regression,
            "reg_genmove" => self.genmove_regression,
            "undo" => self.undo,
            "place_free_handicap" => self.place_free_handicap,
            "set_free_handiciap" | "fixed_handicap" => self.set_free_handicap,
            "time_settings" => self.time_settings,
            "final_status_list" => self.final_status_list,
            "final_score" => self.final_score,
            "showboard" => self.showboard,
            _ => false
        })
    }

    fn cmd_boardsize<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match from_str::<uint>(args.as_str_ascii()) {
            Some(n) => match bot.gtp_boardsize(n) {
                Ok(()) => (true, String::new()),
                Err(api::InvalidBoardSize) => (false, String::from_str("invalid board size")),
                _ => fail!("Unexpected error in gtp_boardsize.")
            },
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_clear_board<T: api::GoBot>(&self, bot: &mut T) -> () {
        bot.gtp_clear_board();
    }

    fn cmd_komi<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match from_str::<f32>(args.as_str_ascii()) {
            Some(k) => {bot.gtp_komi(k); (true, String::new())},
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_play<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match parsing::parse_args(args, [parsing::ColouredMoveArg]) {
            Some(vect) => match vect[0] {
                parsing::ArgColouredMove(mv) => match bot.gtp_play(mv) {
                    Ok(()) => (true, String::new()),
                    Err(api::InvalidMove) => (false, String::from_str("invalid move")),
                    _ => fail!("Unexpected error in gtp_play.")
                },
                _ => unreachable!() // if parse_args returns a vector, it is valid
            },
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_genmove<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match parsing::parse_args(args, [parsing::ColourArg]) {
            Some(vect) => match vect[0] {
                parsing::ArgColour(col) => (true, bot.gtp_genmove(col).to_string()),
                _ => unreachable!()
            },
            None => (false, String::from_str("syntax error"))
        }
    }

    // optional functions, should not be called
    // if the bot does not implement their conterpart

    fn cmd_loadsgf<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_reg_genmove<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match parsing::parse_args(args, [parsing::ColourArg]) {
            Some(vect) => match vect[0] {
                parsing::ArgColour(col) => match bot.gtp_genmove_regression(col) {
                    Ok(mv) => (true, mv.to_string()),
                    _ => fail!("Unexpected error in gtp_reg_genmove.")
                },
                _ => unreachable!()
            },
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_fixed_handicap<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        match from_str::<uint>(args.as_str_ascii()) {
            Some(n) if n >= 2 && n <= 9 => match bot.gtp_fixed_handicap(n) {
                Ok(vec) => (true, {
                    let mut it = vec.iter();
                    let mut out = it.next().to_string();
                    for &vrtx in it {
                        out = out.append(" ").append(vrtx.to_string().as_slice());
                    };
                    out }),
                Err(api::BoardNotEmpty) => (false, String::from_str("board not empty")),
                _ => fail!("Unexpected error in gtp_boardsize.")
            },
            Some(_) => (false, String::from_str("invalid number of stones")),
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_place_free_handicap<T: api::GoBot>(&self, bot: &mut T,  args: &[Ascii]) -> (bool, String) {
        match from_str::<uint>(args.as_str_ascii()) {
            Some(n) if n >= 2 => match bot.gtp_place_free_handicap(n) {
                Ok(vec) => (true, {
                    let mut it = vec.iter();
                    let mut out = it.next().to_string();
                    for &vrtx in it {
                        out = out.append(" ").append(vrtx.to_string().as_slice());
                    };
                    out }),
                Err(api::BoardNotEmpty) => (false, String::from_str("board not empty")),
                _ => fail!("Unexpected error in gtp_boardsize.")
            },
            Some(_) => (false, String::from_str("invalid number of stones")),
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_set_free_handicap<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_undo<T: api::GoBot>(&self, bot: &mut T) -> (bool, String) {
        match bot.gtp_undo() {
            Ok(()) => (true, String::new()),
            Err(api::CannotUndo) => (false, String::from_str("cannot undo")),
            _ => fail!("Unexpected error in gtp_undo.")
        }
    }

    fn cmd_time_settings<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool, String) {
        fail!("Not Implemented.");
    }

    fn cmd_final_status_list<T: api::GoBot>(&self, bot: &mut T, args: &[Ascii]) -> (bool,String) {
        match parsing::parse_args(args, [parsing::StoneStatusArg]) {
            Some(vect) => match vect[0] {
                parsing::ArgStoneStatus(st) => match bot.gtp_final_status_list(st) {
                    Ok(lst) => {
                        let mut output = String::new();
                        for &vrtx in lst.iter() {
                            output = output.append(vrtx.to_string().as_slice());
                        }
                        (true, output)
                    },
                    _ => fail!("Unexpected error in gtp_final_status_list.")
                },
                _ => unreachable!()
            },
            None => (false, String::from_str("syntax error"))
        }
    }

    fn cmd_final_score<T: api::GoBot>(&self, bot: &mut T) -> (bool, String) {
        match bot.gtp_final_score() {
            Ok(val) => match val {
                (0.0, _) => (true, String::from_str("0")),
                (x, api::White) => (true, format!("w+{}", x)),
                (x, api::Black) => (true, format!("b+{}", x))
                },
            Err(api::CannotScore) => (false, String::from_str("cannot score")),
            _ => fail!("Unexpected error in gtp_final_score.")
        }
    }

    fn cmd_showboard<T: api::GoBot>(&self, bot: &mut T) -> String {
        fail!("Not Implemented.");
    }

    // dispatcher

    fn dispatch_cmd<T: api::GoBot>(&self, bot: &mut T, cmd: &[Ascii], args: &[Ascii]) -> (bool, String) {
        match cmd.as_str_ascii() {
            "protocol_version" => (true, String::from_str("2")),
            "name" => (true, bot.gtp_name()),
            "version" => (true, bot.gtp_version()),
            "known_command" => (true, self.cmd_known_command(args)),
            "list_commands" => (true, self.cmd_list_commands()),
            "boardsize" => self.cmd_boardsize(bot, args),
            "clear_board" => {self.cmd_clear_board(bot); (true, String::new())},
            "komi" => self.cmd_komi(bot, args),
            "play" => self.cmd_play(bot, args),
            "genmove" => self.cmd_genmove(bot, args),
            "loadsgf" => match self.genmove_regression {
                true => self.cmd_loadsgf(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "reg_genmove" => match self.genmove_regression {
                true => self.cmd_reg_genmove(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "fixed_handicap" => match self.set_free_handicap {
                true => self.cmd_fixed_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "set_free_handicap" => match self.set_free_handicap {
                true => self.cmd_set_free_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "place_free_handicap" => match self.place_free_handicap {
                true => self.cmd_place_free_handicap(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "undo" => match self.undo {
                true => self.cmd_undo(bot),
                false => (false, String::from_str("unknown command"))
            },
            "time_settings" => match self.time_settings {
                true => self.cmd_time_settings(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "time_left" => match self.genmove_regression {
                true => (true, String::new()), // noop for now
                false => (false, String::from_str("unknown command"))
            },
            "final_status_list" => match self.final_status_list {
                true => self.cmd_final_status_list(bot, args),
                false => (false, String::from_str("unknown command"))
            },
            "final_score" => match self.final_score {
                true => self.cmd_final_score(bot),
                false => (false, String::from_str("unknown command"))
            },
            "showboard" => match self.final_status_list {
                true => (true, self.cmd_showboard(bot)),
                false => (false, String::from_str("unknown command"))
            },
            _ => (false, String::from_str("unknown command"))
        }
    }
    // public functions

    pub fn from_bot<T: api::GoBot>(bot: &mut T) -> BotHandler {
        let mut handler = BotHandler::new();
        handler.populate(bot);
        handler
    }

    // handles content from ascii input
    // will parse and execute the first command encountered only
    // do nothing if no command is found
    #[allow(dead_code)]
    pub fn handle_command<T: api::GoBot>(&self, bot: &mut T, input: &[Ascii]) -> String {
        match parsing::parse_command(input) {
            Some(parsing::GTPCommand{id: id, command: command, args: args}) => {
                let (result, output) = self.dispatch_cmd(bot, command.as_slice(), args.as_slice());
                format!("{:c}{:s} {:s}",
                    match result {true => '=', false => '?'},
                    match id {Some(i) => format!("{:u}", i), _ => String::new()},
                    output)
                },
            _ => {String::new()}
        }
    }
}

<|endoftext|>


<commit_before>extern crate cargotest;
extern crate hamcrest;

use cargotest::support::{project, execs, main_file, basic_bin_manifest};
use hamcrest::{assert_that};

fn remove_all_whitespace(s: &str) -> String {
    s.split_whitespace().collect()
}

fn read_manifest_output() -> String {
    remove_all_whitespace(r#"
{
    "name":"foo",
    "version":"0.5.0",
    "id":"foo[..]0.5.0[..](path+file://[..]/foo)",
    "license": null,
    "license_file": null,
    "source":null,
    "dependencies":[],
    "targets":[{
        "kind":["bin"],
        "name":"foo",
        "src_path":"src[/]foo.rs"
    }],
    "features":{},
    "manifest_path":"[..]Cargo.toml"
}"#)
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_relative() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg("foo/Cargo.toml")
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_absolute() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg(p.root().join("Cargo.toml"))
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_parent_relative() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg("foo")
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(101)
                       .with_stderr("[ERROR] the manifest-path must be \
                                             a path to a Cargo.toml file"));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_parent_absolute() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg(p.root())
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(101)
                       .with_stderr("[ERROR] the manifest-path must be \
                                             a path to a Cargo.toml file"));
}

#[test]
fn cargo_read_manifest_cwd() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .cwd(p.root()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}
<commit_msg>Revert tests/read-manifest.rs<commit_after>extern crate cargotest;
extern crate hamcrest;

use cargotest::support::{project, execs, main_file, basic_bin_manifest};
use hamcrest::{assert_that};

fn remove_all_whitespace(s: &str) -> String {
    s.split_whitespace().collect()
}

fn read_manifest_output() -> String {
    remove_all_whitespace(r#"
{
    "name":"foo",
    "version":"0.5.0",
    "id":"foo[..]0.5.0[..](path+file://[..]/foo)",
    "license": null,
    "license_file": null,
    "source":null,
    "dependencies":[],
    "targets":[{
        "kind":["bin"],
        "name":"foo",
        "src_path":"src[..]foo.rs"
    }],
    "features":{},
    "manifest_path":"[..]Cargo.toml"
}"#)
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_relative() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg("foo/Cargo.toml")
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_absolute() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg(p.root().join("Cargo.toml"))
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_parent_relative() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg("foo")
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(101)
                       .with_stderr("[ERROR] the manifest-path must be \
                                             a path to a Cargo.toml file"));
}

#[test]
fn cargo_read_manifest_path_to_cargo_toml_parent_absolute() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .arg("--manifest-path").arg(p.root())
                 .cwd(p.root().parent().unwrap()),
                execs().with_status(101)
                       .with_stderr("[ERROR] the manifest-path must be \
                                             a path to a Cargo.toml file"));
}

#[test]
fn cargo_read_manifest_cwd() {
    let p = project("foo")
        .file("Cargo.toml", &basic_bin_manifest("foo"))
        .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));

    assert_that(p.cargo_process("read-manifest")
                 .cwd(p.root()),
                execs().with_status(0)
                       .with_stdout(read_manifest_output()));
}
<|endoftext|>


<commit_before>use tileset::FlipContext;
use map::{Adjacents};
use sdl2::pixels::Color;

#[derive(Clone, Copy)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

pub fn get_tileset_sprite(adjacents: &[bool; 8]) -> (u32, u32, FlipContext) {
    let adjacent_tiles = (
        adjacents[0],
        adjacents[1],
        adjacents[2],
        adjacents[3],
        adjacents[4],
        adjacents[5],
        adjacents[6],
        adjacents[7],
    );
    match adjacent_tiles {
        (
            _,     true,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 0, FlipContext::FlipNone)  // Standalone
        },
        (
            _,     true,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 1, FlipContext::FlipNone)  // Top nub
        },
        (
            _,     false,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 2, FlipContext::FlipNone)  // Vertical bar
        },
        (
            _,     false,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 3, FlipContext::FlipNone)  // Bottom nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipNone)  // Left nub
        },
        (
            _,     true,  _,
            false,        false,
            _,     true,  _
        ) => {
            (7, 1, FlipContext::FlipNone)  // Horizontal bar
        },
        (
            _,     true,  _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipHorizontal)  // Right nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     false, false
        ) => {
            (1, 0, FlipContext::FlipNone)  // Top-left
        },
        (
            _,     false,  false,
            true,         false,
            _,     false, false
        ) => {
            (1, 1, FlipContext::FlipNone)  // Left
        },
        (
            _,     false, false,
            true,         false,
            _,     true , _
        ) => {
            (1, 2, FlipContext::FlipNone)  // Bottom-left
        },
        (
            _,     true,  _,
            false,        true,
            false, false, _
        ) => {
            (1, 0, FlipContext::FlipHorizontal)  // Top-right
        },
        (
            false,  false, _,
            false,         true,
            false,  false, _
        ) => {
            (1, 1, FlipContext::FlipHorizontal)  // Right
        },
        (
            false, false, _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 2, FlipContext::FlipHorizontal)  // Bottom-right
        },
        (
            _, true,      _,
            _,            _,
            false, false, false
        ) => {
            (6, 1, FlipContext::FlipNone)  // Top
        },
        (
            false, false, false,
            _,            _,
            _, true,      _
        ) => {
            (6, 3, FlipContext::FlipNone)  // Bottom
        },
        (
            _,    true,   _,
            false,        false,
            false, false, true
        ) => {
            (3, 0, FlipContext::FlipHorizontal)  // T
        },
        (
            _,    true,  _,
            false,       false,
            true, false, false
        ) => {
            (3, 0, FlipContext::FlipNone)  // T
        },
        (
            true, false, false,
            false,       false,
            true, true,  false
        ) => {
            (3, 2, FlipContext::FlipNone)  // T
        },
        (
            false, false, true,
            false,        false,
            false, true,  true
        ) => {
            (3, 2, FlipContext::FlipHorizontal)  // T
        },
        (
            true,  false, false,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipNone)  // Inside elbow top left
        },
        (
            false, false, true,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipHorizontal)  // Inside elbow top right
        },
        (
            false, false, false,
            false,        false,
            true, false, false
        ) => {
            (2, 2, FlipContext::FlipNone)  // Inside elbow bottom left
        },
        (
            false, false, false,
            false,        false,
            false, false, true
        ) => {
            (2, 2, FlipContext::FlipHorizontal)  // Inside elbow bottom right
        },
        (
            _, _, _,
            _,    _,
            _, _, _
        ) => {
            (6, 2, FlipContext::FlipNone)
        }
    }
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }

    pub fn get_color(&self) -> Color {
        let BLACK = Color::RGB(0, 0, 0);
        let BROWN = Color::RGB(100, 100, 0);
        let GREY = Color::RGB(100, 100, 100);
        let WHITE = Color::RGB(255, 255, 255);
        match *self {
            CellState::Dirt => BROWN,
            CellState::Stone => GREY,
            CellState::Wall => WHITE,
            CellState::Empty => BLACK
        }
    }

    pub fn get_sprite(&self, adjacents: &Adjacents) -> Option<(u32, u32, FlipContext)> {
        match *self {
            CellState::Dirt => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y, context))
            },
            CellState::Stone => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty | CellState::Dirt => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y + 12, context))
            },
            CellState::Wall => Some((2, 5, FlipContext::FlipNone)),
            CellState::Empty => None
        }
    }
}

<commit_msg>Simplified<commit_after>use tileset::FlipContext;
use map::{Adjacents};
use sdl2::pixels::Color;

#[derive(Clone, Copy)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

pub fn get_tileset_sprite(adjacents: &[bool; 8]) -> (u32, u32, FlipContext) {
    let adjacent_tiles = (
        adjacents[0],
        adjacents[1],
        adjacents[2],
        adjacents[3],
        adjacents[4],
        adjacents[5],
        adjacents[6],
        adjacents[7],
    );
    match adjacent_tiles {
        (
            _,     true,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 0, FlipContext::FlipNone)  // Standalone
        },
        (
            _,     true,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 1, FlipContext::FlipNone)  // Top nub
        },
        (
            _,     false,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 2, FlipContext::FlipNone)  // Vertical bar
        },
        (
            _,     false,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 3, FlipContext::FlipNone)  // Bottom nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipNone)  // Left nub
        },
        (
            _,     true,  _,
            false,        false,
            _,     true,  _
        ) => {
            (7, 1, FlipContext::FlipNone)  // Horizontal bar
        },
        (
            _,     true,  _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipHorizontal)  // Right nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     false, false
        ) => {
            (1, 0, FlipContext::FlipNone)  // Top-left
        },
        (
            _,     false,  false,
            true,         false,
            _,     false, false
        ) => {
            (1, 1, FlipContext::FlipNone)  // Left
        },
        (
            _,     false, false,
            true,         false,
            _,     true , _
        ) => {
            (1, 2, FlipContext::FlipNone)  // Bottom-left
        },
        (
            _,     true,  _,
            false,        true,
            false, false, _
        ) => {
            (1, 0, FlipContext::FlipHorizontal)  // Top-right
        },
        (
            false,  false, _,
            false,         true,
            false,  false, _
        ) => {
            (1, 1, FlipContext::FlipHorizontal)  // Right
        },
        (
            false, false, _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 2, FlipContext::FlipHorizontal)  // Bottom-right
        },
        (
            _, true,      _,
            _,            _,
            false, false, false
        ) => {
            (6, 1, FlipContext::FlipNone)  // Top
        },
        (
            false, false, false,
            _,            _,
            _, true,      _
        ) => {
            (6, 3, FlipContext::FlipNone)  // Bottom
        },
        (
            _,    true,   _,
            false,        false,
            false, false, true
        ) => {
            (3, 0, FlipContext::FlipHorizontal)  // T
        },
        (
            _,    true,  _,
            false,       false,
            true, false, false
        ) => {
            (3, 0, FlipContext::FlipNone)  // T
        },
        (
            true, false, false,
            false,       false,
            _, true,  false
        ) => {
            (3, 2, FlipContext::FlipNone)  // T
        },
        (
            false, false, true,
            false,        false,
            false, true,  _
        ) => {
            (3, 2, FlipContext::FlipHorizontal)  // T
        },
        (
            true,  false, false,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipNone)  // Inside elbow top left
        },
        (
            false, false, true,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipHorizontal)  // Inside elbow top right
        },
        (
            false, false, false,
            false,        false,
            true, false, false
        ) => {
            (2, 2, FlipContext::FlipNone)  // Inside elbow bottom left
        },
        (
            false, false, false,
            false,        false,
            false, false, true
        ) => {
            (2, 2, FlipContext::FlipHorizontal)  // Inside elbow bottom right
        },
        (
            _, _, _,
            _,    _,
            _, _, _
        ) => {
            (6, 2, FlipContext::FlipNone)
        }
    }
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }

    pub fn get_color(&self) -> Color {
        let BLACK = Color::RGB(0, 0, 0);
        let BROWN = Color::RGB(100, 100, 0);
        let GREY = Color::RGB(100, 100, 100);
        let WHITE = Color::RGB(255, 255, 255);
        match *self {
            CellState::Dirt => BROWN,
            CellState::Stone => GREY,
            CellState::Wall => WHITE,
            CellState::Empty => BLACK
        }
    }

    pub fn get_sprite(&self, adjacents: &Adjacents) -> Option<(u32, u32, FlipContext)> {
        match *self {
            CellState::Dirt => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y, context))
            },
            CellState::Stone => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty | CellState::Dirt => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y + 12, context))
            },
            CellState::Wall => Some((2, 5, FlipContext::FlipNone)),
            CellState::Empty => None
        }
    }
}

<|endoftext|>