<commit_before>//! # FixedPoint
//!
//! A simple library for computing the fixed point of a given function
//!
//! ## Example
//! ```rust
//! extern crate fixedpoint;
//!
//! use fixedpoint::fixedpoint;
//!
//! fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
//!     150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
//! }
//!
//! fn main() {
//!      let val = fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap();
//!      println!("Fixed Point of function exists at: {}", val);
//! }
//! ```

// Clippy Lints
// #![warn(cast_possible_truncation)]
// #![warn(cast_possible_wrap)]
// #![warn(cast_sign_loss)]
// #![warn(filter_map)]
// #![warn(if_not_else)]
// #![warn(items_after_statements)]
// #![warn(nonminimal_bool)]
// #![warn(option_map_unwrap_or)]
// #![warn(option_map_unwrap_or_else)]
// #![warn(option_unwrap_used)]
// #![warn(shadow_reuse)]
// #![warn(shadow_same)]
// #![warn(shadow_unrelated)]
// #![warn(single_match_else)]
// #![warn(wrong_pub_self_convention)]

#![warn(missing_docs,
        missing_debug_implementations,
        missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unsafe_code,
        unstable_features,
        unused_import_braces, unused_qualifications)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;

#[allow(missing_docs)]
pub mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! {
        errors {
            ValueLimit
            IterLimit(limit: usize) {
                description("Max iteration limit exceeded")
                display("Could not converge function after {} iterations", limit)
            }
        }
    }
}

use errors::*;

/// Compute the fixed point of a given function
///
/// Given a function `func`, compute the fixed point of the function such that `func(x, args) = x`
/// `args` is a reference to any object that the function uses as auxillary data
/// `maxiter` is used to limit the maximum number of iterations that the function will go through
/// before giving up. The default is 100.
/// If a `maxval` is provided, then the fixedpoint computation will stop when the value of the
/// function exceeds `maxval`. This is mostly useful in providing upper bounds on monotonic
/// functions.
///
/// The `args` parameter is generic. So you can pass arbitrary data types to it that only the
/// provided function needs to know how to interpret. This allows you to pass a `Option`, `struct`,
/// `Result` or any other type of complex object as well.
pub fn fixedpoint<T, U>(func: &Fn(U, &T) -> U, x0: U, args: &T, maxiter: Option<usize>, maxval: Option<U>) -> Result<U>
where U: PartialEq + PartialOrd + Copy + std::fmt::Debug {
    let maxiter = maxiter.unwrap_or(100);
    let mut itr = maxiter;
    let mut x = x0;
    let mut val = func(x0, args);
    while val != x {
        trace!("Iteration: {}", maxiter - itr);
        x = val;
        val = func(x, args);
        trace!("\tx: {:?}; F(x): {:?}", x, val);
        itr -= 1;
        if itr == 0 {
            debug!("Max Iterations reached. Last value: {:?}", val);
            bail!(ErrorKind::IterLimit(maxiter));
        } else if val > maxval.unwrap_or(val) {
            debug!("Maximum value of function reached. Last value: {:?}", val);
            bail!(ErrorKind::ValueLimit);
        }
    };
    Ok(val)
}

#[cfg(test)]
mod tests {
    use fixedpoint;

    fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
        150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
    }

    #[test]
    fn basic_tests() {
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap(), 450);
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, Some(400)).is_err(), true);
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &10, Some(5), None).is_err(), true);
    }

    #[test]
    #[should_panic(expected = "attempt to multiply with overflow")]
    fn maxiter() {
        fixedpoint(&func_with_fixed_point, 0, &10, Some(10), None).unwrap_or(0);
    }
}
<commit_msg>Add more clippy lints<commit_after>//! # FixedPoint
//!
//! A simple library for computing the fixed point of a given function
//!
//! ## Example
//! ```rust
//! extern crate fixedpoint;
//!
//! use fixedpoint::fixedpoint;
//!
//! fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
//!     150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
//! }
//!
//! fn main() {
//!      let val = fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap();
//!      println!("Fixed Point of function exists at: {}", val);
//! }
//! ```

// Clippy Lints
// #![warn(cast_possible_truncation)]
// #![warn(cast_possible_wrap)]
// #![warn(cast_precision_loss)]
// #![warn(cast_sign_loss)]
// #![warn(empty_enum)]
// #![warn(enum_glob_use)]
// #![warn(filter_map)]
// #![warn(if_not_else)]
// #![warn(indexing_slicing)]
// #![warn(invalid_upcast_comparisons)]
// #![warn(items_after_statements)]
// #![warn(missing_docs_in_private_items)]
// #![warn(mut_mut)]
// #![warn(nonminimal_bool)]
// #![warn(option_map_unwrap_or)]
// #![warn(option_map_unwrap_or_else)]
// #![warn(option_unwrap_used)]
// #![warn(pub_enum_variant_names)]
// #![warn(result_unwrap_used)]
// #![warn(shadow_reuse)]
// #![warn(shadow_same)]
// #![warn(shadow_unrelated)]
// #![warn(similar_names)]
// #![warn(single_match_else)]
// #![warn(stutter)]
// #![warn(wrong_pub_self_convention)]

#![warn(missing_docs,
        missing_debug_implementations,
        missing_copy_implementations,
        trivial_casts, trivial_numeric_casts,
        unsafe_code,
        unstable_features,
        unused_import_braces, unused_qualifications)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;

#[allow(missing_docs)]
pub mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain! {
        errors {
            ValueLimit
            IterLimit(limit: usize) {
                description("Max iteration limit exceeded")
                display("Could not converge function after {} iterations", limit)
            }
        }
    }
}

use errors::*;

/// Compute the fixed point of a given function
///
/// Given a function `func`, compute the fixed point of the function such that `func(x, args) = x`
/// `args` is a reference to any object that the function uses as auxillary data
/// `maxiter` is used to limit the maximum number of iterations that the function will go through
/// before giving up. The default is 100.
/// If a `maxval` is provided, then the fixedpoint computation will stop when the value of the
/// function exceeds `maxval`. This is mostly useful in providing upper bounds on monotonic
/// functions.
///
/// The `args` parameter is generic. So you can pass arbitrary data types to it that only the
/// provided function needs to know how to interpret. This allows you to pass a `Option`, `struct`,
/// `Result` or any other type of complex object as well.
pub fn fixedpoint<T, U>(func: &Fn(U, &T) -> U, x0: U, args: &T, maxiter: Option<usize>, maxval: Option<U>) -> Result<U>
where U: PartialEq + PartialOrd + Copy + std::fmt::Debug {
    let maxiter = maxiter.unwrap_or(100);
    let mut itr = maxiter;
    let mut x = x0;
    let mut val = func(x0, args);
    while val != x {
        trace!("Iteration: {}", maxiter - itr);
        x = val;
        val = func(x, args);
        trace!("\tx: {:?}; F(x): {:?}", x, val);
        itr -= 1;
        if itr == 0 {
            debug!("Max Iterations reached. Last value: {:?}", val);
            bail!(ErrorKind::IterLimit(maxiter));
        } else if val > maxval.unwrap_or(val) {
            debug!("Maximum value of function reached. Last value: {:?}", val);
            bail!(ErrorKind::ValueLimit);
        }
    };
    Ok(val)
}

