<commit_before>use std::ptr;
use std::ffi;
use super::{stringutils, sys, libc};

use super::fitserror::{FitsError, Result, status_to_error};
use super::fitshdu::{FitsHdu, DescribesHdu};
use super::columndescription::ColumnDescription;
use super::types::{FileOpenMode, HduType, ImageType};

/// Description of the current HDU
///
/// If the current HDU is an image, then
/// [`fetch_hdu_info`](struct.FitsFile.html#method.fetch_hdu_info) returns `HduInfo::ImageInfo`.
/// Otherwise the variant is `HduInfo::TableInfo`.
#[derive(Debug)]
pub enum HduInfo {
    ImageInfo {
        dimensions: usize,
        shape: Vec<usize>,
    },
    TableInfo {
        column_descriptions: Vec<ColumnDescription>,
        num_rows: usize,
    },
}

pub struct ImageDescription {
    pub data_type: ImageType,
    pub dimensions: Vec<usize>,
}

/// Main entry point to the FITS file format
///
///
pub struct FitsFile {
    pub fptr: *mut sys::fitsfile,
    pub filename: String,
}

impl Clone for FitsFile {
    fn clone(&self) -> Self {
        FitsFile::open(&self.filename).unwrap()
    }
}

unsafe fn fetch_hdu_info(fptr: *mut sys::fitsfile) -> Result<HduInfo> {
    let mut status = 0;
    let mut hdu_type = 0;

    sys::ffghdt(fptr, &mut hdu_type, &mut status);
    let hdu_type = match hdu_type {
        0 => {
            let mut dimensions = 0;
            sys::ffgidm(fptr, &mut dimensions, &mut status);

            let mut shape = vec![0; dimensions as usize];
            sys::ffgisz(fptr, dimensions, shape.as_mut_ptr(), &mut status);

            HduInfo::ImageInfo {
                dimensions: dimensions as usize,
                shape: shape.iter().map(|v| *v as usize).collect(),
            }
        }
        1 | 2 => {
            let mut num_rows = 0;
            sys::ffgnrw(fptr, &mut num_rows, &mut status);

            let mut num_cols = 0;
            sys::ffgncl(fptr, &mut num_cols, &mut status);
            let mut column_descriptions = Vec::with_capacity(num_cols as usize);

            for i in 0..num_cols {
                let mut name_buffer: Vec<libc::c_char> = vec![0; 71];
                let mut type_buffer: Vec<libc::c_char> = vec![0; 71];
                sys::ffgbcl(fptr,
                            (i + 1) as i32,
                            name_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            type_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            &mut status);

                column_descriptions.push(ColumnDescription {
                    name: stringutils::buf_to_string(&name_buffer).unwrap(),
                    data_type: stringutils::buf_to_string(&type_buffer).unwrap(),
                });
            }

            HduInfo::TableInfo {
                column_descriptions: column_descriptions,
                num_rows: num_rows as usize,
            }
        }
        _ => panic!("Invalid hdu type found"),
    };

    fits_try!(status, hdu_type)
}

impl FitsFile {
    /// Open a fits file from disk
    ///
    /// # Examples
    ///
    /// ```
    /// use fitsio::FitsFile;
    ///
    /// let f = FitsFile::open("../testdata/full_example.fits").unwrap();
    ///
    /// // Continue to use `f` afterwards
    /// ```
    pub fn open(filename: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(filename).unwrap();

        unsafe {
            sys::ffopen(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        FileOpenMode::READONLY as libc::c_int,
                        &mut status);
        }

        fits_try!(status,
                  FitsFile {
                      fptr: fptr,
                      filename: filename.to_string(),
                  })
    }

    /// Create a new fits file on disk
    pub fn create(path: &str) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let c_filename = ffi::CString::new(path).unwrap();

        unsafe {
            sys::ffinit(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        &mut status);
        }

