<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.

//! BED reading and writing.
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::bed;
//! let reader = bed::Reader::new(io::stdin());
//! ```

use std::io;
use std::str;

use csv;

pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl<R: io::Read> Reader<R> {
    pub fn new(reader: R) -> Self {
        Reader { inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false) }
    }

    pub fn records(&mut self) -> csv::DecodedRecords<R, Record> {
        self.inner.decode()
    }
}

pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl<W: io::Write> Writer<W> {
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        self.inner.encode(record)
    }
}

/// A BED record as defined by UCSC (https://genome.ucsc.edu/FAQ/FAQformat.html#format1)
#[derive(RustcEncodable)]
#[derive(Debug)]
pub struct Record<'a> {
    pub chrom: &str,
    pub start: u64,
    pub end: u64,
    fields: Vec<&'a str>
}

impl Record {
    pub fn new() -> Self {
        Record { chrom: "", start: 0, end: 0, fields: vec![] }
    }

    pub fn is_empty() -> bool {
        self.chrom.len() == 0
    }

    pub fn clear() {
        self.chrom = "";
    }

    pub fn name(&self) -> Option<&str> {
        self.field(3)
    }

    pub fn score(&self) -> Option<&str> {
        self.field(4)
    }

    pub fn is_forward_strand(&self) -> Option<bool> {
        match self.field(5) {
            Some(v) => if v == "+" { Some(true) } else if v == "-" { Some(false) } else { None },
            None    => None
        }
    }

    pub fn field(&self, i: usize) -> Option<&str> {
        let j = i - 3;
        if self.fields.len() > j {
            Some(&self.fields[j])
        }
        else {
            None
        }
    }

    pub fn set_name(&mut self, name: &str) {
        if self.fields.len() < 1 {
            self.fields.push(name);
        }
        else {
            self.fields[0] = name;
        }
    }

    pub fn set_score(&mut self, score: &str) {
        if self.fields.len() < 1 {
            self.fields.push(b"");
        }
        if self.fields.len() < 2 {
            self.fields.push(score);
        }
        else {
            self.fields[1] = score;
        }
    }

    pub fn push_field(&mut self, field: &str) {
        self.fields.push(field);
    }
}

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

    const BED_FILE: &'static [u8] = b"chr1\t5\t500";
//2\t3\t5005\tname2\tup";

    #[test]
    fn test_reader() {
        let chroms = [b"1", b"2"];
        let starts = [5, 2];
        let ends = [5000, 5005];
        let names = ["name1", "name2"];
        let scores = ["up", "up"];

        let mut reader = Reader::new(BED_FILE);
        for (i, r) in reader.records().enumerate() {
            println!("{:?}", r);
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.chrom(), chroms[i]);
            assert_eq!(record.start(), starts[i]);
            assert_eq!(record.end(), ends[i]);
            assert_eq!(record.name().unwrap(), names[i]);
            assert_eq!(record.score().unwrap(), scores[i]);
        }
    }
}
<commit_msg>Finalized BED io.<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.

//! BED reading and writing.
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::bed;
//! let reader = bed::Reader::new(io::stdin());
//! ```

use std::io;

use csv;

pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl<R: io::Read> Reader<R> {
    pub fn new(reader: R) -> Self {
        Reader { inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false) }
    }

    pub fn records(&mut self) -> Records<R> {
        Records { inner: self.inner.decode() }
    }
}

pub struct Records<'a, R: 'a +io::Read> {
    inner: csv::DecodedRecords<'a, R, (String, u64, u64, Vec<String>)>
}

impl<'a, R: io::Read> Iterator for Records<'a, R> {
    type Item = csv::Result<Record>;

    fn next(&mut self) -> Option<csv::Result<Record>> {
        self.inner.next().map(|res| match res {
            Ok((chrom, start, end, aux)) => Ok(Record {
                chrom: chrom, start: start, end: end, aux: aux
            }),
            Err(e) => Err(e)
        })
    }
}

pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl<W: io::Write> Writer<W> {
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        if record.aux.len() == 0 {
            self.inner.encode((record.chrom, record.start, record.end))
        }
        else {
            self.inner.encode(record)
        }
    }
}

/// A BED record as defined by BEDtools (http://bedtools.readthedocs.org/en/latest/content/general-usage.html)
#[derive(RustcEncodable)]
pub struct Record {
    chrom: String,
    start: u64,
    end: u64,
    aux: Vec<String>
}

impl Record {
    pub fn new() -> Self {
        Record { chrom: "".to_string(), start: 0, end: 0, aux: vec![] }
    }

    pub fn chrom(&self) -> &str {
        &self.chrom
    }

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

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

    pub fn name(&self) -> Option<&str> {
        self.aux(3)
    }

    pub fn score(&self) -> Option<&str> {
        self.aux(4)
    }

    pub fn strand(&self) -> Option<Strand> {
        match self.aux(5) {
            Some("+") => Some(Strand::Forward),
            Some("-") => Some(Strand::Reverse),
            _         => None
        }
    }

    pub fn aux(&self, i: usize) -> Option<&str> {
        let j = i - 3;
        if j < self.aux.len() {
            Some(&self.aux[j])
        }
        else {
            None
        }
    }

    pub fn set_chrom(&mut self, chrom: &str) {
        self.chrom = chrom.to_string();
    }

    pub fn set_start(&mut self, start: u64) {
        self.start = start;
    }

    pub fn set_end(&mut self, end: u64) {
        self.end = end;
    }

    pub fn set_name(&mut self, name: &str) {
        if self.aux.len() < 1 {
            self.aux.push(name.to_string());
        }
        else {
            self.aux[0] = name.to_string();
        }
    }

    pub fn set_score(&mut self, score: &str) {
        if self.aux.len() < 1 {
            self.aux.push("".to_string());
        }
        if self.aux.len() < 2 {
            self.aux.push(score.to_string());
        }
        else {
            self.aux[1] = score.to_string();
        }
    }

    pub fn push_aux(&mut self, field: &str) {
        self.aux.push(field.to_string());
    }
}

pub enum Strand {
    Forward,
    Reverse,
}

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

    const BED_FILE: &'static [u8] = b"1\t5\t5000\tname1\tup
2\t3\t5005\tname2\tup
";

    #[test]
    fn test_reader() {
        let chroms = ["1", "2"];
        let starts = [5, 3];
        let ends = [5000, 5005];
        let names = ["name1", "name2"];
        let scores = ["up", "up"];

        let mut reader = Reader::new(BED_FILE);
        for (i, r) in reader.records().enumerate() {
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.chrom(), chroms[i]);
            assert_eq!(record.start(), starts[i]);
            assert_eq!(record.end(), ends[i]);
            assert_eq!(record.name().expect("Error reading name"), names[i]);
            assert_eq!(record.score().expect("Error reading score"), scores[i]);
        }
    }

    #[test]
    fn test_writer() {
        let mut reader = Reader::new(BED_FILE);
        let mut writer = Writer::new(vec![]);
        for r in reader.records() {
            writer.write(r.ok().expect("Error reading record"));
        }
        assert_eq!(writer.inner.as_bytes(), BED_FILE);
    }
}
<|endoftext|>


<commit_before>//! Saving and loading data to and from disk.
//!
//! # HDF5
//!
//! The recommended way to save/load data in Numeric is using HDF5.
//!
//! **Note:** The HDF5 library will by default not be thread-safe (it depends on how you compiled
//! it), so do not call either of these functions concurrently.
//!
//! ##Saving to HDF5 file:
//!
//! ```no_run
//! use std::path::Path;
//! use numeric::Tensor;
//!
//! let path = Path::new("output.h5");
//! let t: Tensor<i32> = Tensor::range(100);
//! let ret = t.save_hdf5(&path);
//! ```
//! The data will be saved to the group `/data`.
//!
//! ## Loading from HDF5 file
//!
//! Now, we can load this file:
//!
//! ```no_run
//! use std::path::Path;
//! use numeric::Tensor;
//!
//! let path = Path::new("output.h5");
//! let t = match numeric::io::load_hdf5_as_f64(&path, "/data") {
//!     Ok(v) => v,
//!     Err(e) => panic!("Failed: {}", e),
//! };
//! ```
//!
//! Note that since we need to know the type of `t` at compile time, it doesn't matter that we
//! saved the file as `i32`, we have to specify how to load it. The way this is done is that it
//! will load the `i32` natively and then convert it to `f64`. If your data converted, you simply
//! have to load it as the same type as you know is in the file.

extern crate std;

use libc::{c_char, c_void};
use std::path::Path;
use hdf5_sys as ffi;

use tensor::Tensor;

extern fn error_handler(_: ffi::hid_t, _: *const c_void) {
    // Suppress errors. We will rely on return statuses alone.
}

