<commit_before>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are
//! defined in this crate because they cannot be used in [syn] without "full"
//! feature.
//!
//! # Optional features
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: ItemFn
//! [`syn_mid::Block`]: Block
//! [syn]: https://github.com/dtolnay/syn

#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod func;
mod pat;
mod path;

pub use crate::{
    func::{Block, FnArg, ItemFn, Receiver, Signature},
    pat::{
        FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct,
        PatType, PatWild,
    },
};
<commit_msg>Allow unused_variables lint in doctests<commit_after>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are
//! defined in this crate because they cannot be used in [syn] without "full"
//! feature.
//!
//! # Optional features
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: ItemFn
//! [`syn_mid::Block`]: Block
//! [syn]: https://github.com/dtolnay/syn

#![doc(test(
    no_crate_inject,
    attr(
        deny(warnings, rust_2018_idioms, single_use_lifetimes),
        allow(dead_code, unused_variables)
    )
))]
#![forbid(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod func;
mod pat;
mod path;

pub use crate::{
    func::{Block, FnArg, ItemFn, Receiver, Signature},
    pat::{
        FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct,
        PatType, PatWild,
    },
};
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use libc::{c_int, c_char};
use std::ffi::{CString, CStr};
use std::borrow::{Cow};
use std::ptr;
use std::fs::File;
use std::path::Path;
use std::io;
use std::io::prelude::*;
use std::process::{Command, Stdio};
use std::os::unix::io::{AsRawFd, FromRawFd};
use openssl::sign::Verifier;
use openssl::pkey::PKey;
use openssl::hash::MessageDigest;
use bindings::*;
use toml::Value;
use uuid::Uuid;

extern crate libc;
extern crate openssl;
extern crate toml;
extern crate uuid;

pub mod bindings;

fn get_user(pamh: *mut pam_handle_t, prompt: Option<&str>) -> Result<Cow<str>, i32> {
    let mut c_user: *const c_char = ptr::null();
    let c_prompt = match prompt {
        Some(prompt_str) => CString::new(prompt_str).unwrap().as_ptr(),
        None => ptr::null(),
    };
    let err;
    unsafe {
        err = pam_get_user(pamh, &mut c_user, c_prompt);
    }
    match err {
        PAM_SUCCESS => unsafe {
            let c_user_str = CStr::from_ptr(c_user);
            Ok(c_user_str.to_string_lossy())
        },
        err => Err(err),
    }
}

#[derive(Debug)]
enum ConfigError {
    Io(io::Error),
    Toml(toml::de::Error),
    MissingField(String),
    InvalidValueType(String),
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError {
        ConfigError::Io(err)
    }
}

impl From<toml::de::Error> for ConfigError {
    fn from(err: toml::de::Error) -> ConfigError {
        ConfigError::Toml(err)
    }
}

fn get_authenticator_path() -> Result<String, ConfigError> {
    let mut config_file = File::open("/etc/pam_wsl_hello/config")?;
    let mut config = String::new();
    config_file.read_to_string(&mut config)?;

    let config_value = config.parse::<Value>()?;
    let authenticator_path = config_value.get("authenticator_path")
        .ok_or(ConfigError::MissingField("authenticator_path".to_owned()))?
        .as_str()
        .ok_or(ConfigError::InvalidValueType("authenticator_path".to_owned()))?;
    Ok(authenticator_path.to_owned())
}

fn sm_authenticate(
    pamh: *mut pam_handle_t,
    flags: c_int,
    argc: c_int,
    argv: *mut *const c_char,
) -> Result<i32, i32> {

    let user_name = get_user(pamh, None)?;
    let credential_key_name = format!("pam_wsl_hello_{}", user_name);

    let mut hello_public_key_file = File::open(format!("/etc/pam_wsl_hello/public_keys/{}.pem", credential_key_name)).map_err(|_| PAM_AUTH_ERR)?;
    let mut key_str = String::new();
    hello_public_key_file.read_to_string(&mut key_str).map_err(|_| PAM_AUTH_ERR)?;
    let hello_public_key = PKey::public_key_from_pem(key_str.as_bytes()).map_err(|_| PAM_AUTH_ERR)?;

    let challenge = format!("pam_wsl_hello:{}:{}", user_name, Uuid::new_v4());

    // Since there seems to be a bug that C# applications cannot read from pipes on WSL,
    // we create a temporary file to redirect
    let challenge_tmpfile_path = &format!("/tmp/{}", challenge);
    {
        let mut challenge_tmpfile = File::create(challenge_tmpfile_path).map_err(|_| PAM_AUTH_ERR)?;
        challenge_tmpfile.write_all(challenge.as_bytes()).map_err(|_| PAM_AUTH_ERR)?;
    }
    let challenge_tmpfile = File::open(challenge_tmpfile_path).map_err(|_| PAM_AUTH_ERR)?;
    let challenge_tmpfile_in = unsafe {Stdio::from_raw_fd(challenge_tmpfile.as_raw_fd())};

    let authenticator_path = get_authenticator_path().map_err(|_| PAM_AUTH_ERR)?;
    let authenticator = Command::new(&authenticator_path)
        .arg(credential_key_name)
        .current_dir("/mnt/c")
        .stdin(challenge_tmpfile_in)
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|_| PAM_AUTH_ERR)?;

    let output = authenticator
        .wait_with_output()
        .map_err(|_| PAM_AUTH_ERR)?;
    match output.status.code() {
        Some(code) if code == 0 => {/* Success */},
        _ => {
            return Err(PAM_AUTH_ERR);
        }
    }
    let signature = output.stdout;

    let mut verifier = Verifier::new(MessageDigest::sha256(), &hello_public_key).unwrap();
    verifier.update(challenge.as_bytes()).map_err(|_| PAM_AUTH_ERR);

    match verifier.finish(&signature).map_err(|_| PAM_AUTH_ERR)? {
        true => Ok(PAM_SUCCESS),
        false => Err(PAM_AUTH_ERR),
    }
}