        fits_try!(status, {
            let f = FitsFile {
                fptr: fptr,
                filename: path.to_string(),
            };
            try!(f.add_empty_primary());
            f
        })
    }

    fn add_empty_primary(&self) -> Result<()> {
        let mut status = 0;
        unsafe {
            sys::ffphps(self.fptr, 8, 0, ptr::null_mut(), &mut status);
        }

        fits_try!(status, ())
    }

    /// Change the current HDU
    pub fn change_hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<()> {
        hdu_description.change_hdu(self)
    }

    /// Return a new HDU object
    pub fn hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<FitsHdu> {
        FitsHdu::new(self, hdu_description)
    }

    pub fn hdu_number(&self) -> usize {
        let mut hdu_num = 0;
        unsafe {
            sys::ffghdn(self.fptr, &mut hdu_num);
        }
        (hdu_num - 1) as usize
    }

    /// Get the current hdu as an HDU object
    pub fn current_hdu(&self) -> Result<FitsHdu> {
        let current_hdu_number = self.hdu_number();
        self.hdu(current_hdu_number)
    }

    /// Get the current hdu info
    pub fn fetch_hdu_info(&self) -> Result<HduInfo> {
        unsafe { fetch_hdu_info(self.fptr) }
    }

    pub fn create_table(&self,
                        extname: String,
                        table_description: &[ColumnDescription])
                        -> Result<()> {
        let tfields = {
            let stringlist = table_description.iter()
                .map(|desc| desc.name.clone())
                .collect();
            stringutils::StringList::from_vec(stringlist)
        };

        let ttype = {
            let stringlist = table_description.iter()
                .map(|desc| desc.data_type.clone())
                .collect();
            stringutils::StringList::from_vec(stringlist)
        };

        let c_extname = ffi::CString::new(extname).unwrap();

        let mut status: libc::c_int = 0;
        unsafe {
            sys::ffcrtb(self.fptr,
                        HduType::BINARY_TBL.into(),
                        0,
                        tfields.len as libc::c_int,
                        tfields.list,
                        ttype.list,
                        ptr::null_mut(),
                        c_extname.into_raw(),
                        &mut status);
        }

        fits_try!(status, ())
    }

    pub fn create_image(&self,
                        extname: String,
                        image_description: &ImageDescription)
                        -> Result<()> {
        let naxis = image_description.dimensions.len();
        let mut status = 0;
        unsafe {
            sys::ffcrim(self.fptr,
                        image_description.data_type.into(),
                        naxis as i32,
                        image_description.dimensions.as_ptr() as *mut i64,
                        &mut status);
        }

        match status {
            0 => {}
            _ => return status_to_error(status),
        }

        // Current HDU should be at the new HDU
        let current_hdu = try!(self.current_hdu());
        try!(current_hdu.write_key("EXTNAME".into(), extname));

        Ok(())
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        let mut status = 0;
        unsafe {
            sys::ffclos(self.fptr, &mut status);
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use super::*;
    use conversions::typechar_to_data_type;
    use fitserror::FitsError;
    use ::types::*;

    #[test]
    fn typechar_conversions() {
        let input = vec!["X", "B", "L", "A", "I", "J", "E", "D", "C", "M"];
        let expected = vec![DataType::TBIT,
                            DataType::TBYTE,
                            DataType::TLOGICAL,
                            DataType::TSTRING,
                            DataType::TSHORT,
                            DataType::TLONG,
                            DataType::TFLOAT,
                            DataType::TDOUBLE,
                            DataType::TCOMPLEX,
                            DataType::TDBLCOMPLEX];

        for (i, e) in input.iter().zip(expected) {
            assert_eq!(typechar_to_data_type(i), e);
        }
    }

    #[test]
    fn opening_an_existing_file() {
        match FitsFile::open("../testdata/full_example.fits") {
            Ok(_) => {}
            Err(e) => panic!("{:?}", e),
        }
    }

    #[test]
    fn creating_a_new_file() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        FitsFile::create(filename.to_str().unwrap())
            .map(|f| {
                assert!(filename.exists());

                // Ensure the empty primary has been written
                let naxis: i64 = f.hdu(0)
                    .unwrap()
                    .read_key("NAXIS")
                    .unwrap();
                assert_eq!(naxis, 0);
            })
            .unwrap();
    }

    #[test]
    fn fetching_a_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        for i in 0..2 {
            f.change_hdu(i).unwrap();
            assert_eq!(f.hdu_number(), i);
        }

        match f.change_hdu(2) {
            Err(e) => assert_eq!(e.status, 107),
            _ => panic!("Error checking for failure"),
        }

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_number(), 1);
    }

    #[test]
    fn fetching_hdu_info() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        match f.fetch_hdu_info() {
            Ok(HduInfo::ImageInfo { dimensions, shape }) => {
                assert_eq!(dimensions, 2);
                assert_eq!(shape, vec![100, 100]);
            }
            Err(e) => panic!("Error fetching hdu info {:?}", e),
            _ => panic!("Unknown error"),
        }

        f.change_hdu(1).unwrap();
        match f.fetch_hdu_info() {
            Ok(HduInfo::TableInfo { column_descriptions, num_rows }) => {
                assert_eq!(num_rows, 50);
                assert_eq!(column_descriptions.iter()
                               .map(|desc| desc.name.clone())
                               .collect::<Vec<String>>(),
                           vec!["intcol".to_string(),
                                "floatcol".to_string(),
                                "doublecol".to_string()]);
                assert_eq!(column_descriptions.iter()
                               .map(|ref desc| typechar_to_data_type(desc.data_type.clone()))
                               .collect::<Vec<DataType>>(),
                           vec![DataType::TLONG, DataType::TFLOAT, DataType::TDOUBLE]);
            }
            Err(e) => panic!("Error fetching hdu info {:?}", e),
            _ => panic!("Unknown error"),
        }
    }

    #[test]
    fn cloning() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let f2 = f.clone();

        assert!(f.fptr != f2.fptr);

        f.change_hdu(1).unwrap();
        assert!(f.hdu_number() != f2.hdu_number());
    }

    #[test]
    fn test_fits_try() {
        use stringutils;

        let status = 0;
        assert_eq!(fits_try!(status, 10), Ok(10));

        let status = 105;
        assert_eq!(fits_try!(status, 10),
                   Err(FitsError {
                       status: status,
                       message: stringutils::status_to_string(status).unwrap(),
                   }));
    }

    #[test]
    fn adding_new_table() {
        use super::super::columndescription::ColumnDescription;

        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");

        {
            let f = FitsFile::create(filename.to_str().unwrap()).unwrap();
            let table_description = vec![ColumnDescription {
                                             name: "bar".to_string(),
                                             data_type: "1J".to_string(),
                                         }];
            f.create_table("foo".to_string(), &table_description).unwrap();
        }

        FitsFile::open(filename.to_str().unwrap())
            .map(|f| {
                f.change_hdu("foo").unwrap();
                match f.fetch_hdu_info() {
                    Ok(HduInfo::TableInfo { column_descriptions, .. }) => {
                        let column_names = column_descriptions.iter()
                            .map(|desc| desc.name.clone())
                            .collect::<Vec<String>>();
                        let column_types = column_descriptions.iter()
                            .map(|desc| typechar_to_data_type(desc.data_type.clone()))
                            .collect::<Vec<DataType>>();
                        assert_eq!(column_names, vec!["bar".to_string()]);
                        assert_eq!(column_types, vec![DataType::TLONG]);
                    }
                    thing => panic!("{:?}", thing),
                }
            })
            .unwrap();
    }

    #[test]
    fn adding_new_image() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");

        {
            let f = FitsFile::create(filename.to_str().unwrap()).unwrap();
            let image_description = ImageDescription {
                data_type: ImageType::LONG_IMG,
                dimensions: vec![100, 20],
            };
            f.create_image("foo".to_string(), &image_description).unwrap();
        }

        FitsFile::open(filename.to_str().unwrap())
            .map(|f| {
                f.change_hdu("foo").unwrap();
                match f.fetch_hdu_info() {
                    Ok(HduInfo::ImageInfo { shape, .. }) => {
                        assert_eq!(shape, vec![100, 20]);
                    }
                    thing => panic!("{:?}", thing),
                }
            })
            .unwrap();

    }

    #[test]
    fn fetching_hdu_object_hdu_info() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let testext = f.hdu("TESTEXT").unwrap();
        match testext.info {
            HduInfo::TableInfo { num_rows, .. } => {
                assert_eq!(num_rows, 50);
            }
            _ => panic!("Incorrect HDU type found"),
        }
    }

    #[test]
    fn fetch_current_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        f.change_hdu("TESTEXT").unwrap();
        let hdu = f.current_hdu().unwrap();

        assert_eq!(hdu.read_key::<String>("EXTNAME").unwrap(),
                   "TESTEXT".to_string());
    }
}
<commit_msg>Make open and create take Into<String><commit_after>use std::ptr;
use std::ffi;
use super::{stringutils, sys, libc};

use super::fitserror::{FitsError, Result, status_to_error};
use super::fitshdu::{FitsHdu, DescribesHdu};
use super::columndescription::ColumnDescription;
use super::types::{FileOpenMode, HduType, ImageType};

/// Description of the current HDU
///
/// If the current HDU is an image, then
/// [`fetch_hdu_info`](struct.FitsFile.html#method.fetch_hdu_info) returns `HduInfo::ImageInfo`.
/// Otherwise the variant is `HduInfo::TableInfo`.
#[derive(Debug)]
pub enum HduInfo {
    ImageInfo {
        dimensions: usize,
        shape: Vec<usize>,
    },
    TableInfo {
        column_descriptions: Vec<ColumnDescription>,
        num_rows: usize,
    },
}

pub struct ImageDescription {
    pub data_type: ImageType,
    pub dimensions: Vec<usize>,
}

/// Main entry point to the FITS file format
///
///
pub struct FitsFile {
    pub fptr: *mut sys::fitsfile,
    pub filename: String,
}

impl Clone for FitsFile {
    fn clone(&self) -> Self {
        FitsFile::open(self.filename.clone()).unwrap()
    }
}