macro_rules! add_save {
    ($t:ty, $h5type:expr) => (
        impl Tensor<$t> {
            /// Saves tensor to an HDF5 file.
            ///
            /// **Warning**: This function is not thread-safe (unless you compiled HDF5 to be
            /// thread-safe). Do no call this function concurrently from multiple threads.
            pub fn save_hdf5(&self, path: &Path) -> std::io::Result<()> {
                let filename = match path.to_str() {
                    Some(v) => v,
                    None => {
                        let msg = format!("Path could not be converted to string: {:?}", path);
                        let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, msg);
                        return Err(err);
                    },
                };
                // This could be made an option
                let group = "data";

                unsafe {
                    let filename_cstr = try!(::std::ffi::CString::new(filename));
                    let group_cstr = try!(::std::ffi::CString::new(group));

                    //ffi::H5Eset_auto2(0, error_handler, 0 as *const c_void);

                    let file = ffi::H5Fcreate(filename_cstr.as_ptr() as *const c_char,
                                   ffi::H5F_ACC_TRUNC, ffi::H5P_DEFAULT, ffi::H5P_DEFAULT);

                    let mut shape: Vec<u64> = Vec::new();
                    for s in self.shape().iter() {
                        shape.push(*s as u64);
                    }

                    let space = ffi::H5Screate_simple(shape.len() as i32, shape.as_ptr(),
                                                      std::ptr::null());

                    let dset = ffi::H5Dcreate2(file, group_cstr.as_ptr() as *const c_char,
                                               $h5type, space,
                                               ffi::H5P_DEFAULT,
                                               ffi::H5P_DEFAULT,
                                               ffi::H5P_DEFAULT);

                    let status = ffi::H5Dwrite(dset, $h5type, ffi::H5S_ALL, ffi::H5S_ALL,
                                               ffi::H5P_DEFAULT, self.as_ptr() as * const c_void);

                    if status < 0 {
                        let msg = format!("Failed to write '{}': {:?}", group, path);
                        let err = std::io::Error::new(std::io::ErrorKind::Other, msg);
                        return Err(err);
                    }

                    ffi::H5Dclose(dset);
                    ffi::H5Fclose(file);
                }
                Ok(())
            }
        }
    )
}

add_save!(u8, ffi::H5T_NATIVE_UINT8);
add_save!(u16, ffi::H5T_NATIVE_UINT16);
add_save!(u32, ffi::H5T_NATIVE_UINT32);
add_save!(u64, ffi::H5T_NATIVE_UINT64);
add_save!(i8, ffi::H5T_NATIVE_INT8);
add_save!(i16, ffi::H5T_NATIVE_INT16);
add_save!(i32, ffi::H5T_NATIVE_INT32);
add_save!(i64, ffi::H5T_NATIVE_INT64);
add_save!(f32, ffi::H5T_NATIVE_FLOAT);
add_save!(f64, ffi::H5T_NATIVE_DOUBLE);

macro_rules! add_load {
    ($name:ident, $t:ty) => (
        /// Load HDF5 file and convert to specified type.
        pub fn $name(path: &Path, group: &str) -> std::io::Result<Tensor<$t>> {
            let filename = match path.to_str() {
                Some(v) => v,
                None => {
                    let msg = format!("Path could not be converted to string: {:?}", path);
                    let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, msg);
                    return Err(err);
                },
            };
            unsafe {
                let filename_cstr = try!(::std::ffi::CString::new(filename));
                let group_cstr = try!(::std::ffi::CString::new(group));

                ffi::H5Eset_auto2(0, error_handler, 0 as *const c_void);

                let file = ffi::H5Fopen(filename_cstr.as_ptr() as *const c_char,
                               ffi::H5F_ACC_RDONLY, ffi::H5P_DEFAULT);

                if file < 0 {
                    let msg = format!("File not found: {:?}", path);
                    let err = std::io::Error::new(std::io::ErrorKind::NotFound, msg);
                    return Err(err);
                }

                let dset = ffi::H5Dopen2(file, group_cstr.as_ptr() as *const c_char,
                                        ffi::H5P_DEFAULT);

                if dset < 0 {
                    let msg = format!("Group '{}' not found: {}", group, filename);
                    let err = std::io::Error::new(std::io::ErrorKind::NotFound, msg);
                    return Err(err);
                }

                let datatype = ffi::H5Dget_type(dset);

                let space = ffi::H5Dget_space(dset);
                let ndims = ffi::H5Sget_simple_extent_ndims(space);

                let mut shape: Tensor<ffi::hsize_t> = Tensor::zeros(&[ndims as usize]);

                if ffi::H5Sget_simple_extent_dims(space, shape.as_mut_ptr(),
                                                  0 as *mut ffi::hsize_t) != ndims {
                    let msg = format!("Could not read shape of tesor: {}", filename);
                    let err = std::io::Error::new(std::io::ErrorKind::InvalidData, msg);
                    return Err(err);
                }

                //let unsigned_shape: Vec<usize> = shape.iter().map(|x| x as usize).collect();
                let unsigned_tensor = shape.convert::<usize>();
                let unsigned_shape = &unsigned_tensor.data();

                let data: Tensor<$t> = {
                    if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT8) == 1 {
                        let mut native_data: Tensor<u8> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT8, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT8) == 1 {
                        let mut native_data: Tensor<i8> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT8, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT16) == 1 {
                        let mut native_data: Tensor<u16> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT16, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT16) == 1 {
                        let mut native_data: Tensor<i16> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT16, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT32) == 1 {
                        let mut native_data: Tensor<u32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT32, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT32) == 1 {
                        let mut native_data: Tensor<i32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT32, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT64) == 1 {
                        let mut native_data: Tensor<u64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT64, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT64) == 1 {
                        let mut native_data: Tensor<i64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT64, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_FLOAT) == 1 {
                        let mut native_data: Tensor<f32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_FLOAT, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_DOUBLE) == 1 {
                        let mut native_data: Tensor<f64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_DOUBLE, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else {
                        let msg = format!("Unable to convert '{}' to {}: {}",
                                          group, "f64", filename);
                        let err = std::io::Error::new(std::io::ErrorKind::InvalidData, msg);
                        return Err(err);
                    }
                };

                ffi::H5Tclose(datatype);
                ffi::H5Dclose(dset);
                ffi::H5Fclose(file);

                Ok(data)
            }
        }
    )
}

add_load!(load_hdf5_as_u8, u8);
add_load!(load_hdf5_as_u16, u16);
add_load!(load_hdf5_as_u32, u32);
add_load!(load_hdf5_as_u64, u64);
add_load!(load_hdf5_as_i8, i8);
add_load!(load_hdf5_as_i16, i16);
add_load!(load_hdf5_as_i32, i32);
add_load!(load_hdf5_as_i64, i64);
add_load!(load_hdf5_as_f32, f32);
add_load!(load_hdf5_as_f64, f64);
add_load!(load_hdf5_as_isize, isize);
add_load!(load_hdf5_as_usize, usize);
<commit_msg>DOC: Fixed typo<commit_after>//! Saving and loading data to and from disk.
//!
//! # HDF5
//!
//! The recommended way to save/load data in Numeric is using HDF5.
//!
//! **Note:** The HDF5 library will by default not be thread-safe (it depends on how you compiled
//! it), so do not call either of these functions concurrently.
//!
//! ##Saving to HDF5 file:
//!
//! ```no_run
//! use std::path::Path;
//! use numeric::Tensor;
//!
//! let path = Path::new("output.h5");
//! let t: Tensor<i32> = Tensor::range(100);
//! let ret = t.save_hdf5(&path);
//! ```
//! The data will be saved to the group `/data`.
//!
//! ## Loading from HDF5 file
//!
//! Now, we can load this file:
//!
//! ```no_run
//! use std::path::Path;
//! use numeric::Tensor;
//!
//! let path = Path::new("output.h5");
//! let t = match numeric::io::load_hdf5_as_f64(&path, "/data") {
//!     Ok(v) => v,
//!     Err(e) => panic!("Failed: {}", e),
//! };
//! ```
//!
//! Note that since we need to know the type of `t` at compile time, it doesn't matter that we
//! saved the file as `i32`, we have to specify how to load it. The way this is done is that it
//! will load the `i32` natively and then convert it to `f64`. If you do not want your data to be
//! converted, you simply have to load it as the same type as you know is in the file.

extern crate std;

use libc::{c_char, c_void};
use std::path::Path;
use hdf5_sys as ffi;

use tensor::Tensor;

extern fn error_handler(_: ffi::hid_t, _: *const c_void) {
    // Suppress errors. We will rely on return statuses alone.
}