#[cfg(test)]
mod tests {
    use fixedpoint;

    fn func_with_fixed_point(num: u32, param: &u32) -> u32 {
        150 + (((num as f32 / param.clone() as f32).ceil() as u32)*100)
    }

    #[test]
    fn basic_tests() {
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, None).unwrap(), 450);
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &150, None, Some(400)).is_err(), true);
        assert_eq!(fixedpoint(&func_with_fixed_point, 0, &10, Some(5), None).is_err(), true);
    }

    #[test]
    #[should_panic(expected = "attempt to multiply with overflow")]
    fn maxiter() {
        fixedpoint(&func_with_fixed_point, 0, &10, Some(10), None).unwrap_or(0);
    }
}
<|endoftext|>


<commit_before>//! Temporary files and directories.
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! ## Resource Leaking
//!
//! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if
//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
//! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tempfile()`]: fn.tempfile.html
//! [`tempdir()`]: fn.tempdir.html
//! [`TempDir`]: struct.TempDir.html
//! [`NamedTempFile`]: struct.NamedTempFile.html
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://docs.rs/tempfile/2.2.0"
)]
#![cfg_attr(test, deny(warnings))]

#[macro_use]
extern crate cfg_if;
extern crate rand;
extern crate remove_dir_all;

#[cfg(unix)]
extern crate libc;

#[cfg(windows)]
extern crate winapi;

#[cfg(target_os = "redox")]
extern crate syscall;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

use std::ffi::OsStr;
use std::path::Path;
use std::{env, io};

mod dir;
mod error;
mod file;
mod spooled;
mod util;

pub use dir::{tempdir, tempdir_in, TempDir};
pub use file::{tempfile, tempfile_in, NamedTempFile, PersistError, TempPath};
pub use spooled::{spooled_tempfile, SpooledTempFile};

/// Create a new temporary file or directory with custom parameters.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Builder<'a, 'b> {
    random_len: usize,
    prefix: &'a OsStr,
    suffix: &'b OsStr,
}

impl<'a, 'b> Default for Builder<'a, 'b> {
    fn default() -> Self {
        Builder {
            random_len: ::NUM_RAND_CHARS,
            prefix: OsStr::new(".tmp"),
            suffix: OsStr::new(""),
        }
    }
}

impl<'a, 'b> Builder<'a, 'b> {
    /// Create a new `Builder`.
    ///
    /// # Examples
    ///
    /// Create a named temporary file and write some data into it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .suffix(".txt")
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    ///
    /// let name = named_tempfile
    ///     .path()
    ///     .file_name().and_then(OsStr::to_str);
    ///
    /// if let Some(name) = name {
    ///     assert!(name.starts_with("my-temporary-note"));
    ///     assert!(name.ends_with(".txt"));
    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Create a temporary directory and add a file to it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io::{self, Write};
    /// # use std::fs::File;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let dir = Builder::new()
    ///     .prefix("my-temporary-dir")
    ///     .rand_bytes(5)
    ///     .tempdir()?;
    ///
    /// let file_path = dir.path().join("my-temporary-note.txt");
    /// let mut file = File::create(file_path)?;
    /// writeln!(file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly, we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(file);
    /// dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Set a custom filename prefix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: `.tmp`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
        self.prefix = prefix.as_ref();
        self
    }

    /// Set a custom filename suffix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .suffix(".txt")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
        self.suffix = suffix.as_ref();
        self
    }

    /// Set the number of random bytes.
    ///
    /// Default: `6`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
        self.random_len = rand;
        self
    }

    /// Create the named temporary file.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
        self.tempfile_in(&env::temp_dir())
    }

    /// Create the named temporary file in the specified directory.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
        util::create_helper(
            dir.as_ref(),
            self.prefix,
            self.suffix,
            self.random_len,
            file::create_named,
        )
    }

    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
    /// name will have the prefix, `prefix`. The directory and
    /// everything inside it will be automatically deleted once the
    /// returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir(&self) -> io::Result<TempDir> {
        self.tempdir_in(&env::temp_dir())
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
        let storage;
        let mut dir = dir.as_ref();
        if !dir.is_absolute() {
            let cur_dir = env::current_dir()?;
            storage = cur_dir.join(dir);
            dir = &storage;
        }

        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
    }
}
<commit_msg>fix: export PathPersistError<commit_after>//! Temporary files and directories.
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! ## Resource Leaking
//!
//! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if
//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
//! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tempfile()`]: fn.tempfile.html
//! [`tempdir()`]: fn.tempdir.html
//! [`TempDir`]: struct.TempDir.html
//! [`NamedTempFile`]: struct.NamedTempFile.html
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://docs.rs/tempfile/2.2.0"
)]
#![cfg_attr(test, deny(warnings))]

#[macro_use]
extern crate cfg_if;
extern crate rand;
extern crate remove_dir_all;

#[cfg(unix)]
extern crate libc;

#[cfg(windows)]
extern crate winapi;

#[cfg(target_os = "redox")]
extern crate syscall;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

use std::ffi::OsStr;
use std::path::Path;
use std::{env, io};

mod dir;
mod error;
mod file;
mod spooled;
mod util;

pub use dir::{tempdir, tempdir_in, TempDir};
pub use file::{tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath};
pub use spooled::{spooled_tempfile, SpooledTempFile};

/// Create a new temporary file or directory with custom parameters.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Builder<'a, 'b> {
    random_len: usize,
    prefix: &'a OsStr,
    suffix: &'b OsStr,
}

impl<'a, 'b> Default for Builder<'a, 'b> {
    fn default() -> Self {
        Builder {
            random_len: ::NUM_RAND_CHARS,
            prefix: OsStr::new(".tmp"),
            suffix: OsStr::new(""),
        }
    }
}