unsafe fn fetch_hdu_info(fptr: *mut sys::fitsfile) -> Result<HduInfo> {
    let mut status = 0;
    let mut hdu_type = 0;

    sys::ffghdt(fptr, &mut hdu_type, &mut status);
    let hdu_type = match hdu_type {
        0 => {
            let mut dimensions = 0;
            sys::ffgidm(fptr, &mut dimensions, &mut status);

            let mut shape = vec![0; dimensions as usize];
            sys::ffgisz(fptr, dimensions, shape.as_mut_ptr(), &mut status);

            HduInfo::ImageInfo {
                dimensions: dimensions as usize,
                shape: shape.iter().map(|v| *v as usize).collect(),
            }
        }
        1 | 2 => {
            let mut num_rows = 0;
            sys::ffgnrw(fptr, &mut num_rows, &mut status);

            let mut num_cols = 0;
            sys::ffgncl(fptr, &mut num_cols, &mut status);
            let mut column_descriptions = Vec::with_capacity(num_cols as usize);

            for i in 0..num_cols {
                let mut name_buffer: Vec<libc::c_char> = vec![0; 71];
                let mut type_buffer: Vec<libc::c_char> = vec![0; 71];
                sys::ffgbcl(fptr,
                            (i + 1) as i32,
                            name_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            type_buffer.as_mut_ptr(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut(),
                            &mut status);

                column_descriptions.push(ColumnDescription {
                    name: stringutils::buf_to_string(&name_buffer).unwrap(),
                    data_type: stringutils::buf_to_string(&type_buffer).unwrap(),
                });
            }

            HduInfo::TableInfo {
                column_descriptions: column_descriptions,
                num_rows: num_rows as usize,
            }
        }
        _ => panic!("Invalid hdu type found"),
    };

    fits_try!(status, hdu_type)
}

impl FitsFile {
    /// Open a fits file from disk
    ///
    /// # Examples
    ///
    /// ```
    /// use fitsio::FitsFile;
    ///
    /// let f = FitsFile::open("../testdata/full_example.fits").unwrap();
    ///
    /// // Continue to use `f` afterwards
    /// ```
    pub fn open<T: Into<String>>(filename: T) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let filename = filename.into();
        let c_filename = ffi::CString::new(filename.as_str()).unwrap();

        unsafe {
            sys::ffopen(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        FileOpenMode::READONLY as libc::c_int,
                        &mut status);
        }

        fits_try!(status,
                  FitsFile {
                      fptr: fptr,
                      filename: filename.clone(),
                  })
    }

    /// Create a new fits file on disk
    pub fn create<T: Into<String>>(path: T) -> Result<Self> {
        let mut fptr = ptr::null_mut();
        let mut status = 0;
        let path = path.into();
        let c_filename = ffi::CString::new(path.as_str()).unwrap();

        unsafe {
            sys::ffinit(&mut fptr as *mut *mut sys::fitsfile,
                        c_filename.as_ptr(),
                        &mut status);
        }

        fits_try!(status, {
            let f = FitsFile {
                fptr: fptr,
                filename: path.clone(),
            };
            try!(f.add_empty_primary());
            f
        })
    }

    fn add_empty_primary(&self) -> Result<()> {
        let mut status = 0;
        unsafe {
            sys::ffphps(self.fptr, 8, 0, ptr::null_mut(), &mut status);
        }

        fits_try!(status, ())
    }

    /// Change the current HDU
    pub fn change_hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<()> {
        hdu_description.change_hdu(self)
    }

    /// Return a new HDU object
    pub fn hdu<T: DescribesHdu>(&self, hdu_description: T) -> Result<FitsHdu> {
        FitsHdu::new(self, hdu_description)
    }

    pub fn hdu_number(&self) -> usize {
        let mut hdu_num = 0;
        unsafe {
            sys::ffghdn(self.fptr, &mut hdu_num);
        }
        (hdu_num - 1) as usize
    }

    /// Get the current hdu as an HDU object
    pub fn current_hdu(&self) -> Result<FitsHdu> {
        let current_hdu_number = self.hdu_number();
        self.hdu(current_hdu_number)
    }

    /// Get the current hdu info
    pub fn fetch_hdu_info(&self) -> Result<HduInfo> {
        unsafe { fetch_hdu_info(self.fptr) }
    }

    pub fn create_table(&self,
                        extname: String,
                        table_description: &[ColumnDescription])
                        -> Result<()> {
        let tfields = {
            let stringlist = table_description.iter()
                .map(|desc| desc.name.clone())
                .collect();
            stringutils::StringList::from_vec(stringlist)
        };

        let ttype = {
            let stringlist = table_description.iter()
                .map(|desc| desc.data_type.clone())
                .collect();
            stringutils::StringList::from_vec(stringlist)
        };

        let c_extname = ffi::CString::new(extname).unwrap();

        let mut status: libc::c_int = 0;
        unsafe {
            sys::ffcrtb(self.fptr,
                        HduType::BINARY_TBL.into(),
                        0,
                        tfields.len as libc::c_int,
                        tfields.list,
                        ttype.list,
                        ptr::null_mut(),
                        c_extname.into_raw(),
                        &mut status);
        }

        fits_try!(status, ())
    }

    pub fn create_image(&self,
                        extname: String,
                        image_description: &ImageDescription)
                        -> Result<()> {
        let naxis = image_description.dimensions.len();
        let mut status = 0;
        unsafe {
            sys::ffcrim(self.fptr,
                        image_description.data_type.into(),
                        naxis as i32,
                        image_description.dimensions.as_ptr() as *mut i64,
                        &mut status);
        }

        match status {
            0 => {}
            _ => return status_to_error(status),
        }

        // Current HDU should be at the new HDU
        let current_hdu = try!(self.current_hdu());
        try!(current_hdu.write_key("EXTNAME".into(), extname));

        Ok(())
    }
}

impl Drop for FitsFile {
    fn drop(&mut self) {
        let mut status = 0;
        unsafe {
            sys::ffclos(self.fptr, &mut status);
        }
    }
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use super::*;
    use conversions::typechar_to_data_type;
    use fitserror::FitsError;
    use ::types::*;

    #[test]
    fn typechar_conversions() {
        let input = vec!["X", "B", "L", "A", "I", "J", "E", "D", "C", "M"];
        let expected = vec![DataType::TBIT,
                            DataType::TBYTE,
                            DataType::TLOGICAL,
                            DataType::TSTRING,
                            DataType::TSHORT,
                            DataType::TLONG,
                            DataType::TFLOAT,
                            DataType::TDOUBLE,
                            DataType::TCOMPLEX,
                            DataType::TDBLCOMPLEX];

        for (i, e) in input.iter().zip(expected) {
            assert_eq!(typechar_to_data_type(i), e);
        }
    }

    #[test]
    fn opening_an_existing_file() {
        match FitsFile::open("../testdata/full_example.fits") {
            Ok(_) => {}
            Err(e) => panic!("{:?}", e),
        }
    }

    #[test]
    fn creating_a_new_file() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");
        assert!(!filename.exists());

        FitsFile::create(filename.to_str().unwrap())
            .map(|f| {
                assert!(filename.exists());

                // Ensure the empty primary has been written
                let naxis: i64 = f.hdu(0)
                    .unwrap()
                    .read_key("NAXIS")
                    .unwrap();
                assert_eq!(naxis, 0);
            })
            .unwrap();
    }

    #[test]
    fn fetching_a_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        for i in 0..2 {
            f.change_hdu(i).unwrap();
            assert_eq!(f.hdu_number(), i);
        }

        match f.change_hdu(2) {
            Err(e) => assert_eq!(e.status, 107),
            _ => panic!("Error checking for failure"),
        }

        f.change_hdu("TESTEXT").unwrap();
        assert_eq!(f.hdu_number(), 1);
    }

    #[test]
    fn fetching_hdu_info() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        match f.fetch_hdu_info() {
            Ok(HduInfo::ImageInfo { dimensions, shape }) => {
                assert_eq!(dimensions, 2);
                assert_eq!(shape, vec![100, 100]);
            }
            Err(e) => panic!("Error fetching hdu info {:?}", e),
            _ => panic!("Unknown error"),
        }

        f.change_hdu(1).unwrap();
        match f.fetch_hdu_info() {
            Ok(HduInfo::TableInfo { column_descriptions, num_rows }) => {
                assert_eq!(num_rows, 50);
                assert_eq!(column_descriptions.iter()
                               .map(|desc| desc.name.clone())
                               .collect::<Vec<String>>(),
                           vec!["intcol".to_string(),
                                "floatcol".to_string(),
                                "doublecol".to_string()]);
                assert_eq!(column_descriptions.iter()
                               .map(|ref desc| typechar_to_data_type(desc.data_type.clone()))
                               .collect::<Vec<DataType>>(),
                           vec![DataType::TLONG, DataType::TFLOAT, DataType::TDOUBLE]);
            }
            Err(e) => panic!("Error fetching hdu info {:?}", e),
            _ => panic!("Unknown error"),
        }
    }

    #[test]
    fn cloning() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let f2 = f.clone();

        assert!(f.fptr != f2.fptr);

        f.change_hdu(1).unwrap();
        assert!(f.hdu_number() != f2.hdu_number());
    }

    #[test]
    fn test_fits_try() {
        use stringutils;

        let status = 0;
        assert_eq!(fits_try!(status, 10), Ok(10));

        let status = 105;
        assert_eq!(fits_try!(status, 10),
                   Err(FitsError {
                       status: status,
                       message: stringutils::status_to_string(status).unwrap(),
                   }));
    }

    #[test]
    fn adding_new_table() {
        use super::super::columndescription::ColumnDescription;

        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");

        {
            let f = FitsFile::create(filename.to_str().unwrap()).unwrap();
            let table_description = vec![ColumnDescription {
                                             name: "bar".to_string(),
                                             data_type: "1J".to_string(),
                                         }];
            f.create_table("foo".to_string(), &table_description).unwrap();
        }

        FitsFile::open(filename.to_str().unwrap())
            .map(|f| {
                f.change_hdu("foo").unwrap();
                match f.fetch_hdu_info() {
                    Ok(HduInfo::TableInfo { column_descriptions, .. }) => {
                        let column_names = column_descriptions.iter()
                            .map(|desc| desc.name.clone())
                            .collect::<Vec<String>>();
                        let column_types = column_descriptions.iter()
                            .map(|desc| typechar_to_data_type(desc.data_type.clone()))
                            .collect::<Vec<DataType>>();
                        assert_eq!(column_names, vec!["bar".to_string()]);
                        assert_eq!(column_types, vec![DataType::TLONG]);
                    }
                    thing => panic!("{:?}", thing),
                }
            })
            .unwrap();
    }

    #[test]
    fn adding_new_image() {
        let tdir = tempdir::TempDir::new("fitsio-").unwrap();
        let tdir_path = tdir.path();
        let filename = tdir_path.join("test.fits");

        {
            let f = FitsFile::create(filename.to_str().unwrap()).unwrap();
            let image_description = ImageDescription {
                data_type: ImageType::LONG_IMG,
                dimensions: vec![100, 20],
            };
            f.create_image("foo".to_string(), &image_description).unwrap();
        }

        FitsFile::open(filename.to_str().unwrap())
            .map(|f| {
                f.change_hdu("foo").unwrap();
                match f.fetch_hdu_info() {
                    Ok(HduInfo::ImageInfo { shape, .. }) => {
                        assert_eq!(shape, vec![100, 20]);
                    }
                    thing => panic!("{:?}", thing),
                }
            })
            .unwrap();

    }

    #[test]
    fn fetching_hdu_object_hdu_info() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let testext = f.hdu("TESTEXT").unwrap();
        match testext.info {
            HduInfo::TableInfo { num_rows, .. } => {
                assert_eq!(num_rows, 50);
            }
            _ => panic!("Incorrect HDU type found"),
        }
    }

    #[test]
    fn fetch_current_hdu() {
        let f = FitsFile::open("../testdata/full_example.fits").unwrap();
        f.change_hdu("TESTEXT").unwrap();
        let hdu = f.current_hdu().unwrap();

        assert_eq!(hdu.read_key::<String>("EXTNAME").unwrap(),
                   "TESTEXT".to_string());
    }
}
<|endoftext|>


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

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