macro_rules! add_save {
    ($t:ty, $h5type:expr) => (
        impl Tensor<$t> {
            /// Saves tensor to an HDF5 file.
            ///
            /// **Warning**: This function is not thread-safe (unless you compiled HDF5 to be
            /// thread-safe). Do no call this function concurrently from multiple threads.
            pub fn save_hdf5(&self, path: &Path) -> std::io::Result<()> {
                let filename = match path.to_str() {
                    Some(v) => v,
                    None => {
                        let msg = format!("Path could not be converted to string: {:?}", path);
                        let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, msg);
                        return Err(err);
                    },
                };
                // This could be made an option
                let group = "data";

                unsafe {
                    let filename_cstr = try!(::std::ffi::CString::new(filename));
                    let group_cstr = try!(::std::ffi::CString::new(group));

                    //ffi::H5Eset_auto2(0, error_handler, 0 as *const c_void);

                    let file = ffi::H5Fcreate(filename_cstr.as_ptr() as *const c_char,
                                   ffi::H5F_ACC_TRUNC, ffi::H5P_DEFAULT, ffi::H5P_DEFAULT);

                    let mut shape: Vec<u64> = Vec::new();
                    for s in self.shape().iter() {
                        shape.push(*s as u64);
                    }

                    let space = ffi::H5Screate_simple(shape.len() as i32, shape.as_ptr(),
                                                      std::ptr::null());

                    let dset = ffi::H5Dcreate2(file, group_cstr.as_ptr() as *const c_char,
                                               $h5type, space,
                                               ffi::H5P_DEFAULT,
                                               ffi::H5P_DEFAULT,
                                               ffi::H5P_DEFAULT);

                    let status = ffi::H5Dwrite(dset, $h5type, ffi::H5S_ALL, ffi::H5S_ALL,
                                               ffi::H5P_DEFAULT, self.as_ptr() as * const c_void);

                    if status < 0 {
                        let msg = format!("Failed to write '{}': {:?}", group, path);
                        let err = std::io::Error::new(std::io::ErrorKind::Other, msg);
                        return Err(err);
                    }

                    ffi::H5Dclose(dset);
                    ffi::H5Fclose(file);
                }
                Ok(())
            }
        }
    )
}

add_save!(u8, ffi::H5T_NATIVE_UINT8);
add_save!(u16, ffi::H5T_NATIVE_UINT16);
add_save!(u32, ffi::H5T_NATIVE_UINT32);
add_save!(u64, ffi::H5T_NATIVE_UINT64);
add_save!(i8, ffi::H5T_NATIVE_INT8);
add_save!(i16, ffi::H5T_NATIVE_INT16);
add_save!(i32, ffi::H5T_NATIVE_INT32);
add_save!(i64, ffi::H5T_NATIVE_INT64);
add_save!(f32, ffi::H5T_NATIVE_FLOAT);
add_save!(f64, ffi::H5T_NATIVE_DOUBLE);

macro_rules! add_load {
    ($name:ident, $t:ty) => (
        /// Load HDF5 file and convert to specified type.
        pub fn $name(path: &Path, group: &str) -> std::io::Result<Tensor<$t>> {
            let filename = match path.to_str() {
                Some(v) => v,
                None => {
                    let msg = format!("Path could not be converted to string: {:?}", path);
                    let err = std::io::Error::new(std::io::ErrorKind::InvalidInput, msg);
                    return Err(err);
                },
            };
            unsafe {
                let filename_cstr = try!(::std::ffi::CString::new(filename));
                let group_cstr = try!(::std::ffi::CString::new(group));

                ffi::H5Eset_auto2(0, error_handler, 0 as *const c_void);

                let file = ffi::H5Fopen(filename_cstr.as_ptr() as *const c_char,
                               ffi::H5F_ACC_RDONLY, ffi::H5P_DEFAULT);

                if file < 0 {
                    let msg = format!("File not found: {:?}", path);
                    let err = std::io::Error::new(std::io::ErrorKind::NotFound, msg);
                    return Err(err);
                }

                let dset = ffi::H5Dopen2(file, group_cstr.as_ptr() as *const c_char,
                                        ffi::H5P_DEFAULT);

                if dset < 0 {
                    let msg = format!("Group '{}' not found: {}", group, filename);
                    let err = std::io::Error::new(std::io::ErrorKind::NotFound, msg);
                    return Err(err);
                }

                let datatype = ffi::H5Dget_type(dset);

                let space = ffi::H5Dget_space(dset);
                let ndims = ffi::H5Sget_simple_extent_ndims(space);

                let mut shape: Tensor<ffi::hsize_t> = Tensor::zeros(&[ndims as usize]);

                if ffi::H5Sget_simple_extent_dims(space, shape.as_mut_ptr(),
                                                  0 as *mut ffi::hsize_t) != ndims {
                    let msg = format!("Could not read shape of tesor: {}", filename);
                    let err = std::io::Error::new(std::io::ErrorKind::InvalidData, msg);
                    return Err(err);
                }

                //let unsigned_shape: Vec<usize> = shape.iter().map(|x| x as usize).collect();
                let unsigned_tensor = shape.convert::<usize>();
                let unsigned_shape = &unsigned_tensor.data();

                let data: Tensor<$t> = {
                    if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT8) == 1 {
                        let mut native_data: Tensor<u8> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT8, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT8) == 1 {
                        let mut native_data: Tensor<i8> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT8, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT16) == 1 {
                        let mut native_data: Tensor<u16> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT16, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT16) == 1 {
                        let mut native_data: Tensor<i16> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT16, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT32) == 1 {
                        let mut native_data: Tensor<u32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT32, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT32) == 1 {
                        let mut native_data: Tensor<i32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT32, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_UINT64) == 1 {
                        let mut native_data: Tensor<u64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_UINT64, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_INT64) == 1 {
                        let mut native_data: Tensor<i64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_INT64, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_FLOAT) == 1 {
                        let mut native_data: Tensor<f32> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_FLOAT, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else if ffi::H5Tequal(datatype, ffi::H5T_NATIVE_DOUBLE) == 1 {
                        let mut native_data: Tensor<f64> = Tensor::empty(&unsigned_shape[..]);
                        // Finally load the actual data
                        ffi::H5Dread(dset, ffi::H5T_NATIVE_DOUBLE, ffi::H5S_ALL, ffi::H5S_ALL,
                                     ffi::H5P_DEFAULT, native_data.as_mut_ptr() as *mut c_void);
                        native_data.convert::<$t>()
                    } else {
                        let msg = format!("Unable to convert '{}' to {}: {}",
                                          group, "f64", filename);
                        let err = std::io::Error::new(std::io::ErrorKind::InvalidData, msg);
                        return Err(err);
                    }
                };

                ffi::H5Tclose(datatype);
                ffi::H5Dclose(dset);
                ffi::H5Fclose(file);

                Ok(data)
            }
        }
    )
}

add_load!(load_hdf5_as_u8, u8);
add_load!(load_hdf5_as_u16, u16);
add_load!(load_hdf5_as_u32, u32);
add_load!(load_hdf5_as_u64, u64);
add_load!(load_hdf5_as_i8, i8);
add_load!(load_hdf5_as_i16, i16);
add_load!(load_hdf5_as_i32, i32);
add_load!(load_hdf5_as_i64, i64);
add_load!(load_hdf5_as_f32, f32);
add_load!(load_hdf5_as_f64, f64);
add_load!(load_hdf5_as_isize, isize);
add_load!(load_hdf5_as_usize, usize);
<|endoftext|>


<commit_before>extern crate tar;
extern crate flate2;

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

use self::tar::Archive;
use self::flate2::read::GzDecoder;

fn extract_tar(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let mut archive = Archive::new(file);
    archive.unpack(output).unwrap();
}

fn extract_gz(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let buffer = BufReader::new(file);
    let archive = GzDecoder::new(buffer).unwrap();

    let mut target = File::create(output).unwrap();
    for byte in archive.bytes() {
        target.write(&[byte.unwrap()]);
    }
}

<commit_msg>Use match instead of unwrap<commit_after>extern crate tar;
extern crate flate2;

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

use self::tar::Archive;
use self::flate2::read::GzDecoder;

fn extract_tar(input: &Path, output: &Path) {
    let file = match File::open(input)
    {
        Ok(x) => x,
        Err(why) => panic!("An error occured. \n{}", why),
    };

    let mut archive = Archive::new(file);
    archive.unpack(output).unwrap();
}

fn extract_gz(input: &Path, output: &Path) {
    let file = match File::open(input)
    {
        Ok(x) => x,
        Err(why) => panic!("An error occured. \n{}", why),
    };

    let archive = match GzDecoder::new(BufReader::new(file))
    {
        Ok(x) => x,
        Err(why) => panic!("An error occured. \n{}", why),
    };

    let mut target = File::create(output).unwrap();
    for byte in archive.bytes() {
        target.write(&[byte.unwrap()]);
    }
}

<|endoftext|>


<commit_before>//! Run-Time Limits

use libc::c_int;

use ffi;
pub use ffi::Limit;

use Connection;

impl Connection {
    /// Returns the current value of a limit.
    pub fn get_limit(&self, limit: Limit) -> i32 {
        let c = self.db.borrow();
        unsafe {
            ffi::sqlite3_limit(c.db(), limit as c_int, -1)
        }
    }
    /// Changes the limit to `new_val`.
    /// And returns the prior value of the limit.
    pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
        let c = self.db.borrow_mut();
        unsafe {
            ffi::sqlite3_limit(c.db(), limit as c_int, new_val)
        }
    }
}

#[cfg(test)]
mod test {
    use ffi::Limit;
    use Connection;