impl<'a, 'b> Builder<'a, 'b> {
    /// Create a new `Builder`.
    ///
    /// # Examples
    ///
    /// Create a named temporary file and write some data into it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .suffix(".txt")
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    ///
    /// let name = named_tempfile
    ///     .path()
    ///     .file_name().and_then(OsStr::to_str);
    ///
    /// if let Some(name) = name {
    ///     assert!(name.starts_with("my-temporary-note"));
    ///     assert!(name.ends_with(".txt"));
    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Create a temporary directory and add a file to it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io::{self, Write};
    /// # use std::fs::File;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let dir = Builder::new()
    ///     .prefix("my-temporary-dir")
    ///     .rand_bytes(5)
    ///     .tempdir()?;
    ///
    /// let file_path = dir.path().join("my-temporary-note.txt");
    /// let mut file = File::create(file_path)?;
    /// writeln!(file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly, we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(file);
    /// dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Set a custom filename prefix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: `.tmp`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
        self.prefix = prefix.as_ref();
        self
    }

    /// Set a custom filename suffix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .suffix(".txt")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
        self.suffix = suffix.as_ref();
        self
    }

    /// Set the number of random bytes.
    ///
    /// Default: `6`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
        self.random_len = rand;
        self
    }

    /// Create the named temporary file.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
        self.tempfile_in(&env::temp_dir())
    }

    /// Create the named temporary file in the specified directory.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
        util::create_helper(
            dir.as_ref(),
            self.prefix,
            self.suffix,
            self.random_len,
            file::create_named,
        )
    }

    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
    /// name will have the prefix, `prefix`. The directory and
    /// everything inside it will be automatically deleted once the
    /// returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir(&self) -> io::Result<TempDir> {
        self.tempdir_in(&env::temp_dir())
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
        let storage;
        let mut dir = dir.as_ref();
        if !dir.is_absolute() {
            let cur_dir = env::current_dir()?;
            storage = cur_dir.join(dir);
            dir = &storage;
        }

        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate helix;
extern crate csv;

use std::error::Error;
use std::io::Read;
use std::slice::from_raw_parts;
use helix::sys;
use helix::sys::{VALUE, ID};
use helix::{UncheckedValue, CheckResult, CheckedValue, ToRust, ToRuby};
use helix::libc::c_int;

struct VecWrap<T>(Vec<T>);

impl<T> UncheckedValue<VecWrap<T>> for VALUE
    where VALUE: UncheckedValue<T>
{
    fn to_checked(self) -> CheckResult<VecWrap<T>> {
        if unsafe { sys::RB_TYPE_P(self, sys::T_ARRAY) } {
            let slice = ruby_array_to_slice(self);
            for val in slice.iter() {
                if let Err(error) = val.to_checked() {
                    return Err(format!("Failed to convert value for Vec<T>: {}", error));
                }
            }
            Ok(unsafe { CheckedValue::<VecWrap<T>>::new(self) })
        } else {
            let val = unsafe { CheckedValue::<String>::new(sys::rb_inspect(self)) };
            Err(format!("No implicit conversion of {} into Vec<String>",
                        val.to_rust()))
        }
    }
}

fn ruby_array_to_slice<'a>(array: VALUE) -> &'a [VALUE] {
    let length = unsafe { sys::RARRAY_LEN(array) } as usize;
    unsafe { from_raw_parts(sys::RARRAY_CONST_PTR(array), length) }
}

impl ToRust<VecWrap<String>> for CheckedValue<VecWrap<String>>
    where VALUE: UncheckedValue<String>,
          CheckedValue<String>: ToRust<String>
{
    fn to_rust(self) -> VecWrap<String> {
        let slice = ruby_array_to_slice(self.inner);
        let mut vec: Vec<String> = Vec::with_capacity(slice.len());
        for val in slice.iter() {
            let checked = val.to_checked().unwrap();
            vec.push(checked.to_rust());
        }
        VecWrap(vec)
    }
}

impl ToRust<VecWrap<VecWrap<String>>> for CheckedValue<VecWrap<VecWrap<String>>>
    where VALUE: UncheckedValue<VecWrap<String>>,
          CheckedValue<VecWrap<String>>: ToRust<VecWrap<String>>
{
    fn to_rust(self) -> VecWrap<VecWrap<String>> {
        let slice = ruby_array_to_slice(self.inner);
        let mut vec: Vec<VecWrap<String>> = Vec::with_capacity(slice.len());
        for val in slice.iter() {
            let checked = val.to_checked().unwrap();
            vec.push(checked.to_rust());
        }
        VecWrap(vec)
    }
}

#[cfg_attr(windows, link(name="helix-runtime"))]
extern "C" {
    pub fn rb_ary_new_capa(capa: isize) -> VALUE;
    pub fn rb_ary_entry(ary: VALUE, offset: isize) -> VALUE;
    pub fn rb_ary_push(ary: VALUE, item: VALUE) -> VALUE;
    pub fn rb_block_given_p() -> c_int;
    pub fn rb_yield(value: VALUE);
    pub fn rb_funcall(value: VALUE, name: ID, nargs: c_int, ...) -> VALUE;
}

impl ToRuby for VecWrap<csv::StringRecord> {
    fn to_ruby(self) -> VALUE {
        let ary = unsafe { rb_ary_new_capa(self.0.len() as isize) };
        for row in self.0 {
            let inner_array = unsafe { rb_ary_new_capa(row.len() as isize) };
            for column in row.iter() {
                unsafe {
                    rb_ary_push(inner_array, column.to_ruby());
                }
            }
            unsafe {
                rb_ary_push(ary, inner_array);
            }
        }
        ary
    }
}

fn generate_lines(rows: VecWrap<VecWrap<String>>) -> Result<String, Box<Error>> {
    let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
    for row in rows.0 {
        wtr.write_record(&(row.0))?;
    }

    Ok(String::from_utf8(wtr.into_inner()?)?)
}

fn record_to_ruby(record: &csv::ByteRecord) -> VALUE {
    let inner_array = unsafe { rb_ary_new_capa(record.len() as isize) };
    for column in record.iter() {
        unsafe {
            let column_value = sys::rb_utf8_str_new(column.as_ptr() as *const i8,
                                                    column.len() as i64);
            rb_ary_push(inner_array, column_value);
        }
    }
    inner_array
}

impl UncheckedValue<Enumerator> for VALUE {
    fn to_checked(self) -> CheckResult<Enumerator> {
        Ok(unsafe { CheckedValue::new(self) })
    }
}

impl ToRust<Enumerator> for CheckedValue<Enumerator> {
    fn to_rust(self) -> Enumerator {
        Enumerator { value: self.inner }
    }
}

struct Enumerator {
    value: VALUE,
}

struct EnumeratorRead {
    value: VALUE,
    next: Option<Vec<u8>>,
}