#[no_mangle]
pub fn pam_sm_authenticate(
    pamh: *mut pam_handle_t,
    flags: c_int,
    argc: c_int,
    argv: *mut *const c_char,
) -> c_int {
    match sm_authenticate(pamh, flags, argc, argv) {
        Ok(ok) => ok,
        Err(err) => err,
    }
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_setcred(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_acct_mgmt(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_open_session(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_close_session(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_chauthtok(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}
<commit_msg>Refactoring: Improve error handling<commit_after>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use libc::{c_int, c_char};
use std::ffi::{CString, CStr};
use std::borrow::{Cow};
use std::ptr;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::process::{Command, Stdio};
use std::os::unix::io::{AsRawFd, FromRawFd};
use openssl::sign::Verifier;
use openssl::pkey::PKey;
use openssl::hash::MessageDigest;
use bindings::*;
use toml::Value;
use uuid::Uuid;

extern crate libc;
extern crate openssl;
extern crate toml;
extern crate uuid;

pub mod bindings;

fn get_user(pamh: *mut pam_handle_t, prompt: Option<&str>) -> Result<Cow<str>, i32> {
    let mut c_user: *const c_char = ptr::null();
    let c_prompt = match prompt {
        Some(prompt_str) => CString::new(prompt_str).unwrap().as_ptr(),
        None => ptr::null(),
    };
    let err;
    unsafe {
        err = pam_get_user(pamh, &mut c_user, c_prompt);
    }
    match err {
        PAM_SUCCESS => unsafe {
            let c_user_str = CStr::from_ptr(c_user);
            Ok(c_user_str.to_string_lossy())
        },
        err => Err(err),
    }
}

#[derive(Debug)]
enum ConfigError {
    Io(io::Error),
    Toml(toml::de::Error),
    MissingField(String),
    InvalidValueType(String),
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError {
        ConfigError::Io(err)
    }
}

impl From<toml::de::Error> for ConfigError {
    fn from(err: toml::de::Error) -> ConfigError {
        ConfigError::Toml(err)
    }
}

fn get_authenticator_path() -> Result<String, ConfigError> {
    let mut config_file = File::open("/etc/pam_wsl_hello/config")?;
    let mut config = String::new();
    config_file.read_to_string(&mut config)?;

    let config_value = config.parse::<Value>()?;
    let authenticator_path = config_value.get("authenticator_path")
        .ok_or(ConfigError::MissingField("authenticator_path".to_owned()))?
        .as_str()
        .ok_or(ConfigError::InvalidValueType("authenticator_path".to_owned()))?;
    Ok(authenticator_path.to_owned())
}

#[derive(Debug)]
enum HelloAuthenticationError {
    GetUserError(i32),
    ConfigError(ConfigError),
    PublicKeyFileError(io::Error),
    Io(io::Error),
    InvalidPublicKey(openssl::error::ErrorStack),
    OpenSSLError(openssl::error::ErrorStack),
    AuthenticatorLaunchError(io::Error),
    AuthenticatorConnectionError(io::Error),
    AuthenticationFail(i32),
}

impl From<io::Error> for HelloAuthenticationError {
    fn from(err: io::Error) -> HelloAuthenticationError {
        HelloAuthenticationError::Io(err)
    }
}

impl From<ConfigError> for HelloAuthenticationError {
    fn from(err: ConfigError) -> HelloAuthenticationError {
        HelloAuthenticationError::ConfigError(err)
    }
}

fn authenticate_via_hello(pamh: *mut pam_handle_t) -> Result<i32, HelloAuthenticationError> {

    let user_name = get_user(pamh, None).map_err(|e| HelloAuthenticationError::GetUserError(e))?;
    let credential_key_name = format!("pam_wsl_hello_{}", user_name);

    let mut hello_public_key_file = File::open(format!("/etc/pam_wsl_hello/public_keys/{}.pem", credential_key_name)).map_err(|io| HelloAuthenticationError::PublicKeyFileError(io))?;
    let mut key_str = String::new();
    hello_public_key_file.read_to_string(&mut key_str)?;
    let hello_public_key = PKey::public_key_from_pem(key_str.as_bytes()).map_err(|e| HelloAuthenticationError::InvalidPublicKey(e))?;

    let challenge = format!("pam_wsl_hello:{}:{}", user_name, Uuid::new_v4());

    // Since there seems to be a bug that C# applications cannot read from pipes on WSL,
    // we create a temporary file to redirect
    let challenge_tmpfile_path = &format!("/tmp/{}", challenge);
    {
        let mut challenge_tmpfile = File::create(challenge_tmpfile_path)?;
        challenge_tmpfile.write_all(challenge.as_bytes())?;
    }
    let challenge_tmpfile = File::open(challenge_tmpfile_path)?;
    let challenge_tmpfile_in = unsafe {Stdio::from_raw_fd(challenge_tmpfile.as_raw_fd())};

    let authenticator_path = get_authenticator_path()?;
    let authenticator = Command::new(&authenticator_path)
        .arg(credential_key_name)
        .current_dir("/mnt/c")
        .stdin(challenge_tmpfile_in)
        .stdout(Stdio::piped())
        .spawn()
        .map_err(|e| HelloAuthenticationError::AuthenticatorLaunchError(e))?;

    let output = authenticator
        .wait_with_output()
        .map_err(|e| HelloAuthenticationError::AuthenticatorConnectionError(e))?;
    match output.status.code() {
        Some(code) if code == 0 => {/* Success */},
        Some(code) => return Err(HelloAuthenticationError::AuthenticationFail(code)),
        None => return Err(HelloAuthenticationError::AuthenticationFail(1)),
    }
    let signature = output.stdout;

    let mut verifier = Verifier::new(MessageDigest::sha256(), &hello_public_key).unwrap();
    verifier.update(challenge.as_bytes()).map_err(|e| HelloAuthenticationError::OpenSSLError(e))?;

    match verifier.finish(&signature).map_err(|e| HelloAuthenticationError::OpenSSLError(e))? {
        true => Ok(PAM_SUCCESS),
        false => Err(HelloAuthenticationError::AuthenticationFail(1)),
    }
}

#[no_mangle]
pub fn pam_sm_authenticate(
    pamh: *mut pam_handle_t,
    flags: c_int,
    _: c_int,
    _: *mut *const c_char,
) -> c_int {
    match authenticate_via_hello(pamh) {
        Ok(ok) => ok,
        Err(_) => 0,
    }
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_setcred(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_acct_mgmt(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_open_session(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_close_session(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}

#[no_mangle]
#[allow(unused_variables)]
pub fn pam_sm_chauthtok(
    pamh: *mut pam_handle_t,
    flags: ::std::os::raw::c_int,
    argc: ::std::os::raw::c_int,
    argv: *mut *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
    PAM_IGNORE
}
<|endoftext|>


<commit_before>//! ISO8601 is a parser library for the
//! [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) format
//! and partially RFC3339.
//!
//! Validity of a given date is not guaranteed, this parser will happily parse
//! 2015.02.29 as a valid date,
//! even though 2015 was no leap year.

#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

#[macro_use]
extern crate nom;
use nom::IResult::*;

#[macro_use]
mod helper;
pub mod parsers;

/// A date, can hold three different formats.
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub enum Date {
    /// consists of year, month and day of month
    YMD {
        year: i32,
        month: u32,
        day: u32,
    },
    /// consists of year, week and day of week
    Week {
        year: i32,
        ww: u32,
        d: u32,
    },
    /// consists of year and day of year
    Ordinal {
        year: i32,
        ddd: u32,
    },
}

/// A time object
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub struct Time {
    /// a 24th of a day
    pub hour: u32,
    /// 60 discrete parts of an hour
    pub minute: u32,
    /// a minute are 60 of these
    pub second: u32,
    /// everything after a `.`
    pub millisecond: u32,
    /// depends on where you're at
    pub tz_offset_hours: i32,
    pub tz_offset_minutes: i32,
}

/// Compound struct, hold Date and Time
///
/// duh!
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub struct DateTime {
    pub date: Date,
    pub time: Time,
}

impl Time {
    pub fn set_tz(&self, tzo: (i32, i32)) -> Time {
        let mut t = self.clone();
        t.tz_offset_hours = tzo.0;
        t.tz_offset_hours = tzo.1;
        t
    }
}

/// Parses a date string.
///
/// A string can have one of the following formats:
///
/// * `2015-11-02` or `20151102`
/// * `2015-W45-01` or `2015W451`
/// * `2015-306` or `2015306`
///
/// ## Example
///
/// ```rust
/// let date = iso8601::date("2015-11-02").unwrap();
/// ```
pub fn date(string: &str) -> Result<Date, String> {
    if let Done(_, parsed) = parsers::parse_date(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}

/// Parses a time string.
///
/// A string can have one of the following formats:
///
/// * `07:35:[00][.123]` or `0735[00][.123]`
/// * `07:35:[00][.123][(Z|(+|-)00:00)]`
/// * `0735[00][.123][(Z|(+|-)00:00)]`
/// * `0735[00][.123][(Z|(+|-)0000)]`
///
/// ## Example
///
/// ```rust
/// let time = iso8601::time("21:56:42").unwrap();
/// ```
pub fn time(string: &str) -> Result<Time, String> {
    if let Done(_, parsed) = parsers::parse_time(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}

/// This parses a datetime string.
///
/// A datetime string is a combination of the valid formats for the date and time.
/// separated by a literal `T`.
/// See the respective functions for the correct format.
///
/// ## Example
///
/// ```rust
/// let dt = iso8601::datetime("2015-11-03T21:56").unwrap();
/// ```
pub fn datetime(string: &str) -> Result<DateTime, String> {
    if let Done(_left_overs, parsed) = parsers::parse_datetime(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}
<commit_msg>Add small example<commit_after>//! ISO8601 is a parser library for the
//! [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) format
//! and partially RFC3339.
//!
//! Validity of a given date is not guaranteed, this parser will happily parse
//! 2015.02.29 as a valid date,
//! even though 2015 was no leap year.
//!
//! # Example
//!
//! ```rust
//! let datetime = iso8601::datetime("2015-06-26T16:43:23+0200").unwrap();
//! ```

#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

#[macro_use]
extern crate nom;
use nom::IResult::*;

#[macro_use]
mod helper;
pub mod parsers;

/// A date, can hold three different formats.
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub enum Date {
    /// consists of year, month and day of month
    YMD {
        year: i32,
        month: u32,
        day: u32,
    },
    /// consists of year, week and day of week
    Week {
        year: i32,
        ww: u32,
        d: u32,
    },
    /// consists of year and day of year
    Ordinal {
        year: i32,
        ddd: u32,
    },
}

/// A time object
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub struct Time {
    /// a 24th of a day
    pub hour: u32,
    /// 60 discrete parts of an hour
    pub minute: u32,
    /// a minute are 60 of these
    pub second: u32,
    /// everything after a `.`
    pub millisecond: u32,
    /// depends on where you're at
    pub tz_offset_hours: i32,
    pub tz_offset_minutes: i32,
}

/// Compound struct, hold Date and Time
///
/// duh!
#[derive(Eq,PartialEq,Debug,Copy,Clone)]
pub struct DateTime {
    pub date: Date,
    pub time: Time,
}

impl Time {
    pub fn set_tz(&self, tzo: (i32, i32)) -> Time {
        let mut t = self.clone();
        t.tz_offset_hours = tzo.0;
        t.tz_offset_hours = tzo.1;
        t
    }
}

/// Parses a date string.
///
/// A string can have one of the following formats:
///
/// * `2015-11-02` or `20151102`
/// * `2015-W45-01` or `2015W451`
/// * `2015-306` or `2015306`
///
/// ## Example
///
/// ```rust
/// let date = iso8601::date("2015-11-02").unwrap();
/// ```
pub fn date(string: &str) -> Result<Date, String> {
    if let Done(_, parsed) = parsers::parse_date(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}

/// Parses a time string.
///
/// A string can have one of the following formats:
///
/// * `07:35:[00][.123]` or `0735[00][.123]`
/// * `07:35:[00][.123][(Z|(+|-)00:00)]`
/// * `0735[00][.123][(Z|(+|-)00:00)]`
/// * `0735[00][.123][(Z|(+|-)0000)]`
///
/// ## Example
///
/// ```rust
/// let time = iso8601::time("21:56:42").unwrap();
/// ```
pub fn time(string: &str) -> Result<Time, String> {
    if let Done(_, parsed) = parsers::parse_time(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}

/// This parses a datetime string.
///
/// A datetime string is a combination of the valid formats for the date and time.
/// separated by a literal `T`.
/// See the respective functions for the correct format.
///
/// ## Example
///
/// ```rust
/// let dt = iso8601::datetime("2015-11-03T21:56").unwrap();
/// ```
pub fn datetime(string: &str) -> Result<DateTime, String> {
    if let Done(_left_overs, parsed) = parsers::parse_datetime(string.as_bytes()) {
        Ok(parsed)
    } else {
        Err(format!("Parser Error: {}", string))
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The GeoRust Developers
//
// 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.

#![allow(unstable)]

use std::ascii::AsciiExt;
use std::default::Default;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::GeometryCollection;
use types::LineString;
use types::Point;
use types::Polygon;
use types::MultiPoint;
use types::MultiLineString;
use types::MultiPolygon;

mod tokenizer;
mod towkt;
pub mod types;

pub use towkt::ToWkt;

pub enum Geometry {
    Point(Point),
    LineString(LineString),
    Polygon(Polygon),
    MultiPoint(MultiPoint),
    MultiLineString(MultiLineString),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl Geometry {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens)-> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x = <Point as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "LINESTRING" => {
                let x = <LineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "POLYGON" => {
                let x = <Polygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTIPOINT" => {
                let x = <MultiPoint as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTILINESTRING" => {
                let x = <MultiLineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTIPOLYGON" => {
                let x = <MultiPolygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "GEOMETRYCOLLECTION" => {
                let x = <GeometryCollection as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            _ => Err("Invalid type encountered"),
        }
    }
}
pub struct Wkt {
    pub items: Vec<Geometry>
}

impl Wkt {
    fn new() -> Self {
        Wkt {items: vec![]}
    }

    fn add_item(&mut self, item: Geometry) {
        self.items.push(item);
    }

    fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.to_ascii_uppercase()
            },
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match Geometry::from_word_and_tokens(word.as_slice(), &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

trait FromTokens: Sized+Default {
    fn from_tokens(tokens: &mut PeekableTokens) -> Result<Self, &'static str>;

    fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match tokens.next() {
            Some(Token::ParenOpen) => (),
            Some(Token::Word(ref s)) if s.to_ascii_uppercase() == "EMPTY" =>
                return Ok(Default::default()),
            _ => return Err("Missing open parenthesis for type"),
        };
        let result = FromTokens::from_tokens(tokens);
        match tokens.next() {
            Some(Token::ParenClose) => (),
            _ => return Err("Missing closing parenthesis for type"),
        };
        result
    }

    fn comma_many<F>(f: F, tokens: &mut PeekableTokens) -> Result<Vec<Self>, &'static str>
            where F: Fn(&mut PeekableTokens) -> Result<Self, &'static str> {
        let mut items = Vec::new();

        let item = try!(f(tokens));
        items.push(item);

        while let Some(&Token::Comma) = tokens.peek() {
            tokens.next();  // throw away comma

            let item = try!(f(tokens));
            items.push(item);
        }

        Ok(items)
    }
}

#[cfg(test)]
mod tests {
    use {Wkt, Geometry};
    use types::{MultiPolygon, Point};

    #[test]
    fn empty_string() {
        let wkt = Wkt::from_str("").ok().unwrap();
        assert_eq!(0, wkt.items.len());
    }

    #[test]
    fn empty_items() {
        let mut wkt = Wkt::from_str("POINT EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::Point(Point(None)) => (),
            _ => unreachable!(),
        };

        let mut wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::MultiPolygon(MultiPolygon(polygons)) =>
                assert_eq!(polygons.len(), 0),
            _ => unreachable!(),
        };
    }
}
<commit_msg>Utilize recent Deref changes<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// 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.

#![allow(unstable)]

use std::ascii::AsciiExt;
use std::default::Default;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::GeometryCollection;
use types::LineString;
use types::Point;
use types::Polygon;
use types::MultiPoint;
use types::MultiLineString;
use types::MultiPolygon;

mod tokenizer;
mod towkt;
pub mod types;

pub use towkt::ToWkt;

pub enum Geometry {
    Point(Point),
    LineString(LineString),
    Polygon(Polygon),
    MultiPoint(MultiPoint),
    MultiLineString(MultiLineString),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl Geometry {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens)-> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x = <Point as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "LINESTRING" => {
                let x = <LineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "POLYGON" => {
                let x = <Polygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTIPOINT" => {
                let x = <MultiPoint as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTILINESTRING" => {
                let x = <MultiLineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "MULTIPOLYGON" => {
                let x = <MultiPolygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "GEOMETRYCOLLECTION" => {
                let x = <GeometryCollection as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            _ => Err("Invalid type encountered"),
        }
    }
}
pub struct Wkt {
    pub items: Vec<Geometry>
}

impl Wkt {
    fn new() -> Self {
        Wkt {items: vec![]}
    }

    fn add_item(&mut self, item: Geometry) {
        self.items.push(item);
    }

    fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.to_ascii_uppercase()
            },
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match Geometry::from_word_and_tokens(&word, &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

trait FromTokens: Sized+Default {
    fn from_tokens(tokens: &mut PeekableTokens) -> Result<Self, &'static str>;

    fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match tokens.next() {
            Some(Token::ParenOpen) => (),
            Some(Token::Word(ref s)) if s.to_ascii_uppercase() == "EMPTY" =>
                return Ok(Default::default()),
            _ => return Err("Missing open parenthesis for type"),
        };
        let result = FromTokens::from_tokens(tokens);
        match tokens.next() {
            Some(Token::ParenClose) => (),
            _ => return Err("Missing closing parenthesis for type"),
        };
        result
    }

    fn comma_many<F>(f: F, tokens: &mut PeekableTokens) -> Result<Vec<Self>, &'static str>
            where F: Fn(&mut PeekableTokens) -> Result<Self, &'static str> {
        let mut items = Vec::new();

        let item = try!(f(tokens));
        items.push(item);

        while let Some(&Token::Comma) = tokens.peek() {
            tokens.next();  // throw away comma

            let item = try!(f(tokens));
            items.push(item);
        }

        Ok(items)
    }
}

#[cfg(test)]
mod tests {
    use {Wkt, Geometry};
    use types::{MultiPolygon, Point};

    #[test]
    fn empty_string() {
        let wkt = Wkt::from_str("").ok().unwrap();
        assert_eq!(0, wkt.items.len());
    }

    #[test]
    fn empty_items() {
        let mut wkt = Wkt::from_str("POINT EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::Point(Point(None)) => (),
            _ => unreachable!(),
        };

        let mut wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::MultiPolygon(MultiPolygon(polygons)) =>
                assert_eq!(polygons.len(), 0),
            _ => unreachable!(),
        };
    }
}
<|endoftext|>


<commit_before>
#[cfg(test)]
#[macro_use]
extern crate lazy_static;

use std::ptr;
use std::mem;
use std::cmp::{Ord, Ordering};

pub trait MapInPlace<A, B>: Sized {
    /// Should be of the same base type as the implementor.
    /// E.g. `Vec<B>` when implementing for `Vec<A>`
    type Output;

    /// Apply a mapping function to `self` without allocating.
    /// Makes best effort to maintain the invariant
    ///
    /// `self.as_ptr() as *const () == self.map_in_place(..).as_ptr() as *const ()`
    ///
    /// An example of a case where this isn't possible is for Vec where B is zero-sized but A is not.
    fn map_in_place<F>(self, f: F) -> Self::Output where F: FnMut(A) -> B;
}

unsafe fn map_in_place<A, B, F>(ptr_a: *const A, ptr_b: *mut B, len: usize, mut f: F)
    where F: FnMut(A) -> B
{
    for i in 0..len {
        let ptr_a = ptr_a.offset(i as isize);
        let ptr_b = ptr_b.offset(i as isize);

        ptr::write(ptr_b, f(ptr::read(ptr_a)));
    }
}

impl<A, B> MapInPlace<A, B> for Vec<A> {
    type Output = Vec<B>;

    #[inline]
    fn map_in_place<F>(mut self, mut f: F) -> Self::Output
        where F: FnMut(A) -> B
    {
        let a_size = mem::size_of::<A>();
        let b_size = mem::size_of::<B>();

        match a_size.cmp(&b_size) {
            Ordering::Equal => {
                self.as_mut_slice().map_in_place(f);

                unsafe { mem::transmute(self) }
            }
            Ordering::Greater => {
                if b_size == 0 {
                    // doesn't preserve address invariant
                    let mut v = Vec::with_capacity(0);

                    for e in self.into_iter() {
                        v.push(f(e));
                    }

                    v
                } else {
                    // nA * bytes/A = nbytes
                    // nbytes / bytes/B = nbytes * B/bytes = nB
                    // (assuming bytes/B divides evenly into nbytes)
                    let cap = {
                        let tmp = self.capacity().checked_mul(a_size).unwrap();
                        // TODO: don't require the divisibility constraint
                        assert_eq!(tmp % b_size, 0);
                        tmp / b_size
                    };
                    let ptr_a = self.as_ptr();
                    let ptr_b = ptr_a as *mut B;
                    let len = self.len();

                    unsafe {
                        map_in_place(ptr_a, ptr_b, len, f);
                        mem::forget(self);
                        Vec::from_raw_parts(ptr_b, len, cap)
                    }
                }
            }
            Ordering::Less => {
                panic!("map_in_place(Vec<A>): Size of A must be greater than or equal to size of B")
            }
        }
    }
}

impl<'a, A, B: 'a> MapInPlace<A, B> for &'a mut [A] {
    type Output = &'a mut [B];

    #[inline]
    fn map_in_place<F>(self, f: F) -> Self::Output
        where F: FnMut(A) -> B
    {
        let a_size = mem::size_of::<A>();
        let b_size = mem::size_of::<B>();

        match a_size.cmp(&b_size) {
            Ordering::Equal => {
                let ptr_a = self.as_ptr();
                let ptr_b = ptr_a as *mut B;

                unsafe {
                    map_in_place(ptr_a, ptr_b, self.len(), f);
                    mem::transmute(self)
                }
            }
            _ => panic!("map_in_place(&mut [A]): Size of A must be equal to size of B"),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::MapInPlace;

    use std::mem;
    use std::sync::Mutex;

    #[test]
    fn vec_elements_drop() {
        lazy_static! {
            static ref DROPS: Mutex<Vec<String>> = Mutex::new(vec![]);
        }

        #[derive(Debug, PartialEq, Clone)]
        struct X(usize);

        impl Drop for X {
            fn drop(&mut self) {
                DROPS.lock().unwrap().push(format!("X({})", self.0));
            }
        }

        #[derive(Debug, PartialEq, Clone)]
        struct Y(usize);

        impl Drop for Y {
            fn drop(&mut self) {
                DROPS.lock().unwrap().push(format!("Y({})", self.0));
            }
        }

        assert_eq!(mem::size_of::<X>(), mem::size_of::<Y>()); // will use slice impl

        let v = vec![X(0), X(1), X(2), X(3)];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|X(v)| Y(v));

        {
            let drops = DROPS.lock().unwrap().clone();
            assert_eq!(drops, vec!["X(0)", "X(1)", "X(2)", "X(3)"]);
        }

        let ap = v.as_ptr() as *const ();
        let expected = vec![Y(0), Y(1), Y(2), Y(3)];

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, expected);

        mem::drop(v);

        {
            let drops = DROPS.lock().unwrap().clone();
            assert_eq!(drops,
                       vec!["X(0)", "X(1)", "X(2)", "X(3)", "Y(0)", "Y(1)", "Y(2)", "Y(3)"]);
        }

        mem::drop(expected);
    }

    #[test]
    fn same_size_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|x: u32| (x * x) as i32);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, vec![0, 1, 4, 9]);
    }

    #[test]
    fn different_sizes_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|x: u32| (x * x) as i16);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, vec![0, 1, 4, 9]);
    }

    #[test]
    fn both_zst_vec() {
        #[derive(Debug)]
        struct Zst;

        let v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_| Zst);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    #[test]
    fn nzst_to_zst_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_: u32| ());
        let ap = v.as_ptr() as *const ();

        assert!(bp != ap); // -- NOT -- still at same memory addr
    }

    #[test]
    #[should_panic]
    fn zst_to_nzst_vec() {
        let v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_| 0usize);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    // //////

    #[test]
    fn same_size_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|x: u32| (x * x) as i32);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, &*vec![0, 1, 4, 9]);
    }

    #[test]
    #[should_panic]
    fn different_sizes_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|x: u32| (x * x) as i16);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, &*vec![0, 1, 4, 9]);
    }

    #[test]
    fn both_zst_slice() {
        #[derive(Debug)]
        struct Zst;

        let mut v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_| Zst);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    #[test]
    #[should_panic]
    fn nzst_to_zst_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_: u32| ());
        let ap = v.as_ptr() as *const ();

        assert!(bp != ap); // -- NOT -- still at same memory addr
    }

    #[test]
    #[should_panic]
    fn zst_to_nzst_slice() {
        let mut v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_| 0usize);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }
}
<commit_msg>Missing inline attribute<commit_after>
#[cfg(test)]
#[macro_use]
extern crate lazy_static;