    #[test]
    fn test_limit() {
        let db = Connection::open_in_memory().unwrap();
        assert_eq!(2147483645, db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024));
        assert_eq!(1024, db.get_limit(Limit::SQLITE_LIMIT_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
        assert_eq!(1024, db.get_limit(Limit::SQLITE_LIMIT_SQL_LENGTH));

        assert_eq!(2000, db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64));
        assert_eq!(64, db.get_limit(Limit::SQLITE_LIMIT_COLUMN));

        assert_eq!(1000, db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256));
        assert_eq!(256, db.get_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));

        assert_eq!(500, db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32));
        assert_eq!(32, db.get_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));

        assert_eq!(127, db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32));
        assert_eq!(32, db.get_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));

        assert_eq!(10, db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2));
        assert_eq!(2, db.get_limit(Limit::SQLITE_LIMIT_ATTACHED));

        assert_eq!(50000, db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128));
        assert_eq!(128, db.get_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
        assert_eq!(99, db.get_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));

        assert_eq!(1000, db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32));
        assert_eq!(32, db.get_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));

        assert_eq!(0, db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2));
        assert_eq!(2, db.get_limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
    }
}
 <commit_msg>Rename get_limit to limit<commit_after>//! Run-Time Limits

use libc::c_int;

use ffi;
pub use ffi::Limit;

use Connection;

impl Connection {
    /// Returns the current value of a limit.
    pub fn limit(&self, limit: Limit) -> i32 {
        let c = self.db.borrow();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }
    }
    /// Changes the limit to `new_val`.
    /// And returns the prior value of the limit.
    pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }
    }
}

#[cfg(test)]
mod test {
    use ffi::Limit;
    use Connection;

    #[test]
    fn test_limit() {
        let db = Connection::open_in_memory().unwrap();
        db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64);
        assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN));

        db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256);
        assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));

        db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));

        db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));

        db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2);
        assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED));

        db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128);
        assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
        assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));

        db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));

        db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
        assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
    }
}
<|endoftext|>


<commit_before>//! Run-Time Limits

use libc::c_int;

use ffi;
pub use ffi::Limit;

use Connection;

impl Connection {
    /// Returns the current value of a limit.
    pub fn limit(&self, limit: Limit) -> i32 {
        let c = self.db.borrow();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }
    }
    /// Changes the limit to `new_val`.
    /// And returns the prior value of the limit.
    pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }
    }
}

#[cfg(test)]
mod test {
    use ffi::Limit;
    use Connection;

    #[test]
    fn test_limit() {
        let db = Connection::open_in_memory().unwrap();
        db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64);
        assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN));

        db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256);
        assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));

        db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));

        db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));

        db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2);
        assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED));

        db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128);
        assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
        assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));

        db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));

        db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
        assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
    }
}
<commit_msg>Fix tests with old SQLite version (< 3.8.7)<commit_after>//! Run-Time Limits

use libc::c_int;

use ffi;
pub use ffi::Limit;

use Connection;

impl Connection {
    /// Returns the current value of a limit.
    pub fn limit(&self, limit: Limit) -> i32 {
        let c = self.db.borrow();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }
    }
    /// Changes the limit to `new_val`.
    /// And returns the prior value of the limit.
    pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
        let c = self.db.borrow_mut();
        unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }
    }
}

#[cfg(test)]
mod test {
    use ffi::Limit;
    use Connection;

    #[test]
    fn test_limit() {
        let db = Connection::open_in_memory().unwrap();
        db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
        assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64);
        assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN));

        db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256);
        assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));

        db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));

        db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));

        db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2);
        assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED));

        db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128);
        assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));

        db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
        assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));

        db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
        assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));

        if version_number() >= 3008007 {
            db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
            assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
        }
    }
}
<|endoftext|>


<commit_before>pub use nonblock_logger::{current_thread_name, JoinHandle};
use nonblock_logger::{
    log::{Level::Info, LevelFilter, Record},
    BaseFilter, BaseFormater, FixedLevel, NonblockLogger,
};

pub fn format(base: &BaseFormater, record: &Record) -> String {
    let level = FixedLevel::with_color(record.level(), base.color_get())
        .length(base.level_get())
        .into_colored()
        .into_coloredfg();

    format!(
        "{} {} [{} {}:{}] {}\n",
        chrono::Local::now().format("%Y-%m-%d %H:%M:%S.%3f"),
        level,
        current_thread_name(),
        record.file().unwrap_or("*"),
        record.line().unwrap_or(0),
        record.args()
    )
}

pub fn log_enabled_info(target: &str) -> bool {
    log_enabled!(target: target, Info)
}

pub fn logger_init(verbose: u64) -> JoinHandle {
    let pkg = crate::consts::NAME;
    let log = match verbose {
        0 => LevelFilter::Warn,
        1 => LevelFilter::Info,
        2 => LevelFilter::Debug,
        _more => LevelFilter::Trace,
    };

    if verbose > 2 {
        println!("logger_init: pkg: {}, level: {:?}", pkg, log)
    };

    let formater = BaseFormater::new().local(true).color(true).level(4).formater(format);
    let filter = BaseFilter::new()
        .max_level(log)
        .starts_with(true)
        .notfound(true)
        .chain(pkg, log)
        .chain("tokio", LevelFilter::Info)
        .chain("hyper", LevelFilter::Info)
        .chain("mio", LevelFilter::Info);

    NonblockLogger::new()
        .formater(formater)
        .filter(filter)
        .expect("add filiter failed")
        .log_to_stdout()
        .map_err(|e| eprintln!("failed to init nonblock_logger: {:?}", e))
        .unwrap()
}
<commit_msg>Show source file names only for debug and above log levels<commit_after>pub use nonblock_logger::{current_thread_name, JoinHandle};
use nonblock_logger::{
    log::{Level::Info, LevelFilter, Record},
    BaseFilter, BaseFormater, FixedLevel, NonblockLogger,
};

pub fn format(debug: bool) -> impl Fn(&BaseFormater, &Record) -> String + Send + Sync + 'static {
    move |base: &BaseFormater, record: &Record| {
        let level = FixedLevel::with_color(record.level(), base.color_get())
            .length(base.level_get())
            .into_colored()
            .into_coloredfg();

        format!(
            "{} {} [{} {}:{}] {}\n",
            chrono::Local::now().format("%Y-%m-%d %H:%M:%S.%3f"),
            level,
            current_thread_name(),
            if debug { record.file() } else { record.module_path() }.unwrap_or("*"),
            record.line().unwrap_or(0),
            record.args()
        )
    }
}

pub fn log_enabled_info(target: &str) -> bool {
    log_enabled!(target: target, Info)
}

pub fn logger_init(verbose: u64) -> JoinHandle {
    let pkg = crate::consts::NAME;
    let log = match verbose {
        0 => LevelFilter::Warn,
        1 => LevelFilter::Info,
        2 => LevelFilter::Debug,
        _more => LevelFilter::Trace,
    };

    if verbose > 2 {
        println!("logger_init: pkg: {}, level: {:?}", pkg, log)
    };

    let formater = BaseFormater::new()
        .local(true)
        .color(true)
        .level(4)
        .formater(format(verbose > 1));

    let filter = BaseFilter::new()
        .max_level(log)
        .starts_with(true)
        .notfound(true)
        .chain(pkg, log)
        .chain("tokio", LevelFilter::Info)
        .chain("hyper", LevelFilter::Info)
        .chain("mio", LevelFilter::Info);

    NonblockLogger::new()
        .formater(formater)
        .filter(filter)
        .expect("add filiter failed")
        .log_to_stdout()
        .map_err(|e| eprintln!("failed to init nonblock_logger: {:?}", e))
        .unwrap()
}
<|endoftext|>


<commit_before>// TODO: deny missing docs
#![allow(missing_docs)]

use futures::{self, Future, Stream};
use futures::future::join_all;
use futures::stream::futures_unordered;
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use std::str::FromStr;
use tokio_core::reactor::Handle;
use trust_dns::client::{ClientFuture, ClientHandle};
use trust_dns::rr::domain;
use trust_dns::rr::{DNSClass, Record, RecordType};
use trust_dns::udp::UdpClientStream;

#[derive(Debug, Clone)]
pub struct Query {
    domain_name: domain::Name,
    record_types: Vec<RecordType>,
    timeout: Duration,
}

impl Query {
    pub fn from<T: Into<domain::Name>>(domain_name: T, record_types: Vec<RecordType>) -> Query {
        Query {
            domain_name: domain_name.into(),
            record_types,
            timeout: Duration::from_secs(5),
        }
    }

    pub fn new(domain_name: &str, record_types: Vec<RecordType>) -> Query {
        let domain_name = domain::Name::from_str(domain_name).unwrap();
        Query::from(domain_name, record_types)
    }