use dom::{TNode, TElement, UnsafeNode};
use matching::MatchMethods;
use selectors::bloom::BloomFilter;

pub struct StyleBloom {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains. These unsafe
    /// nodes are guaranteed to be elements.
    ///
    /// Note that the use we do for them is safe, since the data we access from
    /// them is completely read-only during restyling.
    elements: Vec<UnsafeNode>,

    /// A monotonic counter incremented which each reflow in order to invalidate
    /// the bloom filter if appropriate.
    generation: u32,
}

impl StyleBloom {
    pub fn new(generation: u32) -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
            generation: generation,
        }
    }

    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    pub fn generation(&self) -> u32 {
        self.generation
    }

    pub fn maybe_pop<E>(&mut self, element: E)
        where E: TElement + MatchMethods
    {
        if self.elements.last() == Some(&element.as_node().to_unsafe()) {
            self.pop::<E>().unwrap();
        }
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push<E>(&mut self, element: E)
        where E: TElement + MatchMethods,
    {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.parent_element().is_none());
            }
        }
        element.insert_into_bloom_filter(&mut *self.filter);
        self.elements.push(element.as_node().to_unsafe());
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop<E>(&mut self) -> Option<E>
        where E: TElement + MatchMethods,
    {
        let popped =
            self.elements.pop().map(|unsafe_node| {
                let parent = unsafe {
                    E::ConcreteNode::from_unsafe(&unsafe_node)
                };
                parent.as_element().unwrap()
            });
        if let Some(popped) = popped {
            popped.remove_from_bloom_filter(&mut self.filter);
        }

        popped
    }

    fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    fn rebuild<E>(&mut self, mut element: E) -> usize
        where E: TElement + MatchMethods,
    {
        self.clear();

        while let Some(parent) = element.parent_element() {
            parent.insert_into_bloom_filter(&mut *self.filter);
            self.elements.push(parent.as_node().to_unsafe());
            element = parent;
        }

        // Put them in the order we expect, from root to `element`'s parent.
        self.elements.reverse();
        return self.elements.len();
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    pub fn assert_complete<E>(&self, mut element: E)
        where E: TElement,
    {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.parent_element() {
                assert_eq!(parent.as_node().to_unsafe(),
                           self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth.
    pub fn insert_parents_recovering<E>(&mut self,
                                        element: E,
                                        element_depth: Option<usize>,
                                        generation: u32)
                                        -> usize
        where E: TElement,
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.generation != generation || self.elements.is_empty() {
            self.generation = generation;
            return self.rebuild(element);
        }

        let parent_element = match element.parent_element() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return 0;
            }
        };

        let unsafe_parent = parent_element.as_node().to_unsafe();
        if self.elements.last() == Some(&unsafe_parent) {
            // Ta da, cache hit, we're all done.
            return self.elements.len();
        }

        let element_depth = match element_depth {
            Some(depth) => depth,
            // If we don't know the depth of `element`, we'd rather don't try
            // fixing up the bloom filter, since it's quadratic.
            None => {
                return self.rebuild(element);
            }
        };

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth > element_depth - 1 {
            self.pop::<E>().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with parent_element.
        let mut common_parent = parent_element;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = vec![];

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        while *self.elements.last().unwrap() != common_parent.as_node().to_unsafe() {
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied again?");
            self.pop::<E>().unwrap();
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.into_iter().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
        return self.elements.len();
    }
}
<commit_msg>Allow the style bloom filter to recover from switch to a node with no common ancestor with the old node.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

use dom::{TNode, TElement, UnsafeNode};
use matching::MatchMethods;
use selectors::bloom::BloomFilter;

pub struct StyleBloom {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains. These unsafe
    /// nodes are guaranteed to be elements.
    ///
    /// Note that the use we do for them is safe, since the data we access from
    /// them is completely read-only during restyling.
    elements: Vec<UnsafeNode>,

    /// A monotonic counter incremented which each reflow in order to invalidate
    /// the bloom filter if appropriate.
    generation: u32,
}

impl StyleBloom {
    pub fn new(generation: u32) -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
            generation: generation,
        }
    }

    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    pub fn generation(&self) -> u32 {
        self.generation
    }

    pub fn maybe_pop<E>(&mut self, element: E)
        where E: TElement + MatchMethods
    {
        if self.elements.last() == Some(&element.as_node().to_unsafe()) {
            self.pop::<E>().unwrap();
        }
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push<E>(&mut self, element: E)
        where E: TElement + MatchMethods,
    {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.parent_element().is_none());
            }
        }
        element.insert_into_bloom_filter(&mut *self.filter);
        self.elements.push(element.as_node().to_unsafe());
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop<E>(&mut self) -> Option<E>
        where E: TElement + MatchMethods,
    {
        let popped =
            self.elements.pop().map(|unsafe_node| {
                let parent = unsafe {
                    E::ConcreteNode::from_unsafe(&unsafe_node)
                };
                parent.as_element().unwrap()
            });
        if let Some(popped) = popped {
            popped.remove_from_bloom_filter(&mut self.filter);
        }

        popped
    }

    fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    fn rebuild<E>(&mut self, mut element: E) -> usize
        where E: TElement + MatchMethods,
    {
        self.clear();

        while let Some(parent) = element.parent_element() {
            parent.insert_into_bloom_filter(&mut *self.filter);
            self.elements.push(parent.as_node().to_unsafe());
            element = parent;
        }

        // Put them in the order we expect, from root to `element`'s parent.
        self.elements.reverse();
        return self.elements.len();
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    pub fn assert_complete<E>(&self, mut element: E)
        where E: TElement,
    {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.parent_element() {
                assert_eq!(parent.as_node().to_unsafe(),
                           self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth.
    pub fn insert_parents_recovering<E>(&mut self,
                                        element: E,
                                        element_depth: Option<usize>,
                                        generation: u32)
                                        -> usize
        where E: TElement,
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.generation != generation || self.elements.is_empty() {
            self.generation = generation;
            return self.rebuild(element);
        }

        let parent_element = match element.parent_element() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return 0;
            }
        };

        let unsafe_parent = parent_element.as_node().to_unsafe();
        if self.elements.last() == Some(&unsafe_parent) {
            // Ta da, cache hit, we're all done.
            return self.elements.len();
        }

        let element_depth = match element_depth {
            Some(depth) => depth,
            // If we don't know the depth of `element`, we'd rather don't try
            // fixing up the bloom filter, since it's quadratic.
            None => {
                return self.rebuild(element);
            }
        };

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth > element_depth - 1 {
            self.pop::<E>().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with parent_element.
        let mut common_parent = parent_element;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = vec![];

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        //
        // Gecko currently models native anonymous content that conceptually hangs
        // off the document (such as scrollbars) as a separate subtree from the
        // document root.  Thus it's possible with Gecko that we do not find any
        // common ancestor.
        while *self.elements.last().unwrap() != common_parent.as_node().to_unsafe() {
            parents_to_insert.push(common_parent);
            self.pop::<E>().unwrap();
            common_parent = match common_parent.parent_element() {
                Some(parent) => parent,
                None => {
                    debug_assert!(self.elements.is_empty());
                    if cfg!(feature = "gecko") {
                        break;
                    } else {
                        panic!("should have found a common ancestor");
                    }
                }
            }
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.into_iter().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
        return self.elements.len();
    }
}
<|endoftext|>


<commit_before>// main.rs
// Main entry point for a Rust version
// of some (micro-) benchmarks
//
// vim: ft=rust sw=4 ts=4
//

extern crate image;
extern crate num;
extern crate num_complex;
extern crate rayon;

use std::time::{Instant, Duration};

// declare submodules
mod fibonacci;
mod mandelbrot;
mod perfect_number;
mod primes;
mod montecarlo;

// a helper function to time closures
fn time_it<F, T>(fun: F) -> (T, Duration)
where
    F: Fn() -> T,
{
    let tic = Instant::now();
    let res = fun();
    let toc = tic.elapsed();

    (res, toc)
}

fn main() {
    println!("Rust benchmarks");
    println!("===============");

    println!();

    println!("Fibonacci numbers:");
    println!("------------------");

    let (res, elap) = time_it(|| fibonacci::fib_naive(35));
    println!("fib_naive(35) = {}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| fibonacci::fib(35));
    println!("fib(35) = {}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| fibonacci::fib(1000));
    println!("fib(1000) = {}\tElapsed: {}ms", res, elap.as_millis());

    println!();

    println!("Prime numbers:");
    println!("--------------");

    let (res, elap) = time_it(|| primes::find_primes(1_000_000));
    println!(
        "find_primes(1_000_000): {:?} primes\tElapsed: {}ms",
        res.len(),
        elap.as_millis()
    );

    let (res, elap) = time_it(|| primes::find_primes_rayon(1_000_000));
    println!(
        "find_primes_rayon(1_000_000): {:?} primes\tElapsed: {}ms",
        res.len(),
        elap.as_millis()
    );

    println!();

    println!("Perfect numbers:");
    println!("----------------");

    let (res, elap) = time_it(|| perfect_number::perfect_numbers(10000));
    println!("pn(10000) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| perfect_number::perfect_number_iter(4));
    println!("pn_iter(4) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| perfect_number::perfect_numbers_rayon(10000));
    println!("pn_rayon(10000) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    println!();

    println!("Mandelbrot set:");
    println!("--------------");

    let (res, elap) = time_it(|| mandelbrot::mandelbrot(640, 480, -0.5, 0.0, 4.0 / f64::from(640)));
    println!("mandelbrot(640, 480) done\tElapsed: {}ms", elap.as_millis());
    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!("mandelbrot(640, 480) written to PNG\tElapsed: {}ms", elap.as_millis());

    let (res, elap) =
        time_it(|| mandelbrot::mandelbrot(1920, 1200, -0.5, 0.0, 4.0 / f64::from(1920)));
    println!("mandelbrot(1920, 1200) done\tElapsed: {}ms", elap.as_millis());
    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!("mandelbrot(1920, 1200) written to PNG\tElapsed: {}ms", elap.as_millis());

    let _: Vec<()> = vec![1, 2, 4, 8, 16, 30, 60]
        .into_iter()
        .map(|i| {
            let (_res, elap) = time_it(|| {
                mandelbrot::mandelbrot_rayon(1920, 1200, -0.5, 0.0, 4.0 / f64::from(1920), i)
            });
            println!(
                "mandelbrot_rayon(1920, 1200, {}) done\tElapsed: {}ms",
                i, elap.as_millis()
            );
        })
        .collect();

    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!(
        "mandelbrot_rayon(1920, 1200) written to PNG\tElapsed: {}ms",
        elap.as_millis()
    );
    println!();

    println!("Monte-Carlo simulations:");
    println!("------------------------");

    let mc_count = 10_000_000;
    let (res, elap) = time_it(|| montecarlo::simulate_pi(mc_count));
    println!("simulate_pi({}) = {}\tElapsed: {}ms", mc_count, res, elap.as_millis());
    println!();

    println!("------------------");
    println!("Done!");
}
<commit_msg>increase mc count<commit_after>// main.rs
// Main entry point for a Rust version
// of some (micro-) benchmarks
//
// vim: ft=rust sw=4 ts=4
//

extern crate image;
extern crate num;
extern crate num_complex;
extern crate rayon;

use std::time::{Instant, Duration};

// declare submodules
mod fibonacci;
mod mandelbrot;
mod perfect_number;
mod primes;
mod montecarlo;

// a helper function to time closures
fn time_it<F, T>(fun: F) -> (T, Duration)
where
    F: Fn() -> T,
{
    let tic = Instant::now();
    let res = fun();
    let toc = tic.elapsed();

    (res, toc)
}

fn main() {
    println!("Rust benchmarks");
    println!("===============");

    println!();

    println!("Fibonacci numbers:");
    println!("------------------");

    let (res, elap) = time_it(|| fibonacci::fib_naive(35));
    println!("fib_naive(35) = {}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| fibonacci::fib(35));
    println!("fib(35) = {}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| fibonacci::fib(1000));
    println!("fib(1000) = {}\tElapsed: {}ms", res, elap.as_millis());

    println!();

    println!("Prime numbers:");
    println!("--------------");

    let (res, elap) = time_it(|| primes::find_primes(1_000_000));
    println!(
        "find_primes(1_000_000): {:?} primes\tElapsed: {}ms",
        res.len(),
        elap.as_millis()
    );

    let (res, elap) = time_it(|| primes::find_primes_rayon(1_000_000));
    println!(
        "find_primes_rayon(1_000_000): {:?} primes\tElapsed: {}ms",
        res.len(),
        elap.as_millis()
    );

    println!();

    println!("Perfect numbers:");
    println!("----------------");

    let (res, elap) = time_it(|| perfect_number::perfect_numbers(10000));
    println!("pn(10000) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| perfect_number::perfect_number_iter(4));
    println!("pn_iter(4) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    let (res, elap) = time_it(|| perfect_number::perfect_numbers_rayon(10000));
    println!("pn_rayon(10000) = {:?}\tElapsed: {}ms", res, elap.as_millis());

    println!();

    println!("Mandelbrot set:");
    println!("--------------");

    let (res, elap) = time_it(|| mandelbrot::mandelbrot(640, 480, -0.5, 0.0, 4.0 / f64::from(640)));
    println!("mandelbrot(640, 480) done\tElapsed: {}ms", elap.as_millis());
    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!("mandelbrot(640, 480) written to PNG\tElapsed: {}ms", elap.as_millis());

    let (res, elap) =
        time_it(|| mandelbrot::mandelbrot(1920, 1200, -0.5, 0.0, 4.0 / f64::from(1920)));
    println!("mandelbrot(1920, 1200) done\tElapsed: {}ms", elap.as_millis());
    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!("mandelbrot(1920, 1200) written to PNG\tElapsed: {}ms", elap.as_millis());

    let _: Vec<()> = vec![1, 2, 4, 8, 16, 30, 60]
        .into_iter()
        .map(|i| {
            let (_res, elap) = time_it(|| {
                mandelbrot::mandelbrot_rayon(1920, 1200, -0.5, 0.0, 4.0 / f64::from(1920), i)
            });
            println!(
                "mandelbrot_rayon(1920, 1200, {}) done\tElapsed: {}ms",
                i, elap.as_millis()
            );
        })
        .collect();

    let (_, elap) = time_it(|| res.save("./mandelbrot.png").unwrap());
    println!(
        "mandelbrot_rayon(1920, 1200) written to PNG\tElapsed: {}ms",
        elap.as_millis()
    );
    println!();

    println!("Monte-Carlo simulations:");
    println!("------------------------");

    let mc_count = 100_000_000;
    let (res, elap) = time_it(|| montecarlo::simulate_pi(mc_count));
    println!("simulate_pi({}) = {}\tElapsed: {}ms", mc_count, res, elap.as_millis());
    println!();

    println!("------------------");
    println!("Done!");
}
<|endoftext|>


<commit_before>use conduit::{box_error, header, Body, Handler, HandlerResult, RequestExt, Response, StatusCode};
use conduit_mime_types as mime;
use filetime::FileTime;
use std::fs::File;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;

pub struct Static {
    path: PathBuf,
    types: mime::Types,
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new().expect("Couldn't load mime-types"),
        }
    }
}

impl Handler for Static {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let request_path = &request.path()[1..];
        if request_path.contains("..") {
            return Ok(not_found());
        }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = file.metadata().map_err(box_error)?;
        if data.is_dir() {
            return Ok(not_found());
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let mtime = OffsetDateTime::from_unix_timestamp(mtime.unix_seconds() as i64);

        Response::builder()
            .header(header::CONTENT_TYPE, mime)
            .header(header::CONTENT_LENGTH, data.len())
            .header(header::LAST_MODIFIED, mtime.format("%a, %d %b %Y %T GMT"))
            .body(Body::File(file))
            .map_err(box_error)
    }
}

fn not_found() -> Response<Body> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .header(header::CONTENT_LENGTH, 0)
        .header(header::CONTENT_TYPE, "text/plain")
        .body(Body::empty())
        .unwrap()
}

#[cfg(test)]
mod tests {
    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use crate::Static;
    use conduit::{header, Handler, Method, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root);
        File::create(&root.join("Cargo.toml"))
            .unwrap()
            .write_all(b"[package]")
            .unwrap();
        let mut req = MockRequest::new(Method::GET, "/Cargo.toml");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(
            res.headers().get(header::CONTENT_TYPE).unwrap(),
            "text/plain"
        );
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "9");
        assert_eq!(*res.into_cow(), b"[package]"[..]);
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/src/fixture.css");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/css");
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "0");
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/nope");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/test");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.headers().get(header::LAST_MODIFIED).is_some());
    }
}
<commit_msg>static: Adjust to new `conduit-mime-types` API<commit_after>use conduit::{box_error, header, Body, Handler, HandlerResult, RequestExt, Response, StatusCode};
use conduit_mime_types as mime;
use filetime::FileTime;
use std::fs::File;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;

pub struct Static {
    path: PathBuf,
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
        }
    }
}