use std::ptr;
use std::mem;
use std::cmp::{Ord, Ordering};

pub trait MapInPlace<A, B>: Sized {
    /// Should be of the same base type as the implementor.
    /// E.g. `Vec<B>` when implementing for `Vec<A>`
    type Output;

    /// Apply a mapping function to `self` without allocating.
    /// Makes best effort to maintain the invariant
    ///
    /// `self.as_ptr() as *const () == self.map_in_place(..).as_ptr() as *const ()`
    ///
    /// An example of a case where this isn't possible is for Vec where B is zero-sized but A is not.
    fn map_in_place<F>(self, f: F) -> Self::Output where F: FnMut(A) -> B;
}

#[inline]
unsafe fn map_in_place<A, B, F>(ptr_a: *const A, ptr_b: *mut B, len: usize, mut f: F)
    where F: FnMut(A) -> B
{
    for i in 0..len {
        let ptr_a = ptr_a.offset(i as isize);
        let ptr_b = ptr_b.offset(i as isize);

        ptr::write(ptr_b, f(ptr::read(ptr_a)));
    }
}

impl<A, B> MapInPlace<A, B> for Vec<A> {
    type Output = Vec<B>;

    #[inline]
    fn map_in_place<F>(mut self, mut f: F) -> Self::Output
        where F: FnMut(A) -> B
    {
        let a_size = mem::size_of::<A>();
        let b_size = mem::size_of::<B>();

        match a_size.cmp(&b_size) {
            Ordering::Equal => {
                self.as_mut_slice().map_in_place(f);

                unsafe { mem::transmute(self) }
            }
            Ordering::Greater => {
                if b_size == 0 {
                    // doesn't preserve address invariant
                    let mut v = Vec::with_capacity(0);

                    for e in self.into_iter() {
                        v.push(f(e));
                    }

                    v
                } else {
                    // nA * bytes/A = nbytes
                    // nbytes / bytes/B = nbytes * B/bytes = nB
                    // (assuming bytes/B divides evenly into nbytes)
                    let cap = {
                        let tmp = self.capacity().checked_mul(a_size).unwrap();
                        // TODO: don't require the divisibility constraint
                        assert_eq!(tmp % b_size, 0);
                        tmp / b_size
                    };
                    let ptr_a = self.as_ptr();
                    let ptr_b = ptr_a as *mut B;
                    let len = self.len();

                    unsafe {
                        map_in_place(ptr_a, ptr_b, len, f);
                        mem::forget(self);
                        Vec::from_raw_parts(ptr_b, len, cap)
                    }
                }
            }
            Ordering::Less => {
                panic!("map_in_place(Vec<A>): Size of A must be greater than or equal to size of B")
            }
        }
    }
}