    pub fn set_timeout(mut self: Self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    pub fn add_recordtype(mut self: Self, record_type: RecordType) -> Self {
        self.record_types.push(record_type);
        self
    }
}

#[derive(Debug)]
pub struct Response {
    pub server: IpAddr,
    pub answers: Vec<Record>,
}

pub fn lookup<T: Into<SocketAddr>>(
    loop_handle: &Handle,
    query: Query,
    server: T,
) -> Box<Future<Item = Response, Error = Error>> {
    let socket_addr = server.into();
    let domain_name = query.domain_name;

    let (stream, sender) = UdpClientStream::new(socket_addr, loop_handle);
    let mut client = ClientFuture::with_timeout(stream, sender, loop_handle, query.timeout, None);

    let lookups: Vec<_> = query
        .record_types
        .into_iter()
        .enumerate()
        .map(|(index, rt)| {
            client
                .query(domain_name.clone(), DNSClass::IN, rt)
                .map(move |mut response| {
                    Response {
                        server: socket_addr.ip(),
                        answers: response.take_answers(),
                    }
                })
                .map_err(move |e| {
                    Error::with_chain(e, ErrorKind::QueryError(index + 1, rt, socket_addr.ip()))
                })
        })
        .collect();
    let all = join_all(lookups).and_then(move |lookups| {
        let all_answers = lookups.into_iter().fold(Vec::new(), |mut acc,
         mut lookup: Response| {
            acc.append(&mut lookup.answers);
            acc
        });
        futures::future::ok(Response {
            server: socket_addr.ip(),
            answers: all_answers,
        })
    });

    Box::new(all)
}

/// Lookup a domain name against a set of DNS servers
///
/// The return type is special here. `Future<Item=Vec<Result<DnsResponse>>, Error=()`. This Future is not supposed to fail
/// in `future::futures::Err` way, but rather propagate errors as part of the Future's successful execution.
/// In this way, this function does not abort when single lookups fail, but wait for all queries / Futures to finish.
/// The library user than can distinguish between successful and failed lookups.
#[allow(needless_pass_by_value)]
pub fn multiple_lookup<T: Into<SocketAddr>>(
    loop_handle: &Handle,
    query: Query,
    servers: Vec<T>,
) -> Box<Future<Item = Vec<Result<Response>>, Error = ()>> {
    let futures: Vec<_> = servers
        .into_iter()
        .map(|server| {
            lookup(loop_handle, query.clone(), server).map(Ok).or_else(
                |e| {
                    Ok(Err(e))
                },
            )
        })
        .collect();

    Box::new(futures_unordered(futures).collect())
}

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {
        QueryError(index: usize, rt: RecordType, ip: IpAddr) {
            description("Query failed")
            display("{}. query for {:?} record against DNS server {} failed", index, rt, ip)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::net::{Ipv4Addr, Ipv6Addr};
    use std::str::FromStr;
    use tokio_core::reactor::Core;
    use trust_dns::rr::{RData, RecordType};

    #[test]
    fn lookup_with_google() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        let query = Query::new(domain_name, vec![RecordType::A]);
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not a PTR record");
        }
    }

    #[test]
    fn ptr_lookup_with_google() {
        let mut io_loop = Core::new().unwrap();
        let ip_addr = IpAddr::from_str("8.8.8.8").unwrap();
        let query = Query::from(ip_addr, vec![RecordType::PTR]);
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::PTR(ref ptr) = *response.answers[0].rdata() {
            assert_eq!(
                ptr,
                &domain::Name::from_str("google-public-dns-a.google.com.").unwrap()
            );
        } else {
            panic!("Not a PTR record");
        }
    }

    #[test]
    fn multiple_lookup_with_google_ok() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        let query = Query::new(domain_name, vec![RecordType::A]);
        let servers = vec![
            (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53),
            (Ipv4Addr::from_str("8.8.4.4").unwrap(), 53),
        ];

        let lookup = multiple_lookup(&io_loop.handle(), query, servers);
        let mut responses: Vec<_> = io_loop.run(lookup).unwrap();
        assert_eq!(responses.len(), 2);

        let response = responses.pop().unwrap().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.4.4").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }

        let response = responses.pop().unwrap().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
    }

    #[test]
    fn multiple_lookup_with_google_fail_1() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        // short timeout, because we won't the test to take too long, Google is fast enough to answer in time
        let query =
            Query::new(domain_name, vec![RecordType::A]).set_timeout(Duration::from_millis(500));
        let servers = vec![
            (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53),
            // This one does not exists and should lead to a timeout
            (Ipv4Addr::from_str("8.8.5.5").unwrap(), 53),
        ];

        let lookup = multiple_lookup(&io_loop.handle(), query, servers);
        let mut responses: Vec<_> = io_loop.run(lookup).unwrap();
        assert_eq!(responses.len(), 2);

        let response = responses.pop().unwrap();
        assert!(response.is_err());

        let response = responses.pop().unwrap().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
    }

    #[test]
    fn multi_record_type_lookup() {
        let mut io_loop = Core::new().unwrap();
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let record_types = vec![RecordType::A, RecordType::AAAA, RecordType::MX];
        let domain_name = "example.com";
        let query = Query::new(domain_name, record_types);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 2);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
        if let RData::AAAA(ip) = *response.answers[1].rdata() {
            assert_eq!(
                ip,
                Ipv6Addr::new(0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946)
            );
        } else {
            panic!("Not an AAAA record");
        }
    }
}
<commit_msg>Make multiple_lookup_with_google_ok test deterministic<commit_after>// TODO: deny missing docs
#![allow(missing_docs)]

use futures::{self, Future, Stream};
use futures::future::join_all;
use futures::stream::futures_unordered;
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
use std::str::FromStr;
use tokio_core::reactor::Handle;
use trust_dns::client::{ClientFuture, ClientHandle};
use trust_dns::rr::domain;
use trust_dns::rr::{DNSClass, Record, RecordType};
use trust_dns::udp::UdpClientStream;

#[derive(Debug, Clone)]
pub struct Query {
    domain_name: domain::Name,
    record_types: Vec<RecordType>,
    timeout: Duration,
}

impl Query {
    pub fn from<T: Into<domain::Name>>(domain_name: T, record_types: Vec<RecordType>) -> Query {
        Query {
            domain_name: domain_name.into(),
            record_types,
            timeout: Duration::from_secs(5),
        }
    }

    pub fn new(domain_name: &str, record_types: Vec<RecordType>) -> Query {
        let domain_name = domain::Name::from_str(domain_name).unwrap();
        Query::from(domain_name, record_types)
    }

    pub fn set_timeout(mut self: Self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    pub fn add_recordtype(mut self: Self, record_type: RecordType) -> Self {
        self.record_types.push(record_type);
        self
    }
}

#[derive(Debug)]
pub struct Response {
    pub server: IpAddr,
    pub answers: Vec<Record>,
}

pub fn lookup<T: Into<SocketAddr>>(
    loop_handle: &Handle,
    query: Query,
    server: T,
) -> Box<Future<Item = Response, Error = Error>> {
    let socket_addr = server.into();
    let domain_name = query.domain_name;

    let (stream, sender) = UdpClientStream::new(socket_addr, loop_handle);
    let mut client = ClientFuture::with_timeout(stream, sender, loop_handle, query.timeout, None);

    let lookups: Vec<_> = query
        .record_types
        .into_iter()
        .enumerate()
        .map(|(index, rt)| {
            client
                .query(domain_name.clone(), DNSClass::IN, rt)
                .map(move |mut response| {
                    Response {
                        server: socket_addr.ip(),
                        answers: response.take_answers(),
                    }
                })
                .map_err(move |e| {
                    Error::with_chain(e, ErrorKind::QueryError(index + 1, rt, socket_addr.ip()))
                })
        })
        .collect();
    let all = join_all(lookups).and_then(move |lookups| {
        let all_answers = lookups.into_iter().fold(Vec::new(), |mut acc,
         mut lookup: Response| {
            acc.append(&mut lookup.answers);
            acc
        });
        futures::future::ok(Response {
            server: socket_addr.ip(),
            answers: all_answers,
        })
    });

    Box::new(all)
}

/// Lookup a domain name against a set of DNS servers
///
/// The return type is special here. `Future<Item=Vec<Result<DnsResponse>>, Error=()`. This Future is not supposed to fail
/// in `future::futures::Err` way, but rather propagate errors as part of the Future's successful execution.
/// In this way, this function does not abort when single lookups fail, but wait for all queries / Futures to finish.
/// The library user than can distinguish between successful and failed lookups.
#[allow(needless_pass_by_value)]
pub fn multiple_lookup<T: Into<SocketAddr>>(
    loop_handle: &Handle,
    query: Query,
    servers: Vec<T>,
) -> Box<Future<Item = Vec<Result<Response>>, Error = ()>> {
    let futures: Vec<_> = servers
        .into_iter()
        .map(|server| {
            lookup(loop_handle, query.clone(), server).map(Ok).or_else(
                |e| {
                    Ok(Err(e))
                },
            )
        })
        .collect();

    Box::new(futures_unordered(futures).collect())
}

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {
        QueryError(index: usize, rt: RecordType, ip: IpAddr) {
            description("Query failed")
            display("{}. query for {:?} record against DNS server {} failed", index, rt, ip)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::net::{Ipv4Addr, Ipv6Addr};
    use std::str::FromStr;
    use tokio_core::reactor::Core;
    use trust_dns::rr::{RData, RecordType};

    #[test]
    fn lookup_with_google() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        let query = Query::new(domain_name, vec![RecordType::A]);
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not a PTR record");
        }
    }

    #[test]
    fn ptr_lookup_with_google() {
        let mut io_loop = Core::new().unwrap();
        let ip_addr = IpAddr::from_str("8.8.8.8").unwrap();
        let query = Query::from(ip_addr, vec![RecordType::PTR]);
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::PTR(ref ptr) = *response.answers[0].rdata() {
            assert_eq!(
                ptr,
                &domain::Name::from_str("google-public-dns-a.google.com.").unwrap()
            );
        } else {
            panic!("Not a PTR record");
        }
    }