impl Handler for Static {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let request_path = &request.path()[1..];
        if request_path.contains("..") {
            return Ok(not_found());
        }

        let path = self.path.join(request_path);
        let mime = mime::mime_for_path(&path).unwrap_or("application/octet-stream");
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = file.metadata().map_err(box_error)?;
        if data.is_dir() {
            return Ok(not_found());
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let mtime = OffsetDateTime::from_unix_timestamp(mtime.unix_seconds() as i64);

        Response::builder()
            .header(header::CONTENT_TYPE, mime)
            .header(header::CONTENT_LENGTH, data.len())
            .header(header::LAST_MODIFIED, mtime.format("%a, %d %b %Y %T GMT"))
            .body(Body::File(file))
            .map_err(box_error)
    }
}

fn not_found() -> Response<Body> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .header(header::CONTENT_LENGTH, 0)
        .header(header::CONTENT_TYPE, "text/plain")
        .body(Body::empty())
        .unwrap()
}

#[cfg(test)]
mod tests {
    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use crate::Static;
    use conduit::{header, Handler, Method, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root);
        File::create(&root.join("Cargo.toml"))
            .unwrap()
            .write_all(b"[package]")
            .unwrap();
        let mut req = MockRequest::new(Method::GET, "/Cargo.toml");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(
            res.headers().get(header::CONTENT_TYPE).unwrap(),
            "application/toml"
        );
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "9");
        assert_eq!(*res.into_cow(), b"[package]"[..]);
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/src/fixture.css");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/css");
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "0");
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/nope");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/test");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.headers().get(header::LAST_MODIFIED).is_some());
    }
}
<|endoftext|>


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