impl<'a, A, B: 'a> MapInPlace<A, B> for &'a mut [A] {
    type Output = &'a mut [B];

    #[inline]
    fn map_in_place<F>(self, f: F) -> Self::Output
        where F: FnMut(A) -> B
    {
        let a_size = mem::size_of::<A>();
        let b_size = mem::size_of::<B>();

        match a_size.cmp(&b_size) {
            Ordering::Equal => {
                let ptr_a = self.as_ptr();
                let ptr_b = ptr_a as *mut B;

                unsafe {
                    map_in_place(ptr_a, ptr_b, self.len(), f);
                    mem::transmute(self)
                }
            }
            _ => panic!("map_in_place(&mut [A]): Size of A must be equal to size of B"),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::MapInPlace;

    use std::mem;
    use std::sync::Mutex;

    #[test]
    fn vec_elements_drop() {
        lazy_static! {
            static ref DROPS: Mutex<Vec<String>> = Mutex::new(vec![]);
        }

        #[derive(Debug, PartialEq, Clone)]
        struct X(usize);

        impl Drop for X {
            fn drop(&mut self) {
                DROPS.lock().unwrap().push(format!("X({})", self.0));
            }
        }

        #[derive(Debug, PartialEq, Clone)]
        struct Y(usize);

        impl Drop for Y {
            fn drop(&mut self) {
                DROPS.lock().unwrap().push(format!("Y({})", self.0));
            }
        }

        assert_eq!(mem::size_of::<X>(), mem::size_of::<Y>()); // will use slice impl

        let v = vec![X(0), X(1), X(2), X(3)];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|X(v)| Y(v));

        {
            let drops = DROPS.lock().unwrap().clone();
            assert_eq!(drops, vec!["X(0)", "X(1)", "X(2)", "X(3)"]);
        }

        let ap = v.as_ptr() as *const ();
        let expected = vec![Y(0), Y(1), Y(2), Y(3)];

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, expected);

        mem::drop(v);

        {
            let drops = DROPS.lock().unwrap().clone();
            assert_eq!(drops,
                       vec!["X(0)", "X(1)", "X(2)", "X(3)", "Y(0)", "Y(1)", "Y(2)", "Y(3)"]);
        }

        mem::drop(expected);
    }

    #[test]
    fn same_size_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|x: u32| (x * x) as i32);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, vec![0, 1, 4, 9]);
    }

    #[test]
    fn different_sizes_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|x: u32| (x * x) as i16);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, vec![0, 1, 4, 9]);
    }

    #[test]
    fn both_zst_vec() {
        #[derive(Debug)]
        struct Zst;

        let v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_| Zst);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    #[test]
    fn nzst_to_zst_vec() {
        let v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_: u32| ());
        let ap = v.as_ptr() as *const ();

        assert!(bp != ap); // -- NOT -- still at same memory addr
    }

    #[test]
    #[should_panic]
    fn zst_to_nzst_vec() {
        let v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = v.map_in_place(|_| 0usize);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    // //////

    #[test]
    fn same_size_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|x: u32| (x * x) as i32);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, &*vec![0, 1, 4, 9]);
    }

    #[test]
    #[should_panic]
    fn different_sizes_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|x: u32| (x * x) as i16);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
        assert_eq!(v, &*vec![0, 1, 4, 9]);
    }

    #[test]
    fn both_zst_slice() {
        #[derive(Debug)]
        struct Zst;

        let mut v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_| Zst);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }

    #[test]
    #[should_panic]
    fn nzst_to_zst_slice() {
        let mut v = vec![0, 1, 2, 3];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_: u32| ());
        let ap = v.as_ptr() as *const ();

        assert!(bp != ap); // -- NOT -- still at same memory addr
    }

    #[test]
    #[should_panic]
    fn zst_to_nzst_slice() {
        let mut v = vec![(), (), (), ()];

        let bp = v.as_ptr() as *const ();
        let v = (&mut *v).map_in_place(|_| 0usize);
        let ap = v.as_ptr() as *const ();

        assert_eq!(bp, ap); // still at same memory addr
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015 by Michael Dilger (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

//! This crate parses and processes a stream of data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `read_formdata`

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate httparse;
extern crate hyper;
#[macro_use]
extern crate mime;
extern crate textnonce;
#[macro_use]
extern crate log;
extern crate encoding;

extern crate mime_multipart;

mod error;
mod form_data;
#[cfg(test)]
mod mock;

pub use error::Error;
pub use form_data::FormData;

use std::io::{Read, Write};
use hyper::header::{Headers, ContentDisposition, DispositionParam, ContentType,
                    DispositionType};
use mime_multipart::{Node, Part};
use mime::{Mime, TopLevel, SubLevel};

/// Parse MIME `multipart/form-data` information from a stream as a `FormData`.
pub fn read_formdata<S: Read>(stream: &mut S, headers: &Headers) -> Result<FormData, Error>
{
    let nodes = try!(mime_multipart::read_multipart_body(stream, headers, false));

    let mut formdata = FormData::new();
    try!(fill_formdata(&mut formdata, nodes));
    Ok(formdata)
}

// order and nesting are irrelevant, so we interate through the nodes and put them
// into one of two buckets (fields and files);  If a multipart node is found, it uses
// the name in its headers as the key (rather than the name in the headers of the
// subparts), which is how multiple file uploads work.
fn fill_formdata(formdata: &mut FormData, nodes: Vec<Node>) -> Result<(), Error>
{
    for node in nodes {
        match node {
            Node::Part(part) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match part.headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(part.body));
                formdata.fields.push((key, val));
            },
            Node::File(part) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match part.headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                formdata.files.push((key, part));
            }
            Node::Multipart((headers, nodes)) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                for node in nodes {
                    match node {
                        Node::Part(part) => {
                            let val = try!(String::from_utf8(part.body));
                            formdata.fields.push((key.clone(), val));
                        },
                        Node::File(part) => {
                            formdata.files.push((key.clone(), part));
                        },
                        _ => { } // don't recurse deeper
                    }
                }
            }
        }
    }
    Ok(())
}