    #[test]
    fn multiple_lookup_with_google_ok() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        let query = Query::new(domain_name, vec![RecordType::A]);
        let servers = vec![
            (Ipv4Addr::from_str("8.8.4.4").unwrap(), 53),
            (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53),
        ];

        let lookup = multiple_lookup(&io_loop.handle(), query, servers);
        let results: ::std::result::Result<Vec<_>, _> = io_loop.run(lookup)
            .unwrap()
            .into_iter()
            .collect();
        let mut responses = results.unwrap();

        assert_eq!(responses.len(), 2);
        responses.sort_by(|a, b| a.server.cmp(&b.server));

        let response = responses.pop().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }

        let response = responses.pop().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.4.4").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
    }

    #[test]
    fn multiple_lookup_with_google_fail_1() {
        let mut io_loop = Core::new().unwrap();
        let domain_name = "example.com";
        // short timeout, because we won't the test to take too long, Google is fast enough to answer in time
        let query =
            Query::new(domain_name, vec![RecordType::A]).set_timeout(Duration::from_millis(500));
        let servers = vec![
            (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53),
            // This one does not exists and should lead to a timeout
            (Ipv4Addr::from_str("8.8.5.5").unwrap(), 53),
        ];

        let lookup = multiple_lookup(&io_loop.handle(), query, servers);
        let mut responses: Vec<_> = io_loop.run(lookup).unwrap();
        assert_eq!(responses.len(), 2);

        let response = responses.pop().unwrap();
        assert!(response.is_err());

        let response = responses.pop().unwrap().unwrap();
        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 1);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
    }

    #[test]
    fn multi_record_type_lookup() {
        let mut io_loop = Core::new().unwrap();
        let server = (Ipv4Addr::from_str("8.8.8.8").unwrap(), 53);

        let record_types = vec![RecordType::A, RecordType::AAAA, RecordType::MX];
        let domain_name = "example.com";
        let query = Query::new(domain_name, record_types);

        let lookup = lookup(&io_loop.handle(), query, server);
        let result = io_loop.run(lookup).unwrap();
        let response: Response = result;

        assert_eq!(response.server, Ipv4Addr::from_str("8.8.8.8").unwrap());
        assert_eq!(response.answers.len(), 2);
        if let RData::A(ip) = *response.answers[0].rdata() {
            assert_eq!(ip, Ipv4Addr::new(93, 184, 216, 34));
        } else {
            panic!("Not an A record");
        }
        if let RData::AAAA(ip) = *response.answers[1].rdata() {
            assert_eq!(
                ip,
                Ipv6Addr::new(0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946)
            );
        } else {
            panic!("Not an AAAA record");
        }
    }
}
<|endoftext|>