impl EnumeratorRead {
    fn new(value: VALUE) -> EnumeratorRead {
        EnumeratorRead {
            value: value,
            next: None,
        }
    }

    fn read_and_store_overflow(&mut self, buf: &mut [u8], value: &[u8]) -> std::io::Result<usize> {
        if value.len() > buf.len() {
            match value.split_at(buf.len()) {
                (current, next) => {
                    for (index, c) in current.iter().enumerate() {
                        buf[index] = *c;
                    }
                    self.next = Some(next.to_vec());
                    Ok(current.len())
                }
            }

        } else {
            for (index, value) in value.iter().enumerate() {
                buf[index] = *value;
            }
            self.next = None;
            Ok(value.len() as usize)
        }
    }

    fn read_from_external(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        let next = unsafe {
            rb_funcall(self.value,
                       sys::rb_intern("next\0".as_ptr() as *const i8),
                       0)
        };
        let slice = unsafe {
            from_raw_parts(sys::RSTRING_PTR(next) as *const u8,
                           sys::RSTRING_LEN(next) as usize)
        };

        self.read_and_store_overflow(buf, slice)
    }
}

impl Read for EnumeratorRead {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        match self.next.clone() {
            Some(inner) => self.read_and_store_overflow(buf, &inner),
            None => self.read_from_external(buf),
        }
    }
}

fn csv_reader<R: Read>(reader: R) -> csv::Reader<R> {
    csv::ReaderBuilder::new()
        .buffer_capacity(16 * 1024)
        .has_headers(false)
        .from_reader(reader)
}

fn yield_csv(data: Enumerator) -> Result<(), csv::Error> {
    let mut reader = csv_reader(EnumeratorRead::new(data.value));
    let mut record = csv::ByteRecord::new();

    while reader.read_byte_record(&mut record)? {
        let inner_array = record_to_ruby(&record);
        unsafe {
            rb_yield(inner_array);
        }
    }

    Ok(())
}

fn parse_csv(data: String) -> Result<Vec<csv::StringRecord>, csv::Error> {
    let mut reader = csv_reader(data.as_bytes());
    reader
        .records()
        .collect::<Result<Vec<csv::StringRecord>, csv::Error>>()
}

ruby! {
    class RscsvReader {
        def each_internal(data: Enumerator) {
            match yield_csv(data) {
                Err(_) => throw!("Error parsing CSV"),
                Ok(_) => ()
            }
        }
        def parse(data: String) -> VecWrap<csv::StringRecord> {
            match parse_csv(data) {
                Err(_) => throw!("Error parsing CSV"),
                Ok(result) => VecWrap(result)
            }
        }
    }
    class RscsvWriter {
        def generate_line(row: VecWrap<String>) -> String {
            let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
            let result = wtr.write_record(&(row.0));
            match result {
                Err(_) => throw!("Error generating csv"),
                Ok(_) => String::from_utf8(wtr.into_inner().unwrap()).unwrap(),
            }
        }

        def generate_lines(rows: VecWrap<VecWrap<String>>) -> String {
            match generate_lines(rows) {
                Err(_) => throw!("Error generating csv"),
                Ok(csv) => csv
            }
        }
    }
}
<commit_msg>Run rustfmt<commit_after>#[macro_use]
extern crate helix;
extern crate csv;

use std::error::Error;
use std::io::Read;
use std::slice::from_raw_parts;
use helix::sys;
use helix::sys::{ID, VALUE};
use helix::{CheckResult, CheckedValue, ToRuby, ToRust, UncheckedValue};
use helix::libc::c_int;

struct VecWrap<T>(Vec<T>);

impl<T> UncheckedValue<VecWrap<T>> for VALUE
where
    VALUE: UncheckedValue<T>,
{
    fn to_checked(self) -> CheckResult<VecWrap<T>> {
        if unsafe { sys::RB_TYPE_P(self, sys::T_ARRAY) } {
            let slice = ruby_array_to_slice(self);
            for val in slice.iter() {
                if let Err(error) = val.to_checked() {
                    return Err(format!("Failed to convert value for Vec<T>: {}", error));
                }
            }
            Ok(unsafe { CheckedValue::<VecWrap<T>>::new(self) })
        } else {
            let val = unsafe { CheckedValue::<String>::new(sys::rb_inspect(self)) };
            Err(format!(
                "No implicit conversion of {} into Vec<String>",
                val.to_rust()
            ))
        }
    }
}

fn ruby_array_to_slice<'a>(array: VALUE) -> &'a [VALUE] {
    let length = unsafe { sys::RARRAY_LEN(array) } as usize;
    unsafe { from_raw_parts(sys::RARRAY_CONST_PTR(array), length) }
}

impl ToRust<VecWrap<String>> for CheckedValue<VecWrap<String>>
where
    VALUE: UncheckedValue<String>,
    CheckedValue<String>: ToRust<String>,
{
    fn to_rust(self) -> VecWrap<String> {
        let slice = ruby_array_to_slice(self.inner);
        let mut vec: Vec<String> = Vec::with_capacity(slice.len());
        for val in slice.iter() {
            let checked = val.to_checked().unwrap();
            vec.push(checked.to_rust());
        }
        VecWrap(vec)
    }
}

impl ToRust<VecWrap<VecWrap<String>>> for CheckedValue<VecWrap<VecWrap<String>>>
where
    VALUE: UncheckedValue<VecWrap<String>>,
    CheckedValue<VecWrap<String>>: ToRust<VecWrap<String>>,
{
    fn to_rust(self) -> VecWrap<VecWrap<String>> {
        let slice = ruby_array_to_slice(self.inner);
        let mut vec: Vec<VecWrap<String>> = Vec::with_capacity(slice.len());
        for val in slice.iter() {
            let checked = val.to_checked().unwrap();
            vec.push(checked.to_rust());
        }
        VecWrap(vec)
    }
}

#[cfg_attr(windows, link(name = "helix-runtime"))]
extern "C" {
    pub fn rb_ary_new_capa(capa: isize) -> VALUE;
    pub fn rb_ary_entry(ary: VALUE, offset: isize) -> VALUE;
    pub fn rb_ary_push(ary: VALUE, item: VALUE) -> VALUE;
    pub fn rb_block_given_p() -> c_int;
    pub fn rb_yield(value: VALUE);
    pub fn rb_funcall(value: VALUE, name: ID, nargs: c_int, ...) -> VALUE;
}