#[inline]
fn get_content_disposition_name(cd: &ContentDisposition) -> Option<String> {
    if let Some(&DispositionParam::Ext(_, ref value)) = cd.parameters.iter()
        .find(|&x| match *x {
            DispositionParam::Ext(ref token,_) => &*token == "name",
            _ => false,
        })
    {
        Some(value.clone())
    } else {
        None
    }
}

/// Stream out `multipart/form-data` body content matching the passed in `formdata`.  This
/// does not stream out headers, so the caller must stream those out before calling
/// write_formdata().

pub fn write_formdata<S: Write>(stream: &mut S, formdata: &FormData) -> Result<usize, Error>
{
    // Translate to Nodes
    let mut nodes: Vec<Node> = Vec::with_capacity(formdata.fields.len() + formdata.files.len());

    for &(ref name, ref value) in &formdata.fields {
        let mut h = Headers::new();
        h.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone())],
        });
        nodes.push( Node::Part( Part {
            headers: h,
            body: value.as_bytes().to_owned(),
        }));
    }

    for &(ref name, ref filepart) in &formdata.files {
        let mut filepart = filepart.clone();
        // We leave all headers that the caller specified, except that we rewrite
        // Content-Disposition.
        while filepart.headers.remove::<ContentDisposition>() { };
        let filename = match filepart.path.file_name() {
            Some(fname) => fname.to_string_lossy().into_owned(),
            None => return Err(Error::NotAFile),
        };
        filepart.headers.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone()),
                             DispositionParam::Ext("filename".to_owned(), filename)],
        });
        nodes.push( Node::File( filepart ) );
    }

    let boundary = ::mime_multipart::generate_boundary();

    // Write out
    let count = try!(::mime_multipart::write_multipart(stream, &boundary, &nodes));

    Ok(count)
}

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

    use super::{FormData, write_formdata, read_formdata};

    use std::net::SocketAddr;
    use std::fs::File;
    use std::io::Write;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;
    use hyper::header::{Headers, ContentDisposition, DispositionParam, ContentType,
                        DispositionType};
    use mime_multipart::FilePart;
    use mime::{Mime, TopLevel, SubLevel};

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, Some(30));
                        assert_eq!(&*file.filename().unwrap().unwrap(), "image.gif");
                        assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, Some(14));
                        assert_eq!(&*file.filename().unwrap().unwrap(), "file.txt");
                        assert!(file.content_type().is_none());
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn multi_file_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                let (ref key, ref file) = form_data.files[0];

                assert_eq!(key, "field2");
                assert_eq!(file.size, Some(30));
                assert_eq!(&*file.filename().unwrap().unwrap(), "image.gif");
                assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));

                let (ref key, ref file) = form_data.files[1];
                assert!(key == "field2");
                assert_eq!(file.size, Some(14));
                assert_eq!(&*file.filename().unwrap().unwrap(), "file.txt");
                assert!(file.content_type().is_none());

            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename().unwrap().unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, Some(29));
                            assert!(file.content_type().is_none());
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, Some(37));
                            assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn simple_writer() {
        // Create a simple short file for testing
        let tmpdir = tempdir::TempDir::new("formdata_test").unwrap();
        let tmppath = tmpdir.path().join("testfile");
        let mut tmpfile = File::create(tmppath.clone()).unwrap();
        writeln!(tmpfile, "this is example file content").unwrap();

        let mut photo_headers = Headers::new();
        photo_headers.set(ContentType(Mime(TopLevel::Image, SubLevel::Gif, vec![])));
        photo_headers.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), "photo".to_owned()),
                             DispositionParam::Ext("filename".to_owned(), "mike.gif".to_owned())],
        });

        let formdata = FormData {
            fields: vec![ ("name".to_owned(), "Mike".to_owned()),
                            ("age".to_owned(), "46".to_owned()) ],
            files: vec![ ("photo".to_owned(), FilePart::new(photo_headers, &tmppath)) ],
        };

        let mut output: Vec<u8> = Vec::new();
        match write_formdata(&mut output, &formdata) {
            Ok(count) => assert_eq!(count, 568),
            Err(e) => panic!("Unable to write formdata: {}", e),
        }

        println!("{}", String::from_utf8_lossy(&output));
    }
}
<commit_msg>pub use mime_multipart::FilePart<commit_after>// Copyright © 2015 by Michael Dilger (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