use std::collections::HashSet;

use syn::{self, visit};
use syn::punctuated::Punctuated;

use internals::ast::{Data, Container};
use internals::attr;

use proc_macro2::Span;

// Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not
// allowed here".
pub fn without_defaults(generics: &syn::Generics) -> syn::Generics {
    syn::Generics {
        params: generics
            .params
            .iter()
            .map(|param| match *param {
                syn::GenericParam::Type(ref param) => {
                    syn::GenericParam::Type(syn::TypeParam {
                        eq_token: None,
                        default: None,
                        ..param.clone()
                    })
                }
                _ => param.clone(),
            })
            .collect(),
        ..generics.clone()
    }
}

pub fn with_where_predicates(
    generics: &syn::Generics,
    predicates: &[syn::WherePredicate],
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates.into_iter().cloned());
    generics
}

pub fn with_where_predicates_from_fields<F>(
    cont: &Container,
    generics: &syn::Generics,
    from_field: F,
) -> syn::Generics
where
    F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
{
    let predicates = cont.data
        .all_fields()
        .flat_map(|field| from_field(&field.attrs))
        .flat_map(|predicates| predicates.to_vec());

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates);
    generics
}

// Puts the given bound on any generic type parameters that are used in fields
// for which filter returns true.
//
// For example, the following struct needs the bound `A: Serialize, B: Serialize`.
//
//     struct S<'b, A, B: 'b, C> {
//         a: A,
//         b: Option<&'b B>
//         #[serde(skip_serializing)]
//         c: C,
//     }
pub fn with_bound<F>(
    cont: &Container,
    generics: &syn::Generics,
    filter: F,
    bound: &syn::Path,
) -> syn::Generics
where
    F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
    struct FindTyParams {
        // Set of all generic type parameters on the current struct (A, B, C in
        // the example). Initialized up front.
        all_ty_params: HashSet<syn::Ident>,
        // Set of generic type parameters used in fields for which filter
        // returns true (A and B in the example). Filled in as the visitor sees
        // them.
        relevant_ty_params: HashSet<syn::Ident>,
    }
    impl<'ast> visit::Visit<'ast> for FindTyParams {
        fn visit_path(&mut self, path: &syn::Path) {
            if let Some(seg) = path.segments.last() {
                if seg.into_value().ident == "PhantomData" {
                    // Hardcoded exception, because PhantomData<T> implements
                    // Serialize and Deserialize whether or not T implements it.
                    return;
                }
            }
            if path.leading_colon.is_none() && path.segments.len() == 1 {
                let id = path.segments[0].ident;
                if self.all_ty_params.contains(&id) {
                    self.relevant_ty_params.insert(id);
                }
            }
            visit::visit_path(self, path);
        }

        // Type parameter should not be considered used by a macro path.
        //
        //     struct TypeMacro<T> {
        //         mac: T!(),
        //         marker: PhantomData<T>,
        //     }
        fn visit_macro(&mut self, _mac: &syn::Macro) {}
    }

    let all_ty_params: HashSet<_> = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .collect();

    let mut visitor = FindTyParams {
        all_ty_params: all_ty_params,
        relevant_ty_params: HashSet::new(),
    };
    match cont.data {
        Data::Enum(ref variants) => for variant in variants.iter() {
            let relevant_fields = variant
                .fields
                .iter()
                .filter(|field| filter(&field.attrs, Some(&variant.attrs)));
            for field in relevant_fields {
                visit::visit_type(&mut visitor, field.ty);
            }
        },
        Data::Struct(_, ref fields) => {
            for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
                visit::visit_type(&mut visitor, field.ty);
            }
        }
    }

    let new_predicates = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .filter(|id| visitor.relevant_ty_params.contains(id))
        .map(|id| {
            syn::WherePredicate::Type(syn::PredicateType {
                lifetimes: None,
                // the type parameter that is being bounded e.g. T
                bounded_ty: syn::Type::Path(syn::TypePath {
                    qself: None,
                    path: id.into(),
                }),
                colon_token: Default::default(),
                // the bound e.g. Serialize
                bounds: vec![
                    syn::TypeParamBound::Trait(syn::TraitBound {
                        paren_token: None,
                        modifier: syn::TraitBoundModifier::None,
                        lifetimes: None,
                        path: bound.clone(),
                    }),
                ].into_iter().collect(),
            })
        });

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(new_predicates);
    generics
}