<commit_before>macro_rules! intrinsics {
    () => ();

    // Anything which has a `not(feature = "c")` we'll generate a shim function
    // which calls out to the C function if the `c` feature is enabled.
    // Otherwise if the `c` feature isn't enabled then we'll just have a normal
    // intrinsic.
    (
        #[use_c_shim_if($($cfg_clause:tt)*)]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (

        #[cfg(all(feature = "c", $($cfg_clause)*))]
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            extern $abi {
                fn $name($($argname: $ty),*) -> $ret;
            }
            unsafe {
                $name($($argname),*)
            }
        }

        #[cfg(not(all(feature = "c", $($cfg_clause)*)))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // We recognize the `#[aapcs_on_arm]` attribute here and generate the
    // same intrinsic but force it to have the `"aapcs"` calling convention on
    // ARM and `"C"` elsewhere.
    (
        #[aapcs_on_arm]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(target_arch = "arm")]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        #[cfg(not(target_arch = "arm"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // Like aapcs above we recognize an attribute for the "unadjusted" abi on
    // win64 for some methods.
    (
        #[unadjusted_on_win64]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(all(windows, target_pointer_width = "64"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern "unadjusted" fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        #[cfg(not(all(windows, target_pointer_width = "64")))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // Another attribute we recognize is an "abi hack" for win64 to get the 128
    // bit calling convention correct.
    (
        #[win64_128bit_abi_hack]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(all(windows, target_pointer_width = "64"))]
        $(#[$($attr)*])*
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        #[cfg(all(windows, target_pointer_width = "64"))]
        pub mod $name {

            intrinsics! {
                pub extern $abi fn $name( $($argname: $ty),* )
                    -> ::macros::win64_abi_hack::U64x2
                {
                    let e: $ret = super::$name($($argname),*);
                    ::macros::win64_abi_hack::U64x2::from(e)
                }
            }
        }

        #[cfg(not(all(windows, target_pointer_width = "64")))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // A bunch of intrinsics on ARM are aliased in the standard compiler-rt
    // build under `__aeabi_*` aliases, and LLVM will call these instead of the
    // original function. Handle that here
    (
        #[arm_aeabi_alias = $alias:ident]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(target_arch = "arm")]
        $(#[$($attr)*])*
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        #[cfg(target_arch = "arm")]
        pub mod $name {
            intrinsics! {
                pub extern "aapcs" fn $alias( $($argname: $ty),* ) -> $ret {
                    super::$name($($argname),*)
                }

                pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                    super::$name($($argname),*)
                }
            }
        }

        #[cfg(not(target_arch = "arm"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    (
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        $(#[$($attr)*])*
        #[cfg_attr(not(test), no_mangle)]
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        intrinsics!($($rest)*);
    );
}

// Hack for LLVM expectations for ABI on windows
#[cfg(all(windows, target_pointer_width="64"))]
pub mod win64_abi_hack {
    #[repr(simd)]
    pub struct U64x2(u64, u64);

    impl From<i128> for U64x2 {
        fn from(i: i128) -> U64x2 {
            use int::LargeInt;
            let j = i as u128;
            U64x2(j.low(), j.high())
        }
    }

    impl From<u128> for U64x2 {
        fn from(i: u128) -> U64x2 {
            use int::LargeInt;
            U64x2(i.low(), i.high())
        }
    }
}
<commit_msg>Shore up docs in the macros module<commit_after>//! Macros shared throughout the compiler-builtins implementation

/// The "main macro" used for defining intrinsics.
///
/// The compiler-builtins library is super platform-specific with tons of crazy
/// little tweaks for various platforms. As a result it *could* involve a lot fo
/// #[cfg] and macro soup, but the intention is that this macro alleviates a lof
/// of that complexity. Ideally this macro has all the weird ABI things
/// platforms need and elsewhere in this library it just looks like normal Rust
/// code.
///
/// This macro is structured to be invoked with a bunch of functions that looks
/// like:
///
///     intrinsics! {
///         pub extern "C" fn foo(a: i32) -> u32 {
///             // ...
///         }
///
///         #[nonstandard_attribute]
///         pub extern "C" fn bar(a: i32) -> u32 {
///             // ...
///         }
///     }
///
/// Each function is defined in a manner that looks like a normal Rust function.
/// The macro then accepts a few nonstandard attributes that can decorate
/// various functions. Each of the attributes is documented below with what it
/// can do, and each of them slightly tweaks how further expansion happens.
///
/// A quick overview of attributes supported right now are:
///
/// * `use_c_shim_if` - takes a #[cfg] directive and falls back to the
///   C-compiled version if `feature = "c"` is specified.
/// * `aapcs_on_arm` - forces the ABI of the function to be `"aapcs"` on ARM and
///   the specified ABI everywhere else.
/// * `unadjusted_on_win64` - like `aapcs_on_arm` this switches to the
///   `"unadjusted"` abi on Win64 and the specified abi elsewhere.
/// * `win64_128bit_abi_hack` - this attribute is used for 128-bit integer
///   intrinsics where the ABI is slightly tweaked on Windows platforms, but
///   it's a normal ABI elsewhere for returning a 128 bit integer.
/// * `arm_aeabi_alias` - handles the "aliasing" of various intrinsics on ARM
///   their otherwise typical names to other prefixed ones.
///
macro_rules! intrinsics {
    () => ();

    // Right now there's a bunch of architecture-optimized intrinsics in the
    // stock compiler-rt implementation. Not all of these have been ported over
    // to Rust yet so when the `c` feature of this crate is enabled we fall back
    // to the architecture-specific versions which should be more optimized. The
    // purpose of this macro is to easily allow specifying this.
    //
    // The argument to `use_c_shim_if` is a `#[cfg]` directive which, when true,
    // will cause this crate's exported version of `$name` to just redirect to
    // the C implementation. No symbol named `$name` will be in the object file
    // for this crate itself.
    //
    // When the `#[cfg]` directive is false, or when the `c` feature is
    // disabled, the provided implementation is used instead.
    (
        #[use_c_shim_if($($cfg_clause:tt)*)]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (

        #[cfg(all(feature = "c", $($cfg_clause)*))]
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            extern $abi {
                fn $name($($argname: $ty),*) -> $ret;
            }
            unsafe {
                $name($($argname),*)
            }
        }

        #[cfg(not(all(feature = "c", $($cfg_clause)*)))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // We recognize the `#[aapcs_on_arm]` attribute here and generate the
    // same intrinsic but force it to have the `"aapcs"` calling convention on
    // ARM and `"C"` elsewhere.
    (
        #[aapcs_on_arm]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(target_arch = "arm")]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        #[cfg(not(target_arch = "arm"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // Like aapcs above we recognize an attribute for the "unadjusted" abi on
    // win64 for some methods.
    (
        #[unadjusted_on_win64]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(all(windows, target_pointer_width = "64"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern "unadjusted" fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        #[cfg(not(all(windows, target_pointer_width = "64")))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // Some intrinsics on win64 which return a 128-bit integer have an.. unusual
    // calling convention. That's managed here with this "abi hack" which alters
    // the generated symbol's ABI.
    //
    // This will still define a function in this crate with the given name and
    // signature, but the actual symbol for the intrinsic may have a slightly
    // different ABI on win64.
    (
        #[win64_128bit_abi_hack]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(all(windows, target_pointer_width = "64"))]
        $(#[$($attr)*])*
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        #[cfg(all(windows, target_pointer_width = "64"))]
        pub mod $name {

            intrinsics! {
                pub extern $abi fn $name( $($argname: $ty),* )
                    -> ::macros::win64_128bit_abi_hack::U64x2
                {
                    let e: $ret = super::$name($($argname),*);
                    ::macros::win64_128bit_abi_hack::U64x2::from(e)
                }
            }
        }

        #[cfg(not(all(windows, target_pointer_width = "64")))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // A bunch of intrinsics on ARM are aliased in the standard compiler-rt
    // build under `__aeabi_*` aliases, and LLVM will call these instead of the
    // original function. The aliasing here is used to generate these symbols in
    // the object file.
    (
        #[arm_aeabi_alias = $alias:ident]
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        #[cfg(target_arch = "arm")]
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        #[cfg(target_arch = "arm")]
        pub mod $name {
            intrinsics! {
                pub extern "aapcs" fn $alias( $($argname: $ty),* ) -> $ret {
                    super::$name($($argname),*)
                }

                pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                    super::$name($($argname),*)
                }
            }
        }

        #[cfg(not(target_arch = "arm"))]
        intrinsics! {
            $(#[$($attr)*])*
            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
                $($body)*
            }
        }

        intrinsics!($($rest)*);
    );

    // This is the final catch-all rule. At this point we just generate an
    // intrinsic with a conditional `#[no_mangle]` directive to avoid
    // interfereing with duplicate symbols and whatnot.
    //
    // After the intrinsic is defined we just continue with the rest of the
    // input we were given.
    (
        $(#[$($attr:tt)*])*
        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        $(#[$($attr)*])*
        #[cfg_attr(not(test), no_mangle)]
        pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
            $($body)*
        }

        intrinsics!($($rest)*);
    );
}

// Hack for LLVM expectations for ABI on windows. This is used by the
// `#[win64_128bit_abi_hack]` attribute recognized above
#[cfg(all(windows, target_pointer_width="64"))]
pub mod win64_128bit_abi_hack {
    #[repr(simd)]
    pub struct U64x2(u64, u64);

    impl From<i128> for U64x2 {
        fn from(i: i128) -> U64x2 {
            use int::LargeInt;
            let j = i as u128;
            U64x2(j.low(), j.high())
        }
    }

    impl From<u128> for U64x2 {
        fn from(i: u128) -> U64x2 {
            use int::LargeInt;
            U64x2(i.low(), i.high())
        }
    }
}
<|endoftext|>


<commit_before>/// A macro for defining #[cfg] if-else statements.
///
/// This is similar to the `if/elif` C preprocessor macro by allowing definition
/// of a cascade of `#[cfg]` cases, emitting the implementation which matches
/// first.
///
/// This allows you to conveniently provide a long list #[cfg]'d blocks of code
/// without having to rewrite each clause multiple times.
macro_rules! cfg_if {
    ($(
        if #[cfg($($meta:meta),*)] { $($it:item)* }
    ) else * else {
        $($it2:item)*
    }) => {
        __cfg_if_items! {
            () ;
            $( ( ($($meta),*) ($($it)*) ), )*
            ( () ($($it2)*) ),
        }
    }
}

macro_rules! __cfg_if_items {
    (($($not:meta,)*) ; ) => {};
    (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
        __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* }
        __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
    }
}

macro_rules! __cfg_if_apply {
    ($m:meta, $($it:item)*) => {
        $(#[$m] $it)*
    }
}

macro_rules! s {
    ($($(#[$attr:meta])* pub $t:ident $i:ident { $($field:tt)* })*) => ($(
        __item! {
            #[repr(C)]
            $(#[$attr])*
            pub $t $i { $($field)* }
        }
        impl ::dox::Copy for $i {}
        impl ::dox::Clone for $i {
            fn clone(&self) -> $i { *self }
        }
    )*)
}

macro_rules! f {
    ($(pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty {
        $($body:stmt);*
    })*) => ($(
        #[inline]
        #[cfg(not(cross_platform_docs))]
        pub unsafe extern fn $i($($arg: $argty),*) -> $ret {
            $($body);*
        }

        #[cfg(cross_platform_docs)]
        #[allow(dead_code)]
        pub unsafe extern fn $i($($arg: $argty),*) -> $ret {
            loop {}
        }
    )*)
}

macro_rules! __item {
    ($i:item) => ($i)
}

#[allow(unused_macros)]
macro_rules! align_const {
    ($($(#[$attr:meta])* pub const $name:ident : $t1:ty = $t2:ident { $($field:tt)* };)*) => ($(
        #[cfg(feature = "align")]
        $(#[$attr])*
        pub const $name : $t1 = $t2 {
            $($field)*
        };
        #[cfg(not(feature = "align"))]
        $(#[$attr])*
        pub const $name : $t1 = $t2 {
            $($field)*
            __align: [],
        };
    )*)
}
<commit_msg>the f macro is unused in some targets<commit_after>/// A macro for defining #[cfg] if-else statements.
///
/// This is similar to the `if/elif` C preprocessor macro by allowing definition
/// of a cascade of `#[cfg]` cases, emitting the implementation which matches
/// first.
///
/// This allows you to conveniently provide a long list #[cfg]'d blocks of code
/// without having to rewrite each clause multiple times.
macro_rules! cfg_if {
    ($(
        if #[cfg($($meta:meta),*)] { $($it:item)* }
    ) else * else {
        $($it2:item)*
    }) => {
        __cfg_if_items! {
            () ;
            $( ( ($($meta),*) ($($it)*) ), )*
            ( () ($($it2)*) ),
        }
    }
}

macro_rules! __cfg_if_items {
    (($($not:meta,)*) ; ) => {};
    (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
        __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* }
        __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
    }
}

macro_rules! __cfg_if_apply {
    ($m:meta, $($it:item)*) => {
        $(#[$m] $it)*
    }
}

macro_rules! s {
    ($($(#[$attr:meta])* pub $t:ident $i:ident { $($field:tt)* })*) => ($(
        __item! {
            #[repr(C)]
            $(#[$attr])*
            pub $t $i { $($field)* }
        }
        impl ::dox::Copy for $i {}
        impl ::dox::Clone for $i {
            fn clone(&self) -> $i { *self }
        }
    )*)
}

#[allow(unused_macros)]
macro_rules! f {
    ($(pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty {
        $($body:stmt);*
    })*) => ($(
        #[inline]
        #[cfg(not(cross_platform_docs))]
        pub unsafe extern fn $i($($arg: $argty),*) -> $ret {
            $($body);*
        }

        #[cfg(cross_platform_docs)]
        #[allow(dead_code)]
        pub unsafe extern fn $i($($arg: $argty),*) -> $ret {
            loop {}
        }
    )*)
}

macro_rules! __item {
    ($i:item) => ($i)
}

#[allow(unused_macros)]
macro_rules! align_const {
    ($($(#[$attr:meta])* pub const $name:ident : $t1:ty = $t2:ident { $($field:tt)* };)*) => ($(
        #[cfg(feature = "align")]
        $(#[$attr])*
        pub const $name : $t1 = $t2 {
            $($field)*
        };
        #[cfg(not(feature = "align"))]
        $(#[$attr])*
        pub const $name : $t1 = $t2 {
            $($field)*
            __align: [],
        };
    )*)
}
<|endoftext|>


<commit_before>extern crate solicit_fork as solicit;
extern crate futures;
extern crate tokio_core;
extern crate grpc;

use std::sync::mpsc;
use std::net::ToSocketAddrs;
use std::thread;

use futures::stream;
use futures::stream::Stream;
use tokio_core::reactor;
use tokio_core::net::*;

use solicit::http::StaticHeader;

use grpc::for_test::*;

#[test]
fn test() {
    let (port_tx, port_rx) = mpsc::channel();

    thread::spawn(move || {
        let mut lp = reactor::Core::new().unwrap();

        let listener = TcpListener::bind(&("::1", 0).to_socket_addrs().unwrap().next().unwrap(), &lp.handle()).unwrap();

        port_tx.send(listener.local_addr().unwrap().port()).unwrap();

        let server_conn = lp.run(listener.incoming().into_future()).ok().expect("accept").0.unwrap().0;

        struct F {
        }

        impl HttpServerHandlerFactory for F {
            fn new_request(&mut self, _req: HttpStreamStreamSend) -> HttpStreamStreamSend {
                Box::new(stream::iter(vec![].into_iter()))
            }
        }

        let http_server_future = HttpServerConnectionAsync::new(lp.handle(), server_conn, F {});

        lp.run(http_server_future).expect("server run");
    });

    let port = port_rx.recv().expect("recv port");

    let (client_complete_tx, client_complete_rx) = mpsc::channel();

    thread::spawn(move || {
        let mut client_lp = reactor::Core::new().expect("core");

        let (client, future) = HttpClientConnectionAsync::new(client_lp.handle(), &("::1", port).to_socket_addrs().unwrap().next().unwrap());

        struct R {
            client_complete_tx: mpsc::Sender<()>,
        }

        impl HttpClientResponseHandler for R {
            fn headers(&mut self, headers: Vec<StaticHeader>) -> bool {
                println!("test: client: response headers: {}", headers.len());
                true
            }

            fn data_frame(&mut self, chunk: Vec<u8>) -> bool {
                println!("test: client: response data frame: {}", chunk.len());
                true
            }

            fn trailers(&mut self, headers: Vec<StaticHeader>) -> bool {
                println!("test: client: response trailers: {}", headers.len());
                true
            }

            fn end(&mut self) {
                println!("test: client: end");
                self.client_complete_tx.send(()).unwrap();
            }
        }

        let _resp = client.start_request(Vec::new(), stream_once_send((&b"abcd"[..]).to_owned()), R { client_complete_tx: client_complete_tx });

        client_lp.run(future).expect("client run");
    });

    client_complete_rx.recv().expect("client complete recv");
}
<commit_msg>better test for http server<commit_after>extern crate solicit_fork as solicit;
extern crate futures;
extern crate tokio_core;
extern crate grpc;

use std::sync::mpsc;
use std::net::ToSocketAddrs;
use std::thread;

use futures::Future;
use futures::stream;
use futures::stream::Stream;
use tokio_core::reactor;
use tokio_core::net::*;

use solicit::http::StaticHeader;
use solicit::http::Header;
use solicit::http::HttpError;

use grpc::for_test::*;

#[test]
fn test() {
    let (port_tx, port_rx) = mpsc::channel();

    thread::spawn(move || {
        let mut lp = reactor::Core::new().unwrap();

        let listener = TcpListener::bind(&("::1", 0).to_socket_addrs().unwrap().next().unwrap(), &lp.handle()).unwrap();

        port_tx.send(listener.local_addr().unwrap().port()).unwrap();

        let server_conn = lp.run(listener.incoming().into_future()).ok().expect("accept").0.unwrap().0;

        struct F {
        }

        impl HttpServerHandlerFactory for F {
            fn new_request(&mut self, req: HttpStreamStreamSend) -> HttpStreamStreamSend {
                future_flatten_to_stream(req
                    .fold(Vec::new(), |mut v, message| {
                        match message {
                            HttpStreamPart::Headers(..) => (),
                            HttpStreamPart::Data(d) => v.extend(d),
                        }

                        futures::finished::<_, HttpError>(v)
                    })
                    .and_then(|v| {
                        let mut r = Vec::new();
                        r.push(HttpStreamPart::Headers(
                            vec![
                                Header::new(":status", "200"),
                            ]
                        ));
                        r.push(HttpStreamPart::Data(v));
                        Ok(stream::iter(r.into_iter().map(Ok)))
                    }))
            }
        }

        let http_server_future = HttpServerConnectionAsync::new(lp.handle(), server_conn, F {});

        lp.run(http_server_future).expect("server run");
    });

    let port = port_rx.recv().expect("recv port");

    let (client_complete_tx, client_complete_rx) = mpsc::channel();

    thread::spawn(move || {
        let mut client_lp = reactor::Core::new().expect("core");

        let (client, future) = HttpClientConnectionAsync::new(client_lp.handle(), &("::1", port).to_socket_addrs().unwrap().next().unwrap());

        struct R {
            response: Vec<u8>,
            client_complete_tx: mpsc::Sender<Vec<u8>>,
        }

        impl HttpClientResponseHandler for R {
            fn headers(&mut self, headers: Vec<StaticHeader>) -> bool {
                println!("test: client: response headers: {}", headers.len());
                true
            }

            fn data_frame(&mut self, chunk: Vec<u8>) -> bool {
                println!("test: client: response data frame: {}", chunk.len());
                self.response.extend(&chunk);
                true
            }

            fn trailers(&mut self, headers: Vec<StaticHeader>) -> bool {
                println!("test: client: response trailers: {}", headers.len());
                true
            }

            fn end(&mut self) {
                println!("test: client: end");
                self.client_complete_tx.send(self.response.clone()).unwrap();
            }
        }

        let _resp = client.start_request(
            Vec::new(),
            stream_once_send((&b"abcd"[..]).to_owned()),
            R { client_complete_tx: client_complete_tx, response: Vec::new() });

        client_lp.run(future).expect("client run");
    });

    assert_eq!(&b"abcd"[..], &client_complete_rx.recv().expect("client complete recv")[..]);
}
<|endoftext|>


<commit_before>//! Test rendering of primitive effects, through the Dispatch interface.

extern crate libfriendship;
extern crate url;

use std::sync::mpsc::{channel, Sender};

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscToplevel, OscRouteGraph, OscRenderer, OscRendererById};
use libfriendship::routing::{adjlist, DagHandle, Edge, EdgeWeight, EffectMeta, NodeHandle};
use url::Url;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Vec<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, renderer_id: u32, buffer: &[f32], idx: u64, num_ch: u8) {
        self.tx.send(buffer.iter().map(|x| *x).collect());
    }
}

#[test]
fn render_zeros() {
    let (tx, rx) = channel();
    let client = Box::new(MyClient{ tx });
    let mut dispatch = Dispatch::new();
    dispatch.register_client(client);
    // Create the renderer
    let rend_id = 1;
    dispatch.dispatch(OscRenderer::New((), (rend_id,)).into()).unwrap();

    // Read some data from ch=0.
    // This should be all zeros because we have no data being rendered.
    dispatch.dispatch(OscRenderer::ById(rend_id,
        OscRendererById::RenderRange((), (0, 4, 1))
    ).into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![0f32, 0f32, 0f32, 0f32]);

    // Route a constant into ch=0.
    let handle = NodeHandle::new_node(DagHandle::toplevel(), 1);
    dispatch.dispatch(OscRouteGraph::AddNode((), (handle, adjlist::NodeData::Effect(
        EffectMeta::new("Constant".to_string(), None, [Url::parse("primitive:///Constant?value=0.5").unwrap()].iter().cloned())
    ))).into()).unwrap();
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new_to_null(handle, EdgeWeight::new(0, 0, 0, 0)),)).into()).unwrap();

    // Read some data from ch=0.
    // This should be all 0.5 because of the new node we added.
    dispatch.dispatch(OscRenderer::ById(rend_id,
        OscRendererById::RenderRange((), (0, 4, 1))
    ).into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![0.5f32, 0.5f32, 0.5f32, 0.5f32]);
}

<commit_msg>Unwrap result<commit_after>//! Test rendering of primitive effects, through the Dispatch interface.

extern crate libfriendship;
extern crate url;

use std::sync::mpsc::{channel, Sender};

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscToplevel, OscRouteGraph, OscRenderer, OscRendererById};
use libfriendship::routing::{adjlist, DagHandle, Edge, EdgeWeight, EffectMeta, NodeHandle};
use url::Url;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Vec<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, renderer_id: u32, buffer: &[f32], idx: u64, num_ch: u8) {
        self.tx.send(buffer.iter().map(|x| *x).collect()).unwrap();
    }
}

#[test]
fn render_zeros() {
    let (tx, rx) = channel();
    let client = Box::new(MyClient{ tx });
    let mut dispatch = Dispatch::new();
    dispatch.register_client(client);
    // Create the renderer
    let rend_id = 1;
    dispatch.dispatch(OscRenderer::New((), (rend_id,)).into()).unwrap();

    // Read some data from ch=0.
    // This should be all zeros because we have no data being rendered.
    dispatch.dispatch(OscRenderer::ById(rend_id,
        OscRendererById::RenderRange((), (0, 4, 1))
    ).into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![0f32, 0f32, 0f32, 0f32]);

    // Route a constant into ch=0.
    let handle = NodeHandle::new_node(DagHandle::toplevel(), 1);
    dispatch.dispatch(OscRouteGraph::AddNode((), (handle, adjlist::NodeData::Effect(
        EffectMeta::new("Constant".to_string(), None, [Url::parse("primitive:///Constant?value=0.5").unwrap()].iter().cloned())
    ))).into()).unwrap();
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new_to_null(handle, EdgeWeight::new(0, 0, 0, 0)),)).into()).unwrap();

    // Read some data from ch=0.
    // This should be all 0.5 because of the new node we added.
    dispatch.dispatch(OscRenderer::ById(rend_id,
        OscRendererById::RenderRange((), (0, 4, 1))
    ).into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![0.5f32, 0.5f32, 0.5f32, 0.5f32]);
}

<|endoftext|>