//! This crate parses and processes a stream of data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `read_formdata`

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate httparse;
extern crate hyper;
#[macro_use]
extern crate mime;
extern crate textnonce;
#[macro_use]
extern crate log;
extern crate encoding;

extern crate mime_multipart;

mod error;
mod form_data;
#[cfg(test)]
mod mock;

pub use error::Error;
pub use form_data::FormData;

use std::io::{Read, Write};
use hyper::header::{Headers, ContentDisposition, DispositionParam, ContentType,
                    DispositionType};
use mime_multipart::{Node, Part};
pub use mime_multipart::FilePart;
use mime::{Mime, TopLevel, SubLevel};

/// Parse MIME `multipart/form-data` information from a stream as a `FormData`.
pub fn read_formdata<S: Read>(stream: &mut S, headers: &Headers) -> Result<FormData, Error>
{
    let nodes = try!(mime_multipart::read_multipart_body(stream, headers, false));

    let mut formdata = FormData::new();
    try!(fill_formdata(&mut formdata, nodes));
    Ok(formdata)
}

// order and nesting are irrelevant, so we interate through the nodes and put them
// into one of two buckets (fields and files);  If a multipart node is found, it uses
// the name in its headers as the key (rather than the name in the headers of the
// subparts), which is how multiple file uploads work.
fn fill_formdata(formdata: &mut FormData, nodes: Vec<Node>) -> Result<(), Error>
{
    for node in nodes {
        match node {
            Node::Part(part) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match part.headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(part.body));
                formdata.fields.push((key, val));
            },
            Node::File(part) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match part.headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                formdata.files.push((key, part));
            }
            Node::Multipart((headers, nodes)) => {
                let cd_name: Option<String> = {
                    let cd: &ContentDisposition = match headers.get() {
                        Some(cd) => cd,
                        None => return Err(Error::MissingDisposition),
                    };
                    get_content_disposition_name(&cd)
                };
                let key = try!(cd_name.ok_or(Error::NoName));
                for node in nodes {
                    match node {
                        Node::Part(part) => {
                            let val = try!(String::from_utf8(part.body));
                            formdata.fields.push((key.clone(), val));
                        },
                        Node::File(part) => {
                            formdata.files.push((key.clone(), part));
                        },
                        _ => { } // don't recurse deeper
                    }
                }
            }
        }
    }
    Ok(())
}

#[inline]
fn get_content_disposition_name(cd: &ContentDisposition) -> Option<String> {
    if let Some(&DispositionParam::Ext(_, ref value)) = cd.parameters.iter()
        .find(|&x| match *x {
            DispositionParam::Ext(ref token,_) => &*token == "name",
            _ => false,
        })
    {
        Some(value.clone())
    } else {
        None
    }
}

/// Stream out `multipart/form-data` body content matching the passed in `formdata`.  This
/// does not stream out headers, so the caller must stream those out before calling
/// write_formdata().

pub fn write_formdata<S: Write>(stream: &mut S, formdata: &FormData) -> Result<usize, Error>
{
    // Translate to Nodes
    let mut nodes: Vec<Node> = Vec::with_capacity(formdata.fields.len() + formdata.files.len());

    for &(ref name, ref value) in &formdata.fields {
        let mut h = Headers::new();
        h.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone())],
        });
        nodes.push( Node::Part( Part {
            headers: h,
            body: value.as_bytes().to_owned(),
        }));
    }

    for &(ref name, ref filepart) in &formdata.files {
        let mut filepart = filepart.clone();
        // We leave all headers that the caller specified, except that we rewrite
        // Content-Disposition.
        while filepart.headers.remove::<ContentDisposition>() { };
        let filename = match filepart.path.file_name() {
            Some(fname) => fname.to_string_lossy().into_owned(),
            None => return Err(Error::NotAFile),
        };
        filepart.headers.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone()),
                             DispositionParam::Ext("filename".to_owned(), filename)],
        });
        nodes.push( Node::File( filepart ) );
    }

    let boundary = ::mime_multipart::generate_boundary();

    // Write out
    let count = try!(::mime_multipart::write_multipart(stream, &boundary, &nodes));

    Ok(count)
}

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

    use super::{FormData, write_formdata, read_formdata};

    use std::net::SocketAddr;
    use std::fs::File;
    use std::io::Write;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;
    use hyper::header::{Headers, ContentDisposition, DispositionParam, ContentType,
                        DispositionType};
    use mime_multipart::FilePart;
    use mime::{Mime, TopLevel, SubLevel};

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, Some(30));
                        assert_eq!(&*file.filename().unwrap().unwrap(), "image.gif");
                        assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, Some(14));
                        assert_eq!(&*file.filename().unwrap().unwrap(), "file.txt");
                        assert!(file.content_type().is_none());
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn multi_file_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                let (ref key, ref file) = form_data.files[0];

                assert_eq!(key, "field2");
                assert_eq!(file.size, Some(30));
                assert_eq!(&*file.filename().unwrap().unwrap(), "image.gif");
                assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));

                let (ref key, ref file) = form_data.files[1];
                assert!(key == "field2");
                assert_eq!(file.size, Some(14));
                assert_eq!(&*file.filename().unwrap().unwrap(), "file.txt");
                assert!(file.content_type().is_none());

            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let req = HyperRequest::new(&mut stream, sock).unwrap();
        let (_, _, headers, _, _, mut reader) = req.deconstruct();

        match read_formdata(&mut reader, &headers) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename().unwrap().unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, Some(29));
                            assert!(file.content_type().is_none());
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, Some(37));
                            assert_eq!(file.content_type().unwrap(), mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn simple_writer() {
        // Create a simple short file for testing
        let tmpdir = tempdir::TempDir::new("formdata_test").unwrap();
        let tmppath = tmpdir.path().join("testfile");
        let mut tmpfile = File::create(tmppath.clone()).unwrap();
        writeln!(tmpfile, "this is example file content").unwrap();

        let mut photo_headers = Headers::new();
        photo_headers.set(ContentType(Mime(TopLevel::Image, SubLevel::Gif, vec![])));
        photo_headers.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![DispositionParam::Ext("name".to_owned(), "photo".to_owned()),
                             DispositionParam::Ext("filename".to_owned(), "mike.gif".to_owned())],
        });

        let formdata = FormData {
            fields: vec![ ("name".to_owned(), "Mike".to_owned()),
                            ("age".to_owned(), "46".to_owned()) ],
            files: vec![ ("photo".to_owned(), FilePart::new(photo_headers, &tmppath)) ],
        };

        let mut output: Vec<u8> = Vec::new();
        match write_formdata(&mut output, &formdata) {
            Ok(count) => assert_eq!(count, 568),
            Err(e) => panic!("Unable to write formdata: {}", e),
        }

        println!("{}", String::from_utf8_lossy(&output));
    }
}
<|endoftext|>