impl ToRuby for VecWrap<csv::StringRecord> {
    fn to_ruby(self) -> VALUE {
        let ary = unsafe { rb_ary_new_capa(self.0.len() as isize) };
        for row in self.0 {
            let inner_array = unsafe { rb_ary_new_capa(row.len() as isize) };
            for column in row.iter() {
                unsafe {
                    rb_ary_push(inner_array, column.to_ruby());
                }
            }
            unsafe {
                rb_ary_push(ary, inner_array);
            }
        }
        ary
    }
}

fn generate_lines(rows: VecWrap<VecWrap<String>>) -> Result<String, Box<Error>> {
    let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
    for row in rows.0 {
        wtr.write_record(&(row.0))?;
    }

    Ok(String::from_utf8(wtr.into_inner()?)?)
}

fn record_to_ruby(record: &csv::ByteRecord) -> VALUE {
    let inner_array = unsafe { rb_ary_new_capa(record.len() as isize) };
    for column in record.iter() {
        unsafe {
            let column_value =
                sys::rb_utf8_str_new(column.as_ptr() as *const i8, column.len() as i64);
            rb_ary_push(inner_array, column_value);
        }
    }
    inner_array
}

impl UncheckedValue<Enumerator> for VALUE {
    fn to_checked(self) -> CheckResult<Enumerator> {
        Ok(unsafe { CheckedValue::new(self) })
    }
}

impl ToRust<Enumerator> for CheckedValue<Enumerator> {
    fn to_rust(self) -> Enumerator {
        Enumerator { value: self.inner }
    }
}

struct Enumerator {
    value: VALUE,
}

struct EnumeratorRead {
    value: VALUE,
    next: Option<Vec<u8>>,
}

impl EnumeratorRead {
    fn new(value: VALUE) -> EnumeratorRead {
        EnumeratorRead {
            value: value,
            next: None,
        }
    }

    fn read_and_store_overflow(&mut self, buf: &mut [u8], value: &[u8]) -> std::io::Result<usize> {
        if value.len() > buf.len() {
            match value.split_at(buf.len()) {
                (current, next) => {
                    for (index, c) in current.iter().enumerate() {
                        buf[index] = *c;
                    }
                    self.next = Some(next.to_vec());
                    Ok(current.len())
                }
            }

        } else {
            for (index, value) in value.iter().enumerate() {
                buf[index] = *value;
            }
            self.next = None;
            Ok(value.len() as usize)
        }
    }

    fn read_from_external(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        let next = unsafe {
            rb_funcall(
                self.value,
                sys::rb_intern("next\0".as_ptr() as *const i8),
                0,
            )
        };
        let slice = unsafe {
            from_raw_parts(
                sys::RSTRING_PTR(next) as *const u8,
                sys::RSTRING_LEN(next) as usize,
            )
        };

        self.read_and_store_overflow(buf, slice)
    }
}

impl Read for EnumeratorRead {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        match self.next.clone() {
            Some(inner) => self.read_and_store_overflow(buf, &inner),
            None => self.read_from_external(buf),
        }
    }
}

fn csv_reader<R: Read>(reader: R) -> csv::Reader<R> {
    csv::ReaderBuilder::new()
        .buffer_capacity(16 * 1024)
        .has_headers(false)
        .from_reader(reader)
}

fn yield_csv(data: Enumerator) -> Result<(), csv::Error> {
    let mut reader = csv_reader(EnumeratorRead::new(data.value));
    let mut record = csv::ByteRecord::new();

    while reader.read_byte_record(&mut record)? {
        let inner_array = record_to_ruby(&record);
        unsafe {
            rb_yield(inner_array);
        }
    }

    Ok(())
}

fn parse_csv(data: String) -> Result<Vec<csv::StringRecord>, csv::Error> {
    let mut reader = csv_reader(data.as_bytes());
    reader
        .records()
        .collect::<Result<Vec<csv::StringRecord>, csv::Error>>()
}