pub fn with_self_bound(
    cont: &Container,
    generics: &syn::Generics,
    bound: &syn::Path,
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .push(syn::WherePredicate::Type(syn::PredicateType {
            lifetimes: None,
            // the type that is being bounded e.g. MyStruct<'a, T>
            bounded_ty: type_of_item(cont),
            colon_token: Default::default(),
            // the bound e.g. Default
            bounds: vec![
                syn::TypeParamBound::Trait(syn::TraitBound {
                    paren_token: None,
                    modifier: syn::TraitBoundModifier::None,
                    lifetimes: None,
                    path: bound.clone(),
                }),
            ].into_iter().collect(),
        }));
    generics
}

pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
    let bound = syn::Lifetime::new(lifetime, Span::call_site());
    let def = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: bound,
        colon_token: None,
        bounds: Punctuated::new(),
    };

    let params = Some(syn::GenericParam::Lifetime(def))
        .into_iter()
        .chain(generics.params
            .iter()
            .cloned()
            .map(|mut param| {
                match param {
                    syn::GenericParam::Lifetime(ref mut param) => {
                        param.bounds.push(bound);
                    }
                    syn::GenericParam::Type(ref mut param) => {
                        param.bounds.push(syn::TypeParamBound::Lifetime(bound));
                    }
                    syn::GenericParam::Const(_) => {}
                }
                param
            }))
        .collect();

    syn::Generics {
        params: params,
        ..generics.clone()
    }
}

fn type_of_item(cont: &Container) -> syn::Type {
    syn::Type::Path(syn::TypePath {
        qself: None,
        path: syn::Path {
            leading_colon: None,
            segments: vec![
                syn::PathSegment {
                    ident: cont.ident,
                    arguments: syn::PathArguments::AngleBracketed(
                        syn::AngleBracketedGenericArguments {
                            colon2_token: None,
                            lt_token: Default::default(),
                            args: cont.generics
                                .params
                                .iter()
                                .map(|param| match *param {
                                    syn::GenericParam::Type(ref param) => {
                                        syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
                                            qself: None,
                                            path: param.ident.into(),
                                        }))
                                    }
                                    syn::GenericParam::Lifetime(ref param) => {
                                        syn::GenericArgument::Lifetime(param.lifetime)
                                    }
                                    syn::GenericParam::Const(_) => {
                                        panic!("Serde does not support const generics yet");
                                    }
                                })
                                .collect(),
                            gt_token: Default::default(),
                        },
                    ),
                },
            ].into_iter().collect(),
        },
    })
}
<commit_msg>Use Generics::make_where_clause helper<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashSet;

use syn::{self, visit};
use syn::punctuated::Punctuated;

use internals::ast::{Data, Container};
use internals::attr;

use proc_macro2::Span;

// Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not
// allowed here".
pub fn without_defaults(generics: &syn::Generics) -> syn::Generics {
    syn::Generics {
        params: generics
            .params
            .iter()
            .map(|param| match *param {
                syn::GenericParam::Type(ref param) => {
                    syn::GenericParam::Type(syn::TypeParam {
                        eq_token: None,
                        default: None,
                        ..param.clone()
                    })
                }
                _ => param.clone(),
            })
            .collect(),
        ..generics.clone()
    }
}

pub fn with_where_predicates(
    generics: &syn::Generics,
    predicates: &[syn::WherePredicate],
) -> syn::Generics {
    let mut generics = generics.clone();
    generics.make_where_clause()
        .predicates
        .extend(predicates.into_iter().cloned());
    generics
}

pub fn with_where_predicates_from_fields<F>(
    cont: &Container,
    generics: &syn::Generics,
    from_field: F,
) -> syn::Generics
where
    F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
{
    let predicates = cont.data
        .all_fields()
        .flat_map(|field| from_field(&field.attrs))
        .flat_map(|predicates| predicates.to_vec());

    let mut generics = generics.clone();
    generics.make_where_clause()
        .predicates
        .extend(predicates);
    generics
}

// Puts the given bound on any generic type parameters that are used in fields
// for which filter returns true.
//
// For example, the following struct needs the bound `A: Serialize, B: Serialize`.
//
//     struct S<'b, A, B: 'b, C> {
//         a: A,
//         b: Option<&'b B>
//         #[serde(skip_serializing)]
//         c: C,
//     }
pub fn with_bound<F>(
    cont: &Container,
    generics: &syn::Generics,
    filter: F,
    bound: &syn::Path,
) -> syn::Generics
where
    F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
    struct FindTyParams {
        // Set of all generic type parameters on the current struct (A, B, C in
        // the example). Initialized up front.
        all_ty_params: HashSet<syn::Ident>,
        // Set of generic type parameters used in fields for which filter
        // returns true (A and B in the example). Filled in as the visitor sees
        // them.
        relevant_ty_params: HashSet<syn::Ident>,
    }
    impl<'ast> visit::Visit<'ast> for FindTyParams {
        fn visit_path(&mut self, path: &syn::Path) {
            if let Some(seg) = path.segments.last() {
                if seg.into_value().ident == "PhantomData" {
                    // Hardcoded exception, because PhantomData<T> implements
                    // Serialize and Deserialize whether or not T implements it.
                    return;
                }
            }
            if path.leading_colon.is_none() && path.segments.len() == 1 {
                let id = path.segments[0].ident;
                if self.all_ty_params.contains(&id) {
                    self.relevant_ty_params.insert(id);
                }
            }
            visit::visit_path(self, path);
        }

        // Type parameter should not be considered used by a macro path.
        //
        //     struct TypeMacro<T> {
        //         mac: T!(),
        //         marker: PhantomData<T>,
        //     }
        fn visit_macro(&mut self, _mac: &syn::Macro) {}
    }

    let all_ty_params: HashSet<_> = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .collect();

    let mut visitor = FindTyParams {
        all_ty_params: all_ty_params,
        relevant_ty_params: HashSet::new(),
    };
    match cont.data {
        Data::Enum(ref variants) => for variant in variants.iter() {
            let relevant_fields = variant
                .fields
                .iter()
                .filter(|field| filter(&field.attrs, Some(&variant.attrs)));
            for field in relevant_fields {
                visit::visit_type(&mut visitor, field.ty);
            }
        },
        Data::Struct(_, ref fields) => {
            for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
                visit::visit_type(&mut visitor, field.ty);
            }
        }
    }

    let new_predicates = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .filter(|id| visitor.relevant_ty_params.contains(id))
        .map(|id| {
            syn::WherePredicate::Type(syn::PredicateType {
                lifetimes: None,
                // the type parameter that is being bounded e.g. T
                bounded_ty: syn::Type::Path(syn::TypePath {
                    qself: None,
                    path: id.into(),
                }),
                colon_token: Default::default(),
                // the bound e.g. Serialize
                bounds: vec![
                    syn::TypeParamBound::Trait(syn::TraitBound {
                        paren_token: None,
                        modifier: syn::TraitBoundModifier::None,
                        lifetimes: None,
                        path: bound.clone(),
                    }),
                ].into_iter().collect(),
            })
        });

    let mut generics = generics.clone();
    generics.make_where_clause()
        .predicates
        .extend(new_predicates);
    generics
}

pub fn with_self_bound(
    cont: &Container,
    generics: &syn::Generics,
    bound: &syn::Path,
) -> syn::Generics {
    let mut generics = generics.clone();
    generics.make_where_clause()
        .predicates
        .push(syn::WherePredicate::Type(syn::PredicateType {
            lifetimes: None,
            // the type that is being bounded e.g. MyStruct<'a, T>
            bounded_ty: type_of_item(cont),
            colon_token: Default::default(),
            // the bound e.g. Default
            bounds: vec![
                syn::TypeParamBound::Trait(syn::TraitBound {
                    paren_token: None,
                    modifier: syn::TraitBoundModifier::None,
                    lifetimes: None,
                    path: bound.clone(),
                }),
            ].into_iter().collect(),
        }));
    generics
}

pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
    let bound = syn::Lifetime::new(lifetime, Span::call_site());
    let def = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: bound,
        colon_token: None,
        bounds: Punctuated::new(),
    };

    let params = Some(syn::GenericParam::Lifetime(def))
        .into_iter()
        .chain(generics.params
            .iter()
            .cloned()
            .map(|mut param| {
                match param {
                    syn::GenericParam::Lifetime(ref mut param) => {
                        param.bounds.push(bound);
                    }
                    syn::GenericParam::Type(ref mut param) => {
                        param.bounds.push(syn::TypeParamBound::Lifetime(bound));
                    }
                    syn::GenericParam::Const(_) => {}
                }
                param
            }))
        .collect();

    syn::Generics {
        params: params,
        ..generics.clone()
    }
}