<commit_before>//! # TLS Parser
//!
//! A TLS parser, implemented with the [nom](https://github.com/Geal/nom)
//! parser combinator framework.
//!
//! The goal of this parser is to implement TLS messages analysis, for example
//! to use rules from a network IDS, for ex during the TLS handshake.
//!
//! It implements structures and parsing functions for records and messages, but
//! need additional code to handle fragmentation, or to fully inspect messages.
//! Parsing some TLS messages requires to know the previously selected parameters.
//! See [the rusticata TLS parser](https://github.com/rusticata/rusticata/blob/master/src/tls.rs)
//! for a full example.
//!
//! The code is available on [Github](https://github.com/rusticata/tls-parser)
//! and is part of the [Rusticata](https://github.com/rusticata) project.
//!
//! ## Parsing records
//!
//! The main parsing functions are located in the [tls.rs](src/tls.rs) file. The entry functions are:
//! - `parse_tls_plaintext`: parses a record as plaintext
//! - `parse_tls_encrypted`: read an encrypted record. The parser has no crypto or decryption features, so the content
//!   will be left as opaque data.
//!
//! # Examples
//!
//! ```rust
//! extern crate nom;
//! extern crate tls_parser;
//!
//! use nom::{Err, IResult};
//! use tls_parser::parse_tls_plaintext;
//!
//! let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
//! // [ 0x16, 0x03, 0x01 ... ];
//! let res = parse_tls_plaintext(&bytes);
//! match res {
//!     Ok((rem,record)) => {
//!         // rem is the remaining data (not parsed)
//!         // record is an object of type TlsRecord
//!     },
//!     Err(Err::Incomplete(needed)) => {
//!         eprintln!("Defragmentation required (TLS record)");
//!     },
//!     Err(e) => { eprintln!("parse_tls_record_with_header failed: {:?}",e); }
//! }
//! ```
//!
//! Note that knowing if a record is plaintext or not is the responsibility of the caller.
//!
//! As reading TLS records may imply defragmenting records, some functions are
//! provided to only read the record as opaque data (which ensures the record is
//! complete and gives the record header) and then reading messages from data.
//!
//! Here is an example of two-steps parsing:
//!
//! ```rust
//! # extern crate nom;
//! # extern crate tls_parser;
//! # use nom::{Err, IResult};
//! # use tls_parser::{parse_tls_raw_record, parse_tls_record_with_header};
//!
//! # let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
//! // [ 0x16, 0x03, 0x01 ... ];
//! match parse_tls_raw_record(bytes) {
//!     Ok((rem, ref r)) => {
//!         match parse_tls_record_with_header(r.data, &r.hdr) {
//!             Ok((rem2,ref msg_list)) => {
//!                 for msg in msg_list {
//!                     // msg has type TlsMessage
//!                 }
//!             }
//!             Err(Err::Incomplete(needed)) => { eprintln!("incomplete record") }
//!             Err(_) => { eprintln!("error while parsing record") }
//!         }
//!     }
//!     Err(Err::Incomplete(needed)) => { eprintln!("incomplete record header") }
//!     Err(_) => { eprintln!("error while parsing record header") }
//! }
//! ```
//!
//! Some additional work is required if reading packets from the network, to support
//! reassembly of TCP segments and reassembly of TLS records.
//!
//! For a complete example of a TLS parser supporting defragmentation and states, see the
//! [rusticata/src/tls.rs](https://github.com/rusticata/rusticata/blob/master/src/tls.rs) file of
//! the [rusticata](https://github.com/rusticata/rusticata) crate.
//!
//! ## State machine
//!
//! A TLS state machine is provided in [tls_states.rs](src/tls_states.rs). The state machine is separated from the
//! parsing functions, and is almost independent.
//! It is implemented as a table of transitions, mainly for the handshake phase.
//!
//! After reading a TLS message using the previous functions, the TLS state can be
//! updated using the `tls_state_transition` function. If the transition succeeds,
//! it returns `Ok(new_state)`, otherwise it returns `Err(error_state)`.
//!
//! ```rust
//! # extern crate nom;
//! # extern crate tls_parser;
//! # use nom::{Err, IResult};
//! # use tls_parser::{tls_state_transition, TlsMessage, TlsState};
//!
//! struct ParseContext {
//!     state: TlsState,
//! }
//!
//! # fn update_state_machine(msg: &TlsMessage, ctx: &mut ParseContext, to_server:bool) -> Result<(),&'static str> {
//! match tls_state_transition(ctx.state, msg, to_server) {
//!     Ok(s)  => { ctx.state = s; Ok(()) }
//!     Err(_) => {
//!         ctx.state = TlsState::Invalid;
//!         Err("Invalid state")
//!     }
//! }
//! # }
//! ```
//!
//! # Implementation notes
//!
//! When parsing messages, if a field is an integer corresponding to an enum of known values,
//! it is not parsed as an enum type, but as an integer. While this complicates accesses,
//! it allows to read invalid values and continue parsing (for an IDS, it's better to read
//! values than to get a generic parse error).

#![deny(/*missing_docs,*/
        unstable_features,
        /*unused_import_braces,*/ unused_qualifications)]
#![forbid(unsafe_code)]

pub extern crate rusticata_macros;

mod dtls;
mod tls;
mod tls_alert;
mod tls_ciphers;
mod tls_debug;
mod tls_dh;
mod tls_ec;
mod tls_extensions;
mod tls_sign_hash;
mod tls_states;

pub use dtls::*;
pub use tls::*;
pub use tls_alert::*;
pub use tls_ciphers::*;
pub use tls_dh::*;
pub use tls_ec::*;
pub use tls_extensions::*;
pub use tls_sign_hash::*;
pub use tls_states::*;

#[cfg(feature = "serialize")]
mod tls_serialize;
#[cfg(feature = "serialize")]
pub use tls_serialize::*;
<commit_msg>Clippy: allow upper-case acronyms<commit_after>//! # TLS Parser
//!
//! A TLS parser, implemented with the [nom](https://github.com/Geal/nom)
//! parser combinator framework.
//!
//! The goal of this parser is to implement TLS messages analysis, for example
//! to use rules from a network IDS, for ex during the TLS handshake.
//!
//! It implements structures and parsing functions for records and messages, but
//! need additional code to handle fragmentation, or to fully inspect messages.
//! Parsing some TLS messages requires to know the previously selected parameters.
//! See [the rusticata TLS parser](https://github.com/rusticata/rusticata/blob/master/src/tls.rs)
//! for a full example.
//!
//! The code is available on [Github](https://github.com/rusticata/tls-parser)
//! and is part of the [Rusticata](https://github.com/rusticata) project.
//!
//! ## Parsing records
//!
//! The main parsing functions are located in the [tls.rs](src/tls.rs) file. The entry functions are:
//! - `parse_tls_plaintext`: parses a record as plaintext
//! - `parse_tls_encrypted`: read an encrypted record. The parser has no crypto or decryption features, so the content
//!   will be left as opaque data.
//!
//! # Examples
//!
//! ```rust
//! extern crate nom;
//! extern crate tls_parser;
//!
//! use nom::{Err, IResult};
//! use tls_parser::parse_tls_plaintext;
//!
//! let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
//! // [ 0x16, 0x03, 0x01 ... ];
//! let res = parse_tls_plaintext(&bytes);
//! match res {
//!     Ok((rem,record)) => {
//!         // rem is the remaining data (not parsed)
//!         // record is an object of type TlsRecord
//!     },
//!     Err(Err::Incomplete(needed)) => {
//!         eprintln!("Defragmentation required (TLS record)");
//!     },
//!     Err(e) => { eprintln!("parse_tls_record_with_header failed: {:?}",e); }
//! }
//! ```
//!
//! Note that knowing if a record is plaintext or not is the responsibility of the caller.
//!
//! As reading TLS records may imply defragmenting records, some functions are
//! provided to only read the record as opaque data (which ensures the record is
//! complete and gives the record header) and then reading messages from data.
//!
//! Here is an example of two-steps parsing:
//!
//! ```rust
//! # extern crate nom;
//! # extern crate tls_parser;
//! # use nom::{Err, IResult};
//! # use tls_parser::{parse_tls_raw_record, parse_tls_record_with_header};
//!
//! # let bytes : &[u8]= include_bytes!("../assets/client_hello_dhe.bin");
//! // [ 0x16, 0x03, 0x01 ... ];
//! match parse_tls_raw_record(bytes) {
//!     Ok((rem, ref r)) => {
//!         match parse_tls_record_with_header(r.data, &r.hdr) {
//!             Ok((rem2,ref msg_list)) => {
//!                 for msg in msg_list {
//!                     // msg has type TlsMessage
//!                 }
//!             }
//!             Err(Err::Incomplete(needed)) => { eprintln!("incomplete record") }
//!             Err(_) => { eprintln!("error while parsing record") }
//!         }
//!     }
//!     Err(Err::Incomplete(needed)) => { eprintln!("incomplete record header") }
//!     Err(_) => { eprintln!("error while parsing record header") }
//! }
//! ```
//!
//! Some additional work is required if reading packets from the network, to support
//! reassembly of TCP segments and reassembly of TLS records.
//!
//! For a complete example of a TLS parser supporting defragmentation and states, see the
//! [rusticata/src/tls.rs](https://github.com/rusticata/rusticata/blob/master/src/tls.rs) file of
//! the [rusticata](https://github.com/rusticata/rusticata) crate.
//!
//! ## State machine
//!
//! A TLS state machine is provided in [tls_states.rs](src/tls_states.rs). The state machine is separated from the
//! parsing functions, and is almost independent.
//! It is implemented as a table of transitions, mainly for the handshake phase.
//!
//! After reading a TLS message using the previous functions, the TLS state can be
//! updated using the `tls_state_transition` function. If the transition succeeds,
//! it returns `Ok(new_state)`, otherwise it returns `Err(error_state)`.
//!
//! ```rust
//! # extern crate nom;
//! # extern crate tls_parser;
//! # use nom::{Err, IResult};
//! # use tls_parser::{tls_state_transition, TlsMessage, TlsState};
//!
//! struct ParseContext {
//!     state: TlsState,
//! }
//!
//! # fn update_state_machine(msg: &TlsMessage, ctx: &mut ParseContext, to_server:bool) -> Result<(),&'static str> {
//! match tls_state_transition(ctx.state, msg, to_server) {
//!     Ok(s)  => { ctx.state = s; Ok(()) }
//!     Err(_) => {
//!         ctx.state = TlsState::Invalid;
//!         Err("Invalid state")
//!     }
//! }
//! # }
//! ```
//!
//! # Implementation notes
//!
//! When parsing messages, if a field is an integer corresponding to an enum of known values,
//! it is not parsed as an enum type, but as an integer. While this complicates accesses,
//! it allows to read invalid values and continue parsing (for an IDS, it's better to read
//! values than to get a generic parse error).