ruby! {
    class RscsvReader {
        def each_internal(data: Enumerator) {
            match yield_csv(data) {
                Err(_) => throw!("Error parsing CSV"),
                Ok(_) => ()
            }
        }
        def parse(data: String) -> VecWrap<csv::StringRecord> {
            match parse_csv(data) {
                Err(_) => throw!("Error parsing CSV"),
                Ok(result) => VecWrap(result)
            }
        }
    }
    class RscsvWriter {
        def generate_line(row: VecWrap<String>) -> String {
            let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
            let result = wtr.write_record(&(row.0));
            match result {
                Err(_) => throw!("Error generating csv"),
                Ok(_) => String::from_utf8(wtr.into_inner().unwrap()).unwrap(),
            }
        }

        def generate_lines(rows: VecWrap<VecWrap<String>>) -> String {
            match generate_lines(rows) {
                Err(_) => throw!("Error generating csv"),
                Ok(csv) => csv
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate futures;
extern crate tokio_core;
extern crate tokio_service;
extern crate hyper_tls;
extern crate rustc_serialize;
extern crate ring;
extern crate crypto;
extern crate untrusted;

use hyper::client::Client;
use hyper_tls::HttpsConnector;
use hyper::client::{Request as HttpRequest, Response as HttpResponse};
use hyper::header::{Authorization, ContentLength, RetryAfter};
use hyper::Post;
use futures::{Future, Poll};
use futures::future::{ok, err};
use ring::{hmac, hkdf, agreement, rand, digest, error};
use crypto::aes_gcm::AesGcm;
use crypto::aes::KeySize;
use std::convert::From;
use crypto::aead::AeadEncryptor;
use std::error::Error;
use std::fmt;
use tokio_core::reactor::Handle;
use tokio_service::Service;
use rustc_serialize::base64::{ToBase64, URL_SAFE};
use rustc_serialize::json;
use hyper::status::StatusCode;
use untrusted::Input;

#[derive(PartialEq, Debug)]
pub enum WebPushError {
    Unspecified,
    Unauthorized,
    BadRequest,
    ServerError(Option<RetryAfter>),
    KeyAgreement,
}

#[derive(Debug)]
pub struct WebPushPayload {
    pub content: Vec<u8>,
    pub public_key: Vec<u8>,
    pub salt: Vec<u8>,
}

impl WebPushPayload {
    pub fn new(content: Vec<u8>, public_key: Vec<u8>, salt: Vec<u8>) -> WebPushPayload {
        WebPushPayload {
            content: content,
            public_key: public_key,
            salt: salt,
        }
    }
}

#[derive(Debug)]
pub struct WebPushMessage {
    pub gcm_key: Option<String>,
    pub endpoint: String,
    pub ttl: Option<u32>,
    pub payload: Option<WebPushPayload>,
}

pub struct WebPushMessageBuilder<'a> {
    gcm_key: Option<&'a str>,
    endpoint: &'a str,
    auth: &'a [u8],
    p256dh: &'a [u8],
    payload: Option<&'a [u8]>,
    ttl: Option<u32>,
}

impl From<error::Unspecified> for WebPushError {
    fn from(e: error::Unspecified) -> WebPushError {
        println!("{:?}", e);
        WebPushError::Unspecified
    }
}

impl Error for WebPushError {
    fn description(&self) -> &str {
        match self {
            _ => "An unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for WebPushError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl<'a> WebPushMessageBuilder<'a> {
    pub fn new(endpoint: &'a str, auth: &'a [u8], p256dh: &'a [u8]) -> WebPushMessageBuilder<'a> {
        WebPushMessageBuilder {
            endpoint: endpoint,
            auth: auth,
            p256dh: p256dh,
            ttl: None,
            gcm_key: None,
            payload: None,
        }
    }

    pub fn set_ttl(&mut self, ttl: u32) {
        self.ttl = Some(ttl);
    }

    pub fn set_gcm_key(&mut self, gcm_key: &'a str) {
        self.gcm_key = Some(gcm_key);
    }

    pub fn set_payload(&mut self, payload: &'a [u8]) {
        self.payload = Some(payload);
    }

    pub fn build(self) -> Result<WebPushMessage, WebPushError> {
        if let Some(payload) = self.payload {
            let rng               = rand::SystemRandom::new();
            let private_key       = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng).unwrap();
            let mut public_key    = [0u8; agreement::PUBLIC_KEY_MAX_LEN];
            let public_key        = &mut public_key[..private_key.public_key_len()];
            let mut random_bytes  = [0u8; 16];

            rng.fill(&mut random_bytes)?;

            let salt = hmac::SigningKey::new(&digest::SHA256, &random_bytes);

            private_key.compute_public_key(public_key)?;

            agreement::agree_ephemeral(private_key, &agreement::ECDH_P256, Input::from(self.p256dh), WebPushError::KeyAgreement, |shared_secret| {
                let client_auth_secret = hmac::SigningKey::new(&digest::SHA256, self.auth);

                let mut auth_info: Vec<u8> = Vec::new();
                auth_info.extend_from_slice("WebPush: info".as_bytes());
                auth_info.push(0);
                auth_info.extend_from_slice(self.p256dh);
                auth_info.extend_from_slice(public_key);
                auth_info.push(1);

                let mut prk = [0u8; 32];
                hkdf::extract_and_expand(&client_auth_secret, &shared_secret, &auth_info, &mut prk);

                let mut content_encryption_info: Vec<u8> = Vec::new();
                content_encryption_info.extend_from_slice("Content-Encoding: aes128gcm".as_bytes());
                content_encryption_info.push(0);
                content_encryption_info.push(1);

                let mut content_encryption_key = [0u8; 16];
                hkdf::extract_and_expand(&salt, &prk, &content_encryption_info, &mut content_encryption_key);

                let mut nonce_info: Vec<u8> = Vec::new();
                nonce_info.extend_from_slice("Content-Encoding: nonce".as_bytes());
                nonce_info.push(0);
                nonce_info.push(1);
                let mut nonce = [0u8; 12];
                hkdf::extract_and_expand(&salt, &prk, &nonce_info, &mut nonce);

                //let mut padded_payload = [0u8; 4080];
                //Self::pad(payload, &mut padded_payload);

                let mut aes_gcm = AesGcm::new(KeySize::KeySize128, &shared_secret, &nonce, "".as_bytes());
                let mut encrypted_payload = [0u8; 4080];
                let mut encrypted_payload = &mut encrypted_payload[..payload.len()];
                let mut tag = [0u8; 16];

                aes_gcm.encrypt(&payload, &mut encrypted_payload, &mut tag);

                let mut full_payload = Vec::with_capacity(encrypted_payload.len() + tag.len());
                full_payload.extend_from_slice(&encrypted_payload);
                full_payload.extend_from_slice(&tag);

                let web_push_payload = WebPushPayload::new(full_payload, public_key.to_vec(), random_bytes.to_vec());

                Ok(WebPushMessage {
                    gcm_key: self.gcm_key.map(|k| k.to_string()),
                    endpoint: self.endpoint.to_string(),
                    ttl: self.ttl,
                    payload: Some(web_push_payload),
                })
            })
        } else {
            Ok(WebPushMessage {
                gcm_key: self.gcm_key.map(|k| k.to_string()),
                endpoint: self.endpoint.to_string(),
                ttl: self.ttl,
                payload: None,
            })
        }
    }

    fn pad(payload: &[u8], output: &mut [u8]) {
        let payload_len = payload.len();
        let max_payload = output.len() - 2;
        let padding_size = max_payload - payload.len();

        output[0] = (padding_size >> 8) as u8;
        output[1] = (padding_size & 0xff) as u8;

        for i in 0..payload_len {
            output[i + 2 + padding_size] = payload[i];
        }
    }

    fn create_info(&self, hkdf_type: &str, public_key: &[u8]) -> Vec<u8> {
        let mut info: Vec<u8> = Vec::with_capacity(hkdf_type.len() + 159);

        info.extend_from_slice("Content-Encoding: ".as_bytes());
        info.extend_from_slice(hkdf_type.as_bytes());
        info.push(0);
        info.extend_from_slice("P-256".as_bytes());
        info.push(0);
        info.push((self.p256dh.len() >> 8) as u8);
        info.push((self.p256dh.len() & 0xff) as u8);
        info.extend_from_slice(self.p256dh);
        info.push((public_key.len() >> 8) as u8);
        info.push((public_key.len() & 0xff) as u8);
        info.extend_from_slice(public_key);

        info
    }
}

pub struct FutureResponse(Box<Future<Item=(), Error=WebPushError> + 'static>);

impl fmt::Debug for FutureResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Future<Response>")
    }
}

impl Future for FutureResponse {
    type Item = ();
    type Error = WebPushError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

pub struct WebPushSender {
    client: Client<HttpsConnector>,
}

/*
#[derive(RustcDecodable, RustcEncodable)]
struct GcmData {
    registration_ids: Vec<String>,
    raw_data: Option<String>,
    time_to_live: Option<u32>,
}

impl GcmData {
    pub fn new(registration_ids: Vec<String>) -> GcmData {
        GcmData {
            registration_ids: registration_ids,
            raw_data: None,
            time_to_live: None,
        }
    }

    pub fn set_raw_data(&mut self, raw_data: Vec<u8>) {
        self.raw_data = Some(raw_data);
    }

    pub fn set_time_to_live(&mut self, ttl: u32) {
        self.time_to_live = Some(ttl);
    }
}
*/

impl WebPushSender {
    pub fn new(handle: &Handle) -> WebPushSender {
        let client = Client::configure()
            .connector(HttpsConnector::new(4, handle))
            .keep_alive(true)
            .build(handle);

        WebPushSender {
            client: client,
        }
    }

    pub fn send(&self, message: WebPushMessage) -> FutureResponse {
        self.call(message)
    }
}

impl Service for WebPushSender {
    type Request = WebPushMessage;
    type Response = ();
    type Error = WebPushError;
    type Future = FutureResponse;

    fn call(&self, message: Self::Request) -> Self::Future {
        let mut request = HttpRequest::new(Post, message.endpoint.parse().unwrap());

        if let Some(payload) = message.payload {
            request.headers_mut().set_raw("content-encoding", "aesgcm");
            request.headers_mut().set(ContentLength(payload.content.len() as u64));
            request.headers_mut().set_raw("encryption", format!("keyid=p256dh;salt={}", payload.salt.to_base64(URL_SAFE)));
            request.headers_mut().set_raw("crypto-key", format!("keyid=p256dh;dh={}", payload.public_key.to_base64(URL_SAFE)));

            request.set_body(payload.content);
        }

        if let Some(ttl) = message.ttl {
            request.headers_mut().set_raw("TTL", format!("{}", ttl));
        }

        let request_f = self.client.request(request).map_err(|_| { WebPushError::Unspecified });

        let push_f = request_f.and_then(move |response: HttpResponse| {
            let retry_after = response.headers().get::<RetryAfter>().map(|ra| *ra);

            match *response.status() {
                status if status.is_success() =>
                    ok(()),
                StatusCode::Unauthorized =>
                    err(WebPushError::Unauthorized),
                StatusCode::BadRequest =>
                    err(WebPushError::BadRequest),
                status if status.is_server_error() =>
                    err(WebPushError::ServerError(retry_after)),
                status => {
                    println!("{:?}", status);
                    err(WebPushError::Unspecified)
                }
            }
        });

        FutureResponse(Box::new(push_f))
    }
}
<commit_msg>Oh god, it works!<commit_after>extern crate hyper;
extern crate futures;
extern crate tokio_core;
extern crate tokio_service;
extern crate hyper_tls;
extern crate rustc_serialize;
extern crate ring;
extern crate crypto;
extern crate untrusted;

use hyper::client::Client;
use hyper_tls::HttpsConnector;
use hyper::client::{Request as HttpRequest, Response as HttpResponse};
use hyper::header::{Authorization, ContentLength, RetryAfter};
use hyper::Post;
use futures::{Future, Poll};
use futures::future::{ok, err};
use futures::stream::Stream;
use ring::{hmac, hkdf, agreement, rand, digest, error, aead};
use std::convert::From;
use crypto::aead::AeadEncryptor;
use std::error::Error;
use std::fmt;
use std::io::{self, Write};
use tokio_core::reactor::Handle;
use tokio_service::Service;
use rustc_serialize::base64::{ToBase64, URL_SAFE};
use rustc_serialize::json;
use hyper::status::StatusCode;
use untrusted::Input;

#[derive(PartialEq, Debug)]
pub enum WebPushError {
    Unspecified,
    Unauthorized,
    BadRequest,
    ServerError(Option<RetryAfter>),
    KeyAgreement,
}

#[derive(Debug)]
pub struct WebPushPayload {
    pub content: Vec<u8>,
    pub public_key: Vec<u8>,
    pub salt: Vec<u8>,
}

impl WebPushPayload {
    pub fn new(content: Vec<u8>, public_key: Vec<u8>, salt: Vec<u8>) -> WebPushPayload {
        WebPushPayload {
            content: content,
            public_key: public_key,
            salt: salt,
        }
    }
}

#[derive(Debug)]
pub struct WebPushMessage {
    pub gcm_key: Option<String>,
    pub endpoint: String,
    pub ttl: Option<u32>,
    pub payload: Option<WebPushPayload>,
}

pub struct WebPushMessageBuilder<'a> {
    gcm_key: Option<&'a str>,
    endpoint: &'a str,
    auth: &'a [u8],
    p256dh: &'a [u8],
    payload: Option<&'a [u8]>,
    ttl: Option<u32>,
}

impl From<error::Unspecified> for WebPushError {
    fn from(e: error::Unspecified) -> WebPushError {
        println!("{:?}", e);
        WebPushError::Unspecified
    }
}

impl Error for WebPushError {
    fn description(&self) -> &str {
        match self {
            _ => "An unknown error",
        }
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for WebPushError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl<'a> WebPushMessageBuilder<'a> {
    pub fn new(endpoint: &'a str, auth: &'a [u8], p256dh: &'a [u8]) -> WebPushMessageBuilder<'a> {
        WebPushMessageBuilder {
            endpoint: endpoint,
            auth: auth,
            p256dh: p256dh,
            ttl: None,
            gcm_key: None,
            payload: None,
        }
    }

    pub fn set_ttl(&mut self, ttl: u32) {
        self.ttl = Some(ttl);
    }

    pub fn set_gcm_key(&mut self, gcm_key: &'a str) {
        self.gcm_key = Some(gcm_key);
    }

    pub fn set_payload(&mut self, payload: &'a [u8]) {
        self.payload = Some(payload);
    }

    pub fn build(self) -> Result<WebPushMessage, WebPushError> {
        if let Some(payload) = self.payload {
            let rng              = rand::SystemRandom::new();
            let private_key      = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng).unwrap();
            let mut public_key   = [0u8; agreement::PUBLIC_KEY_MAX_LEN];
            let public_key       = &mut public_key[..private_key.public_key_len()];
            let mut random_bytes = [0u8; 16];
            let alg              = &agreement::ECDH_P256;
            let auth_input       = Input::from(self.p256dh);

            rng.fill(&mut random_bytes)?;
            private_key.compute_public_key(public_key)?;

            agreement::agree_ephemeral(private_key, alg, auth_input, WebPushError::KeyAgreement, |shared_secret| {
                let salt               = hmac::SigningKey::new(&digest::SHA256, &random_bytes);
                let client_auth_secret = hmac::SigningKey::new(&digest::SHA256, self.auth);

                let mut context: Vec<u8> = Vec::new();
                context.extend_from_slice("P-256\0".as_bytes());
                context.push((self.p256dh.len() >> 8) as u8);
                context.push((self.p256dh.len() & 0xff) as u8);
                context.extend_from_slice(self.p256dh);
                context.push((public_key.len() >> 8) as u8);
                context.push((public_key.len() & 0xff) as u8);
                context.extend_from_slice(public_key);

                let mut prk = [0u8; 32];
                hkdf::extract_and_expand(&client_auth_secret, &shared_secret, "Content-Encoding: auth\0".as_bytes(), &mut prk);

                let mut cek_info: Vec<u8> = Vec::new();
                cek_info.extend_from_slice("Content-Encoding: aesgcm\x00".as_bytes());
                cek_info.extend_from_slice(&context);

                let mut content_encryption_key = [0u8; 16];
                hkdf::extract_and_expand(&salt, &prk, &cek_info, &mut content_encryption_key);

                let mut nonce_info: Vec<u8> = Vec::new();
                nonce_info.extend_from_slice("Content-Encoding: nonce\x00".as_bytes());
                nonce_info.extend_from_slice(&context);

                let mut nonce = [0u8; 12];
                hkdf::extract_and_expand(&salt, &prk, &nonce_info, &mut nonce);

                let mut padded_payload = [0u8; 4080];
                Self::pad(payload, &mut padded_payload);

                let sealing_key = aead::SealingKey::new(&aead::AES_128_GCM, &content_encryption_key)?;
                let mut encrypted_payload = [0u8; 4080];

                aead::seal_in_place(&sealing_key, &nonce, "".as_bytes(), &mut encrypted_payload, 16)?;

                let web_push_payload = WebPushPayload::new(encrypted_payload.to_vec(), public_key.to_vec(), random_bytes.to_vec());

                Ok(WebPushMessage {
                    gcm_key: self.gcm_key.map(|k| k.to_string()),
                    endpoint: self.endpoint.to_string(),
                    ttl: self.ttl,
                    payload: Some(web_push_payload),
                })
            })
        } else {
            Ok(WebPushMessage {
                gcm_key: self.gcm_key.map(|k| k.to_string()),
                endpoint: self.endpoint.to_string(),
                ttl: self.ttl,
                payload: None,
            })
        }
    }

    fn pad(payload: &[u8], output: &mut [u8]) {
        let payload_len = payload.len();
        let max_payload = output.len() - 2;
        let padding_size = max_payload - payload.len();

        output[0] = (padding_size >> 8) as u8;
        output[1] = (padding_size & 0xff) as u8;

        for i in 0..payload_len {
            output[padding_size + i] = payload[i];
        }
    }

    fn create_info(&self, hkdf_type: &str, public_key: &[u8]) -> Vec<u8> {
        let mut info: Vec<u8> = Vec::with_capacity(hkdf_type.len() + 159);

        info.extend_from_slice("Content-Encoding: ".as_bytes());
        info.extend_from_slice(hkdf_type.as_bytes());
        info.push(0);
        info.extend_from_slice("P-256".as_bytes());
        info.push(0);
        info.push((self.p256dh.len() >> 8) as u8);
        info.push((self.p256dh.len() & 0xff) as u8);
        info.extend_from_slice(self.p256dh);
        info.push((public_key.len() >> 8) as u8);
        info.push((public_key.len() & 0xff) as u8);
        info.extend_from_slice(public_key);

        info
    }
}

pub struct FutureResponse(Box<Future<Item=(), Error=WebPushError> + 'static>);

impl fmt::Debug for FutureResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Future<Response>")
    }
}

impl Future for FutureResponse {
    type Item = ();
    type Error = WebPushError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

pub struct WebPushSender {
    client: Client<HttpsConnector>,
}

/*
#[derive(RustcDecodable, RustcEncodable)]
struct GcmData {
    registration_ids: Vec<String>,
    raw_data: Option<String>,
    time_to_live: Option<u32>,
}

impl GcmData {
    pub fn new(registration_ids: Vec<String>) -> GcmData {
        GcmData {
            registration_ids: registration_ids,
            raw_data: None,
            time_to_live: None,
        }
    }

    pub fn set_raw_data(&mut self, raw_data: Vec<u8>) {
        self.raw_data = Some(raw_data);
    }

    pub fn set_time_to_live(&mut self, ttl: u32) {
        self.time_to_live = Some(ttl);
    }
}
*/

impl WebPushSender {
    pub fn new(handle: &Handle) -> WebPushSender {
        let client = Client::configure()
            .connector(HttpsConnector::new(4, handle))
            .keep_alive(true)
            .build(handle);

        WebPushSender {
            client: client,
        }
    }

    pub fn send(&self, message: WebPushMessage) -> FutureResponse {
        self.call(message)
    }
}

impl Service for WebPushSender {
    type Request = WebPushMessage;
    type Response = ();
    type Error = WebPushError;
    type Future = FutureResponse;

    fn call(&self, message: Self::Request) -> Self::Future {
        let mut request = HttpRequest::new(Post, message.endpoint.parse().unwrap());

        if let Some(payload) = message.payload {
            request.headers_mut().set_raw("Content-Encoding", "aesgcm");
            request.headers_mut().set_raw("Crypto-Key", format!("keyid=p256dh;dh={}", payload.public_key.to_base64(URL_SAFE)));
            request.headers_mut().set_raw("Encryption", format!("keyid=p256dh;salt={}", payload.salt.to_base64(URL_SAFE)));
            request.headers_mut().set(ContentLength(payload.content.len() as u64));
            request.set_body(payload.content);
        }

        if let Some(ttl) = message.ttl {
            request.headers_mut().set_raw("TTL", format!("{}", ttl));
        }

        println!("{:?}", request);

        let request_f = self.client.request(request).map_err(|_| { WebPushError::Unspecified });

        let push_f = request_f.and_then(move |response: HttpResponse| {
            let retry_after = response.headers().get::<RetryAfter>().map(|ra| *ra);

            match *response.status() {
                status if status.is_success() =>
                    ok(()),
                StatusCode::Unauthorized =>
                    err(WebPushError::Unauthorized),
                StatusCode::BadRequest => {
                    let printer = response.body().for_each(|chunk| {
                        io::stdout().write_all(&chunk).map_err(From::from)
                    });

                    let mut core = tokio_core::reactor::Core::new().unwrap();
                    core.run(printer).unwrap();
                    err(WebPushError::BadRequest)
                },
                status if status.is_server_error() => {
                    let printer = response.body().for_each(|chunk| {
                        io::stdout().write_all(&chunk).map_err(From::from)
                    });

                    let mut core = tokio_core::reactor::Core::new().unwrap();
                    core.run(printer).unwrap();
                    err(WebPushError::ServerError(retry_after))
                },
                status => {
                    println!("{:?}", status);
                    err(WebPushError::Unspecified)
                }
            }
        });

        FutureResponse(Box::new(push_f))
    }
}
<|endoftext|>