fn type_of_item(cont: &Container) -> syn::Type {
    syn::Type::Path(syn::TypePath {
        qself: None,
        path: syn::Path {
            leading_colon: None,
            segments: vec![
                syn::PathSegment {
                    ident: cont.ident,
                    arguments: syn::PathArguments::AngleBracketed(
                        syn::AngleBracketedGenericArguments {
                            colon2_token: None,
                            lt_token: Default::default(),
                            args: cont.generics
                                .params
                                .iter()
                                .map(|param| match *param {
                                    syn::GenericParam::Type(ref param) => {
                                        syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
                                            qself: None,
                                            path: param.ident.into(),
                                        }))
                                    }
                                    syn::GenericParam::Lifetime(ref param) => {
                                        syn::GenericArgument::Lifetime(param.lifetime)
                                    }
                                    syn::GenericParam::Const(_) => {
                                        panic!("Serde does not support const generics yet");
                                    }
                                })
                                .collect(),
                            gt_token: Default::default(),
                        },
                    ),
                },
            ].into_iter().collect(),
        },
    })
}
<|endoftext|>


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

use std::fs;
use std::path::Path;

use common::ui::{Status, UI};
use hcore::package::{PackageIdent, PackageInstall};
use hcore::os::filesystem;
use hcore::fs as hfs;

use error::{Error, Result};

pub fn start(ui: &mut UI,
             ident: &PackageIdent,
             binary: &str,
             dest_path: &Path,
             fs_root_path: &Path)
             -> Result<()> {
    let dst_path = fs_root_path.join(try!(dest_path.strip_prefix("/")));
    let dst = dst_path.join(&binary);
    try!(ui.begin(format!("Symlinking {} from {} into {}",
                          &binary,
                          &ident,
                          dst_path.display())));
    let pkg_install = try!(PackageInstall::load(&ident, Some(fs_root_path)));
    let src = match try!(hfs::find_command_in_pkg(binary, &pkg_install, fs_root_path)) {
        Some(c) => c,
        None => {
            return Err(Error::CommandNotFoundInPkg((pkg_install.ident().to_string(),
                                                    binary.to_string())))
        }
    };
    if !dst_path.is_dir() {
        try!(ui.status(Status::Creating,
                       format!("parent directory {}", dst_path.display())));
        try!(fs::create_dir_all(&dst_path))
    }
    match fs::read_link(&dst) {
        Ok(path) => {
            if path != src {
                try!(fs::remove_file(&dst));
                try!(filesystem::symlink(&src, &dst));
            }
        }
        Err(_) => try!(filesystem::symlink(&src, &dst)),
    }
    try!(ui.end(format!("Binary {} from {} symlinked to {}",
                        &binary,
                        &pkg_install.ident(),
                        &dst.display())));
    Ok(())
}

pub fn binlink_all_in_pkg(ui: &mut UI,
                          pkg_ident: &PackageIdent,
                          dest_path: &Path,
                          fs_root_path: &Path)
                          -> Result<()> {
    let pkg_path = PackageInstall::load(&pkg_ident, Some(fs_root_path))?;
    for bin_path in pkg_path.paths()? {
        for bin in fs::read_dir(&bin_path)? {
            let bin_file = bin?;
            let bin_name = bin_file.file_name()
                .to_str()
                .ok_or(Error::Utf8Error("Not a valid Utf8 string".to_string()))?
                .to_owned();
            self::start(ui, &pkg_ident, &bin_name, &dest_path, &fs_root_path)?;
        }
    }
    Ok(())
}
<commit_msg>Updating binlink_all_in_pkg to warn and skip when it encounters a file with an invalid name.<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;
use std::path::Path;

use common::ui::{Status, UI};
use hcore::package::{PackageIdent, PackageInstall};
use hcore::os::filesystem;
use hcore::fs as hfs;

use error::{Error, Result};

pub fn start(ui: &mut UI,
             ident: &PackageIdent,
             binary: &str,
             dest_path: &Path,
             fs_root_path: &Path)
             -> Result<()> {
    let dst_path = fs_root_path.join(try!(dest_path.strip_prefix("/")));
    let dst = dst_path.join(&binary);
    try!(ui.begin(format!("Symlinking {} from {} into {}",
                          &binary,
                          &ident,
                          dst_path.display())));
    let pkg_install = try!(PackageInstall::load(&ident, Some(fs_root_path)));
    let src = match try!(hfs::find_command_in_pkg(binary, &pkg_install, fs_root_path)) {
        Some(c) => c,
        None => {
            return Err(Error::CommandNotFoundInPkg((pkg_install.ident().to_string(),
                                                    binary.to_string())))
        }
    };
    if !dst_path.is_dir() {
        try!(ui.status(Status::Creating,
                       format!("parent directory {}", dst_path.display())));
        try!(fs::create_dir_all(&dst_path))
    }
    match fs::read_link(&dst) {
        Ok(path) => {
            if path != src {
                try!(fs::remove_file(&dst));
                try!(filesystem::symlink(&src, &dst));
            }
        }
        Err(_) => try!(filesystem::symlink(&src, &dst)),
    }
    try!(ui.end(format!("Binary {} from {} symlinked to {}",
                        &binary,
                        &pkg_install.ident(),
                        &dst.display())));
    Ok(())
}

pub fn binlink_all_in_pkg(ui: &mut UI,
                          pkg_ident: &PackageIdent,
                          dest_path: &Path,
                          fs_root_path: &Path)
                          -> Result<()> {
    let pkg_path = PackageInstall::load(&pkg_ident, Some(fs_root_path))?;
    for bin_path in pkg_path.paths()? {
        for bin in fs::read_dir(&bin_path)? {
            let bin_file = bin?;
            let bin_name = match bin_file.file_name().to_str() {
                Some(bn) => bn.to_owned(),
                None => {
                    try!(ui.warn("Found a binary with an invalid name.  Skipping binlink."));
                    continue;
                }
            };
            self::start(ui, &pkg_ident, &bin_name, &dest_path, &fs_root_path)?;
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use clap::{App, Arg};

fn main() {
    // You can define a function (or a closure) to use as a validator to argument values. The
    // function must accept a String and return Result<(), String> where Err(String) is the message
    // displayed to the user.

    let matches = App::new("myapp")
        // Application logic goes here...
        .arg(
            Arg::new("input")
                .about("the input file to use")
                .index(1)
                .required(true)
                // You can pass in a closure, or a function
                .validator(is_png),
        )
        .get_matches();

    // Here we can call .unwrap() because the argument is required.
    println!("The .PNG file is: {}", matches.value_of("input").unwrap());
}

fn is_png(val: &str) -> Result<(), String> {
    // val is the argument value passed in by the user
    // val has type of String.
    if val.ends_with(".png") {
        Ok(())
    } else {
        // clap automatically adds "error: " to the beginning
        // of the message.
        Err(String::from("the file format must be png."))
    }
    // Of course, you can do more complicated validation as
    // well, but for the simplicity, this example only checks
    // if the value passed in ends with ".png" or not.
}
<commit_msg>fix(example): Use correct type in comment<commit_after>use clap::{App, Arg};

fn main() {
    // You can define a function (or a closure) to use as a validator to argument values. The
    // function must accept a `&str` and return `Result<(), String>` where `Err(String)` is the
    // message displayed to the user.

    let matches = App::new("myapp")
        // Application logic goes here...
        .arg(
            Arg::new("input")
                .about("the input file to use")
                .index(1)
                .required(true)
                // You can pass in a closure, or a function
                .validator(is_png),
        )
        .get_matches();

    // Here we can call .unwrap() because the argument is required.
    println!("The .PNG file is: {}", matches.value_of("input").unwrap());
}

fn is_png(val: &str) -> Result<(), String> {
    // val is the argument value passed in by the user
    // val has type of String.
    if val.ends_with(".png") {
        Ok(())
    } else {
        // clap automatically adds "error: " to the beginning
        // of the message.
        Err(String::from("the file format must be png."))
    }
    // Of course, you can do more complicated validation as
    // well, but for the simplicity, this example only checks
    // if the value passed in ends with ".png" or not.
}
<|endoftext|>