#![deny(/*missing_docs,*/
        unstable_features,
        /*unused_import_braces,*/ unused_qualifications)]
#![forbid(unsafe_code)]
#![allow(clippy::upper_case_acronyms)]

pub extern crate rusticata_macros;

mod dtls;
mod tls;
mod tls_alert;
mod tls_ciphers;
mod tls_debug;
mod tls_dh;
mod tls_ec;
mod tls_extensions;
mod tls_sign_hash;
mod tls_states;

pub use dtls::*;
pub use tls::*;
pub use tls_alert::*;
pub use tls_ciphers::*;
pub use tls_dh::*;
pub use tls_ec::*;
pub use tls_extensions::*;
pub use tls_sign_hash::*;
pub use tls_states::*;

#[cfg(feature = "serialize")]
mod tls_serialize;
#[cfg(feature = "serialize")]
pub use tls_serialize::*;
<|endoftext|>


<commit_before>// Copyright 2015-2017 Michael 'ExpHP' Lamparski
//
// Licensed under the terms of the MIT License, available at:
//  http://opensource.org/licenses/MIT
// and also included in the file COPYING at the root of this distribution.
// This file may not be copied, modified, or distributed except according
// to those terms.

//! Documentation is currently under construction.

// TODO: Make documentation not be "currently under construction." :P

#![feature(test)]
#![feature(step_by)]

#![allow(unused_imports)]
#![allow(non_snake_case)]
#![allow(unused_parens)]
#![deny(unused_must_use)]

extern crate num;
extern crate num_bigint;
extern crate test;
extern crate rand;
extern crate bit_set;

mod primes;
mod factorizer;
mod iter_ext;
mod util;
mod factored;
pub mod prelude;

pub use primes::PrimeTester;
pub use primes::PrimeSieve;
pub use factorizer::FactorSieve;
//pub use factorizer::FermatFactorizer;
//pub use factorizer::Dixon; // FIXME broken, has disabled tests
pub use util::isqrt;
pub use util::gcd;
pub use factored::Factored;

// Type-Synonyms, for semantic purposes.
// (NOTE: alas, the docstrings don't appear to take effect)
/// A deterministic `TryFactor` that is guaranteed to work on any number, but may be fast.
pub use factorizer::TrialDivision as SafeFactorizer;
/// The `TryFactor` used by the `factor::factorize` method.
pub use factorizer::TrialDivision as DefaultFactorizer;

use util::literal;
use util::MoreNumCast;

use std::iter::FromIterator;
use std::ops::Shr;
use prelude::*;

use num::{Zero,One,Integer};

/// Factors a number using `DefaultFactorizer`.
pub fn factorize<T>(x: T) -> Factored<T>
 where T: Clone + Zero + One + Integer + Shr<usize, Output=T> + MoreNumCast
{
	DefaultFactorizer.factorize(x)
}

/// Collects all primes up to a limit (inclusive)
///
/// # Example
///
/// ```
/// use factor::primes_upto;
///
/// let v: Vec<u64> = primes_upto(17);
/// assert_eq!(v, vec![2, 3, 5, 7, 11, 13, 17]);
/// ```
pub fn primes_upto<Out>(limit: usize) -> Out
 where Out: FromIterator<u64>
{
	use std::iter::{empty,once};

	if limit < 2 {
		return empty().collect();
	}

	let stop = limit + 1;
	let sieve = primes::PrimeSieve::new(stop);

	let odd_primes = (3..stop).step_by(2)
		.filter(|&i| sieve.is_prime(&i))
		.map(|i| i as u64);

	once(2).chain(odd_primes).collect()
}

#[test]
fn test_primes_upto() {
	// a func to help rust infer the output type
	fn get_em(limit:usize) -> Vec<u64> { primes_upto(limit) }

	assert_eq!(get_em(0), vec![]);
	assert_eq!(get_em(1), vec![]);
	assert_eq!(get_em(2), vec![2]);
	assert_eq!(get_em(3), vec![2, 3]); // first odd prime
	assert_eq!(get_em(4), vec![2, 3]); // even between odd primes
	assert_eq!(get_em(17), vec![2, 3, 5, 7, 11, 13, 17]); // arbitrary odd prime
}
<commit_msg>feature flag changed<commit_after>// Copyright 2015-2017 Michael 'ExpHP' Lamparski
//
// Licensed under the terms of the MIT License, available at:
//  http://opensource.org/licenses/MIT
// and also included in the file COPYING at the root of this distribution.
// This file may not be copied, modified, or distributed except according
// to those terms.

//! Documentation is currently under construction.

// TODO: Make documentation not be "currently under construction." :P

#![feature(test)]
#![feature(iterator_step_by)]

#![allow(unused_imports)]
#![allow(non_snake_case)]
#![allow(unused_parens)]
#![deny(unused_must_use)]

extern crate num;
extern crate num_bigint;
extern crate test;
extern crate rand;
extern crate bit_set;

mod primes;
mod factorizer;
mod iter_ext;
mod util;
mod factored;
pub mod prelude;

pub use primes::PrimeTester;
pub use primes::PrimeSieve;
pub use factorizer::FactorSieve;
//pub use factorizer::FermatFactorizer;
//pub use factorizer::Dixon; // FIXME broken, has disabled tests
pub use util::isqrt;
pub use util::gcd;
pub use factored::Factored;

// Type-Synonyms, for semantic purposes.
// (NOTE: alas, the docstrings don't appear to take effect)
/// A deterministic `TryFactor` that is guaranteed to work on any number, but may be fast.
pub use factorizer::TrialDivision as SafeFactorizer;
/// The `TryFactor` used by the `factor::factorize` method.
pub use factorizer::TrialDivision as DefaultFactorizer;

use util::literal;
use util::MoreNumCast;

use std::iter::FromIterator;
use std::ops::Shr;
use prelude::*;

use num::{Zero,One,Integer};

/// Factors a number using `DefaultFactorizer`.
pub fn factorize<T>(x: T) -> Factored<T>
 where T: Clone + Zero + One + Integer + Shr<usize, Output=T> + MoreNumCast
{
	DefaultFactorizer.factorize(x)
}

/// Collects all primes up to a limit (inclusive)
///
/// # Example
///
/// ```
/// use factor::primes_upto;
///
/// let v: Vec<u64> = primes_upto(17);
/// assert_eq!(v, vec![2, 3, 5, 7, 11, 13, 17]);
/// ```
pub fn primes_upto<Out>(limit: usize) -> Out
 where Out: FromIterator<u64>
{
	use std::iter::{empty,once};

	if limit < 2 {
		return empty().collect();
	}

	let stop = limit + 1;
	let sieve = primes::PrimeSieve::new(stop);

	let odd_primes = (3..stop).step_by(2)
		.filter(|&i| sieve.is_prime(&i))
		.map(|i| i as u64);

	once(2).chain(odd_primes).collect()
}

#[test]
fn test_primes_upto() {
	// a func to help rust infer the output type
	fn get_em(limit:usize) -> Vec<u64> { primes_upto(limit) }

	assert_eq!(get_em(0), vec![]);
	assert_eq!(get_em(1), vec![]);
	assert_eq!(get_em(2), vec![2]);
	assert_eq!(get_em(3), vec![2, 3]); // first odd prime
	assert_eq!(get_em(4), vec![2, 3]); // even between odd primes
	assert_eq!(get_em(17), vec![2, 3, 5, 7, 11, 13, 17]); // arbitrary odd prime
}
<|endoftext|>