<commit_before>extern crate libc;

use std::cell::UnsafeCell;
use std::io::{
    Error,
    ErrorKind,
};
use std::mem;

use libc::{
//    c_char,
    c_int,
    c_uint,
//    mode_t,
};

use self::os::sem_t;

//const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;
// NAME_MAX - 4.
//const SEM_NAME_MAX: usize = 251;

extern {
    fn sem_init(sem: *mut sem_t, pshared: c_int, value: c_uint) -> c_int;
    //fn sem_open(name: *const c_char, oflag: c_int, mode: mode_t, value: c_uint) -> *mut sem_t;
    fn sem_post(sem: *mut sem_t) -> c_int;
    fn sem_wait(sem: *mut sem_t) -> c_int;
    fn sem_destroy(sem: *mut sem_t) -> c_int;
    //fn sem_close(sem: *mut sem_t) -> c_int;
    //fn sem_unlink(sem: *const c_char) -> c_int;
}

#[cfg(not(target_os = "macos"))]
mod os {
    #[cfg(target_pointer_width = "64")]
    const SIZEOF_SEM_T: usize = 32;
    #[cfg(not(target_pointer_width = "64"))]
    const SIZEOF_SEM_T: usize = 16;

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: [u8; SIZEOF_SEM_T],
    }
}

#[cfg(target_os = "macos")]
mod os {
    use libc::c_int;

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: c_int,
    }
}

pub struct Semaphore {
    inner: UnsafeCell<sem_t>,
}

pub struct SemaphoreGuard<'a> {
    sem: &'a Semaphore,
}

impl Semaphore {
    pub fn new(value: u32) -> Semaphore {
        let mut sem: sem_t = unsafe {
            mem::uninitialized()
        };
        let res = unsafe {
            sem_init(&mut sem, 0, value as c_uint)
        };
        debug_assert_eq!(res, 0);

        Semaphore {
            inner: UnsafeCell::new(sem),
        }
    }

    pub fn acquire(&self) {
        loop {
            let res = unsafe {
                sem_wait(self.inner.get())
            };
            if res == -1 {
                match Error::last_os_error() {
                    ref e if e.kind() == ErrorKind::Interrupted => continue,
                    other => panic!("{}", other),
                }
            } else {
                break;
            }
        }
    }

    pub fn release(&self) {
        let res = unsafe {
            sem_post(self.inner.get())
        };
        debug_assert_eq!(res, 0);
    }

    pub fn access(&self) -> SemaphoreGuard {
        self.acquire();
        SemaphoreGuard { sem: self }
    }
}

unsafe impl Send for Semaphore {}
unsafe impl Sync for Semaphore {}

impl Drop for Semaphore {
    fn drop(&mut self) {
        let res = unsafe {
            sem_destroy(self.inner.get())
        };
        debug_assert_eq!(res, 0);
    }
}

impl<'a> Drop for SemaphoreGuard<'a> {
    fn drop(&mut self) {
        self.sem.release();
    }
}

// These tests are taken from the Rust standard library semaphore implementation. Since we
// implement the same interface it makes sense use the same tests as well.
#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use super::Semaphore;
    use std::sync::mpsc::channel;
    use std::thread;

    #[test]
    fn test_sem_acquire_release() {
        let s = Semaphore::new(1);
        s.acquire();
        s.release();
        s.acquire();
    }

    #[test]
    fn test_sem_basic() {
        let s = Semaphore::new(1);
        let _g = s.access();
    }

    #[test]
    fn test_sem_as_mutex() {
        let s = Arc::new(Semaphore::new(1));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            let _g = s2.access();
        });
        let _g = s.access();
    }

    #[test]
    fn test_sem_as_cvar() {
        /* Child waits and parent signals */
        let (tx, rx) = channel();
        let s = Arc::new(Semaphore::new(0));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            s2.acquire();
            tx.send(()).unwrap();
        });
        s.release();
        let _ = rx.recv();

        /* Parent waits and child signals */
        let (tx, rx) = channel();
        let s = Arc::new(Semaphore::new(0));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            s2.release();
            let _ = rx.recv();
        });
        s.acquire();
        tx.send(()).unwrap();
    }

    #[test]
    fn test_sem_multi_resource() {
        // Parent and child both get in the critical section at the same
        // time, and shake hands.
        let s = Arc::new(Semaphore::new(2));
        let s2 = s.clone();
        let (tx1, rx1) = channel();
        let (tx2, rx2) = channel();
        let _t = thread::spawn(move|| {
            let _g = s2.access();
            let _ = rx2.recv();
            tx1.send(()).unwrap();
        });
        let _g = s.access();
        tx2.send(()).unwrap();
        rx1.recv().unwrap();
    }

    #[test]
    fn test_sem_runtime_friendly_blocking() {
        let s = Arc::new(Semaphore::new(1));
        let s2 = s.clone();
        let (tx, rx) = channel();
        {
            let _g = s.access();
            thread::spawn(move|| {
                tx.send(()).unwrap();
                drop(s2.access());
                tx.send(()).unwrap();
            });
            rx.recv().unwrap(); // wait for child to come alive
        }
        rx.recv().unwrap(); // wait for child to be done
    }
}
<commit_msg>use rust stdlib docs<commit_after>extern crate libc;

use std::cell::UnsafeCell;
use std::io::{
    Error,
    ErrorKind,
};
use std::mem;

use libc::{
//    c_char,
    c_int,
    c_uint,
//    mode_t,
};

use self::os::sem_t;

//const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;
// NAME_MAX - 4.
//const SEM_NAME_MAX: usize = 251;

extern {
    fn sem_init(sem: *mut sem_t, pshared: c_int, value: c_uint) -> c_int;
    //fn sem_open(name: *const c_char, oflag: c_int, mode: mode_t, value: c_uint) -> *mut sem_t;
    fn sem_post(sem: *mut sem_t) -> c_int;
    fn sem_wait(sem: *mut sem_t) -> c_int;
    fn sem_destroy(sem: *mut sem_t) -> c_int;
    //fn sem_close(sem: *mut sem_t) -> c_int;
    //fn sem_unlink(sem: *const c_char) -> c_int;
}

#[cfg(not(target_os = "macos"))]
mod os {
    #[cfg(target_pointer_width = "64")]
    const SIZEOF_SEM_T: usize = 32;
    #[cfg(not(target_pointer_width = "64"))]
    const SIZEOF_SEM_T: usize = 16;

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: [u8; SIZEOF_SEM_T],
    }
}

#[cfg(target_os = "macos")]
mod os {
    use libc::c_int;

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: c_int,
    }
}

/// A counting, blocking, semaphore.
///
/// Semaphores are a form of atomic counter where access is only granted if the
/// counter is a positive value. Each acquisition will block the calling thread
/// until the counter is positive, and each release will increment the counter
/// and unblock any threads if necessary.
///
/// # Examples
///
/// ```
/// use sema::Semaphore;
///
/// // Create a semaphore that represents 5 resources
/// let sem = Semaphore::new(5);
///
/// // Acquire one of the resources
/// sem.acquire();
///
/// // Acquire one of the resources for a limited period of time
/// {
///     let _guard = sem.access();
///     // ...
/// } // resources is released here
///
/// // Release our initially acquired resource
/// sem.release();
/// ```
pub struct Semaphore {
    inner: UnsafeCell<sem_t>,
}

/// An RAII guard which will release a resource acquired from a semaphore when
/// dropped.
pub struct SemaphoreGuard<'a> {
    sem: &'a Semaphore,
}

impl Semaphore {
    /// Creates a new semaphore with the initial count specified.
    ///
    /// The count specified can be thought of as a number of resources, and a
    /// call to `acquire` or `access` will block until at least one resource is
    /// available.
    ///
    /// **Note:** Unlike the standard library, this semaphore may not be initialized with a
    /// negative count.
    pub fn new(value: u32) -> Semaphore {
        let mut sem: sem_t = unsafe {
            mem::uninitialized()
        };
        let res = unsafe {
            sem_init(&mut sem, 0, value as c_uint)
        };
        debug_assert_eq!(res, 0);

        Semaphore {
            inner: UnsafeCell::new(sem),
        }
    }

    /// Acquires a resource of this semaphore, blocking the current thread until
    /// it can do so.
    ///
    /// This method will block until the internal count of the semaphore is at
    /// least 1.
    pub fn acquire(&self) {
        loop {
            let res = unsafe {
                sem_wait(self.inner.get())
            };
            if res == -1 {
                match Error::last_os_error() {
                    ref e if e.kind() == ErrorKind::Interrupted => continue,
                    other => panic!("{}", other),
                }
            } else {
                break;
            }
        }
    }

    /// Release a resource from this semaphore.
    ///
    /// This will increment the number of resources in this semaphore by 1 and
    /// will notify any pending waiters in `acquire` or `access` if necessary.
    ///
    /// **Note:** This function is defined by POSIX.1-2001 to be async-signal-safe, and may be
    /// safely called within a signal handler.
    pub fn release(&self) {
        let res = unsafe {
            sem_post(self.inner.get())
        };
        debug_assert_eq!(res, 0);
    }

    /// Acquires a resource of this semaphore, returning an RAII guard to
    /// release the semaphore when dropped.
    ///
    /// This function is semantically equivalent to an `acquire` followed by a
    /// `release` when the guard returned is dropped.
    pub fn access(&self) -> SemaphoreGuard {
        self.acquire();
        SemaphoreGuard { sem: self }
    }
}

unsafe impl Send for Semaphore {}
unsafe impl Sync for Semaphore {}

impl Drop for Semaphore {
    fn drop(&mut self) {
        let res = unsafe {
            sem_destroy(self.inner.get())
        };
        debug_assert_eq!(res, 0);
    }
}

impl<'a> Drop for SemaphoreGuard<'a> {
    fn drop(&mut self) {
        self.sem.release();
    }
}

// These tests are taken from the Rust standard library semaphore implementation. Since we
// implement the same interface, (and use the same docs), it makes sense use the same tests as well.
#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use super::Semaphore;
    use std::sync::mpsc::channel;
    use std::thread;

    #[test]
    fn test_sem_acquire_release() {
        let s = Semaphore::new(1);
        s.acquire();
        s.release();
        s.acquire();
    }

    #[test]
    fn test_sem_basic() {
        let s = Semaphore::new(1);
        let _g = s.access();
    }

    #[test]
    fn test_sem_as_mutex() {
        let s = Arc::new(Semaphore::new(1));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            let _g = s2.access();
        });
        let _g = s.access();
    }

    #[test]
    fn test_sem_as_cvar() {
        /* Child waits and parent signals */
        let (tx, rx) = channel();
        let s = Arc::new(Semaphore::new(0));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            s2.acquire();
            tx.send(()).unwrap();
        });
        s.release();
        let _ = rx.recv();

        /* Parent waits and child signals */
        let (tx, rx) = channel();
        let s = Arc::new(Semaphore::new(0));
        let s2 = s.clone();
        let _t = thread::spawn(move|| {
            s2.release();
            let _ = rx.recv();
        });
        s.acquire();
        tx.send(()).unwrap();
    }

    #[test]
    fn test_sem_multi_resource() {
        // Parent and child both get in the critical section at the same
        // time, and shake hands.
        let s = Arc::new(Semaphore::new(2));
        let s2 = s.clone();
        let (tx1, rx1) = channel();
        let (tx2, rx2) = channel();
        let _t = thread::spawn(move|| {
            let _g = s2.access();
            let _ = rx2.recv();
            tx1.send(()).unwrap();
        });
        let _g = s.access();
        tx2.send(()).unwrap();
        rx1.recv().unwrap();
    }

    #[test]
    fn test_sem_runtime_friendly_blocking() {
        let s = Arc::new(Semaphore::new(1));
        let s2 = s.clone();
        let (tx, rx) = channel();
        {
            let _g = s.access();
            thread::spawn(move|| {
                tx.send(()).unwrap();
                drop(s2.access());
                tx.send(()).unwrap();
            });
            rx.recv().unwrap(); // wait for child to come alive
        }
        rx.recv().unwrap(); // wait for child to be done
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2017 yvt, all rights reserved.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>. This file may
// not be copied, modified,or distributed except
// according to those terms.
//
//! Dynamic suballocators for external memory (e.g., Vulkan device memory).
//!
//! # Provided Algorithms
//!
//! ## Generic
//!
//! |               Name              | Time Complexity |  Space Complexity |
//! | ------------------------------- | --------------- | ----------------- |
//! | TLSF (Two-Level Segregated Fit) | `O(1)`          | `O(N + log size)` |
//! | Free space bitmap               | `O(size)`       | `O(size)`         |
//!
//! ## Specialized
//!
//! |               Name              | Time Complexity |  Space Complexity |
//! | ------------------------------- | --------------- | ----------------- |
//! | Ring buffer                     | `O(1)`          | `O(N)`            |
//!
//! (`size`: heap size measured by the number of allocation units, `N`: number of allocations)
//!
//! # Examples
//!
//! ```
//! use xalloc::{SysTlsf, SysTlsfRegion};
//! let mut tlsf = xalloc::SysTlsf::new(8u32);
//!
//! // Allocate regions
//! let alloc1: (SysTlsfRegion, u32) = tlsf.alloc(4).unwrap();
//! let alloc2: (SysTlsfRegion, u32) = tlsf.alloc(4).unwrap();
//! let (region1, offset1) = alloc1;
//! let (region2, offset2) = alloc2;
//! println!("allocated #1: {:?}", (&region1, offset1));
//! println!("allocated #2: {:?}", (&region2, offset2));
//!
//! // Deallocate a region
//! tlsf.dealloc(region1).unwrap();
//!
//! // Now we can allocate again
//! tlsf.alloc(2).unwrap();
//! tlsf.alloc(2).unwrap();
//! ```
//!
//! # Feature Flags
//!
//! - `nightly` — Enables optimizations which currently require a Nightly Rust
//!   compiler. This flag is now unused due to the [stabilization] of `NonNull`
//!   in Rust 1.25.
//!
//! [stabilization]: https://blog.rust-lang.org/2018/03/29/Rust-1.25.html
//!
// Clippy does not understand that generic numeric types are not always
// as capable as built-in ones and raise false warnings
#![cfg_attr(feature = "cargo-clippy", allow(clippy::op_ref))]

pub extern crate num;
extern crate unreachable;

pub mod arena;
pub mod bitmap;
mod bitmaputils;
pub mod int;
pub mod ring;
pub mod tlsf;

pub use self::bitmap::{BitmapAlloc, BitmapAllocRegion};
pub use self::tlsf::{
    SafeTlsf, SafeTlsfRegion, SysTlsf, SysTlsfRegion, Tlsf, TlsfBlock, TlsfRegion,
};
pub use self::ring::{Ring, RingRegion};
<commit_msg>style: rustfmt<commit_after>//
// Copyright 2017 yvt, all rights reserved.
//
// Licensed under the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>. This file may
// not be copied, modified,or distributed except
// according to those terms.
//
//! Dynamic suballocators for external memory (e.g., Vulkan device memory).
//!
//! # Provided Algorithms
//!
//! ## Generic
//!
//! |               Name              | Time Complexity |  Space Complexity |
//! | ------------------------------- | --------------- | ----------------- |
//! | TLSF (Two-Level Segregated Fit) | `O(1)`          | `O(N + log size)` |
//! | Free space bitmap               | `O(size)`       | `O(size)`         |
//!
//! ## Specialized
//!
//! |               Name              | Time Complexity |  Space Complexity |
//! | ------------------------------- | --------------- | ----------------- |
//! | Ring buffer                     | `O(1)`          | `O(N)`            |
//!
//! (`size`: heap size measured by the number of allocation units, `N`: number of allocations)
//!
//! # Examples
//!
//! ```
//! use xalloc::{SysTlsf, SysTlsfRegion};
//! let mut tlsf = xalloc::SysTlsf::new(8u32);
//!
//! // Allocate regions
//! let alloc1: (SysTlsfRegion, u32) = tlsf.alloc(4).unwrap();
//! let alloc2: (SysTlsfRegion, u32) = tlsf.alloc(4).unwrap();
//! let (region1, offset1) = alloc1;
//! let (region2, offset2) = alloc2;
//! println!("allocated #1: {:?}", (&region1, offset1));
//! println!("allocated #2: {:?}", (&region2, offset2));
//!
//! // Deallocate a region
//! tlsf.dealloc(region1).unwrap();
//!
//! // Now we can allocate again
//! tlsf.alloc(2).unwrap();
//! tlsf.alloc(2).unwrap();
//! ```
//!
//! # Feature Flags
//!
//! - `nightly` — Enables optimizations which currently require a Nightly Rust
//!   compiler. This flag is now unused due to the [stabilization] of `NonNull`
//!   in Rust 1.25.
//!
//! [stabilization]: https://blog.rust-lang.org/2018/03/29/Rust-1.25.html
//!
// Clippy does not understand that generic numeric types are not always
// as capable as built-in ones and raise false warnings
#![cfg_attr(feature = "cargo-clippy", allow(clippy::op_ref))]

pub extern crate num;
extern crate unreachable;

pub mod arena;
pub mod bitmap;
mod bitmaputils;
pub mod int;
pub mod ring;
pub mod tlsf;

pub use self::bitmap::{BitmapAlloc, BitmapAllocRegion};
pub use self::ring::{Ring, RingRegion};
pub use self::tlsf::{
    SafeTlsf, SafeTlsfRegion, SysTlsf, SysTlsfRegion, Tlsf, TlsfBlock, TlsfRegion,
};
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, ()>;

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

fn parse_start(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        _ => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted(byte: u8) -> CSVResult {
    match byte {
        COMMA => Ok(CSVState::Start),
        CR => Ok(CSVState::ExpectLF),
        LF => Ok(CSVState::Start),
        QUOTE => Ok(CSVState::NonQuotedQuote),
        _ => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::NonQuotedValue),
        COMMA => Err(()),
        CR => Err(()),
        LF => Err(()),
        _ => Ok(CSVState::NonQuotedQuote),
    }
}

fn parse_quoted(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuoteQuote),
        CR => Err(()),
        LF => Err(()),
        _ => Ok(CSVState::QuotedValue),
    }
}

fn parse_quote_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        CR => Ok(CSVState::ExpectLF),
        LF => Ok(CSVState::Start),
        _ => Err(()),
    }
}

fn parse_cr(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _ => Err(()),
    }
}

fn parse_err(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _ => Ok(CSVState::Error),
    }
}

fn next_state(state: CSVState, byte: u8) -> CSVResult {
    match state {
        CSVState::Start => parse_start(byte),
        CSVState::NonQuotedValue => parse_non_quoted(byte),
        CSVState::NonQuotedQuote => parse_non_quoted_quote(byte),
        CSVState::QuotedValue => parse_quoted(byte),
        CSVState::QuoteQuote => parse_quote_quote(byte),
        CSVState::ExpectLF => parse_cr(byte),
        CSVState::Error => parse_err(byte),
    }
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for b in file.bytes() {
        let byte = b.unwrap();

        state = match next_state(state, byte) {
            Ok(new_state) => new_state,
            Err(_) => {
                errors.push(CSVError {
                    line: line,
                    col: col,
                });
                CSVState::Error
            }
        };

        if byte == LF {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<commit_msg>Use function pointers and a function type alias<commit_after>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, ()>;

type ByteParser = fn(u8) -> CSVResult;

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

fn parse_start(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        _ => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted(byte: u8) -> CSVResult {
    match byte {
        COMMA => Ok(CSVState::Start),
        CR => Ok(CSVState::ExpectLF),
        LF => Ok(CSVState::Start),
        QUOTE => Ok(CSVState::NonQuotedQuote),
        _ => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::NonQuotedValue),
        COMMA => Err(()),
        CR => Err(()),
        LF => Err(()),
        _ => Ok(CSVState::NonQuotedQuote),
    }
}

fn parse_quoted(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuoteQuote),
        CR => Err(()),
        LF => Err(()),
        _ => Ok(CSVState::QuotedValue),
    }
}

fn parse_quote_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        CR => Ok(CSVState::ExpectLF),
        LF => Ok(CSVState::Start),
        _ => Err(()),
    }
}

fn parse_cr(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _ => Err(()),
    }
}

fn parse_err(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _ => Ok(CSVState::Error),
    }
}

fn next_state(state: CSVState, byte: u8) -> CSVResult {
    let parse_fn: ByteParser = match state {
        CSVState::Start => parse_start,
        CSVState::NonQuotedValue => parse_non_quoted,
        CSVState::NonQuotedQuote => parse_non_quoted_quote,
        CSVState::QuotedValue => parse_quoted,
        CSVState::QuoteQuote => parse_quote_quote,
        CSVState::ExpectLF => parse_cr,
        CSVState::Error => parse_err,
    };
    parse_fn(byte)
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for b in file.bytes() {
        let byte = b.unwrap();

        state = match next_state(state, byte) {
            Ok(new_state) => new_state,
            Err(_) => {
                errors.push(CSVError {
                    line: line,
                    col: col,
                });
                CSVState::Error
            }
        };

        if byte == LF {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate getopts;
extern crate daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

pub mod connection;

use daemonize::{Daemonize};
use std::process::Command;
use std::net::{SocketAddr, SocketAddrV4, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use log::{LogRecord, LogLevel, LogMetadata};
use std::sync::mpsc;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt);
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*);
    };
}

pub struct Server<'a> {
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let logpath = Path::new(&env::var("HOME").unwrap()).join(".shoop.log");
            let mut f = OpenOptions::new().append(true).create(true).open(logpath).expect("failed to open logfile");
            let line = format!("{} - {}\n", record.level(), record.args());
            print!("{}", line);
            let _ = f.write_all(&line.into_bytes()[..]);
            let _ = std::io::stdout().flush();
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr { kind: kind, msg: Some(String::from(msg)), finished: finished }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr { kind: ShoopErrKind::Severed, msg: Some(format!("{:?}", err)), finished: finished }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range : connection::PortRange,
               filename   : &str)
        -> Server
    {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => { die!("SSH_CONNECTION env variable unset and required. Quitting."); }
        };
        let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
        let ip = sshconn[2];
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let conn = connection::Server::new(IpAddr::from_str(ip).unwrap(), port_range, key);
        println!("shoop 0 {} {} {}", ip, conn.port, keybytes.to_hex());
        let daemonize = Daemonize::new();
        match daemonize.start() {
            Ok(_) => { let _ = writeln!(&mut stderr(), "daemonized"); }
            Err(_) => { let _ = writeln!(&mut stderr(), "RWRWARWARARRR"); }
        }

        Server { conn: conn, filename: filename }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => { die!("error on sock accept() {:?}", e); }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Severed, msg, finished}) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished}) => {
                            info!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                            panic!("{:?}", msg);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self, client: &connection::ServerConnection, offset: u64) -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, "failed to write filesize header before timeout", remaining))
            },
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    match client.send(&payload[0..read]) {
                        Ok(()) => {
                            return Err(ShoopErr::new(ShoopErrKind::Severed, "failed to write filesize header before timeout", remaining))
                        },
                        Err(e) => {
                            return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
                        }
                    }
                },
                Err(e) => {
                    client.close().expect("Error closing stream");
                    error!("failed to read from file.");
                    panic!("{:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host : &str,
                port_range      : connection::PortRange,
                remote_path     : &str,
                local_path      : PathBuf)
{
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
                         .arg(remote_ssh_host.to_owned())
                         .arg(cmd)
                         .output()
                         .unwrap_or_else(|e| {
                             panic!("failed to execute process: {}", e);
                         });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(" ").collect();
    if info.len() != 5 {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            },
            Err(e) => {
                panic!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        match conn.send(&wtr[..]) {
            Err(_) => { conn.close().unwrap(); continue; }
            _      => {}
        }

        match conn.recv() {
            Ok(msg) => {
                if msg.len() == 0 {
                    panic!("failed to get filesize from server, probable timeout.");
                }
                let mut rdr = Cursor::new(msg);
                filesize = filesize.or(Some(rdr.read_u64::<LittleEndian>().unwrap()));
                overprint!(" + downloading {} ({:.1}MB)\n", local_path.to_string_lossy(), (filesize.unwrap() as f64)/(1024f64*1024f64));
                match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                     Ok(_) => {
                         break;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Severed, msg: _, finished}) => {
                         println!(" * [[SEVERED]]");
                         offset = finished;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished: _}) => {
                         panic!("{:?}", msg);
                     }
                }
            }
            Err(_) => {}
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time);
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_)     => false
    }
}

fn recv_file(conn: &connection::Client, filesize: u64, filename: &PathBuf, offset: u64) -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
                           .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 / ((speed_elapsed.as_secs() as f64) + (speed_elapsed.subsec_nanos() as f64) / 1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64, speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]", (filesize as f64)/(1024f64*1024f64), ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            println!("\ndone.");
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}

<commit_msg>small logging fix<commit_after>#[macro_use]
extern crate log;
extern crate getopts;
extern crate daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;

pub mod connection;

use daemonize::{Daemonize};
use std::process::Command;
use std::net::{SocketAddr, SocketAddrV4, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use log::{LogRecord, LogLevel, LogMetadata};
use std::sync::mpsc;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt);
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*);
    };
}

pub struct Server<'a> {
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let logpath = Path::new(&env::var("HOME").unwrap()).join(".shoop.log");
            let mut f = OpenOptions::new().append(true).create(true).open(logpath).expect("failed to open logfile");
            let line = format!("{} - {}\n", record.level(), record.args());
            print!("{}", line);
            let _ = f.write_all(&line.into_bytes()[..]);
            let _ = std::io::stdout().flush();
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr { kind: kind, msg: Some(String::from(msg)), finished: finished }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr { kind: ShoopErrKind::Severed, msg: Some(format!("{:?}", err)), finished: finished }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range : connection::PortRange,
               filename   : &str)
        -> Server
    {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => { die!("SSH_CONNECTION env variable unset and required. Quitting."); }
        };
        let sshconn: Vec<&str> = sshconnstr.split(" ").collect();
        let ip = sshconn[2];
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let conn = connection::Server::new(IpAddr::from_str(ip).unwrap(), port_range, key);
        println!("shoop 0 {} {} {}", ip, conn.port, keybytes.to_hex());
        let daemonize = Daemonize::new();
        match daemonize.start() {
            Ok(_) => { let _ = info!("daemonized"); }
            Err(_) => { let _ = error!("RWRWARWARARRR"); }
        }

        Server { conn: conn, filename: filename }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => { die!("error on sock accept() {:?}", e); }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Severed, msg, finished}) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished}) => {
                            info!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                            panic!("{:?}", msg);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self, client: &connection::ServerConnection, offset: u64) -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, "failed to write filesize header before timeout", remaining))
            },
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    match client.send(&payload[0..read]) {
                        Ok(()) => {
                            return Err(ShoopErr::new(ShoopErrKind::Severed, "failed to write filesize header before timeout", remaining))
                        },
                        Err(e) => {
                            return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
                        }
                    }
                },
                Err(e) => {
                    client.close().expect("Error closing stream");
                    error!("failed to read from file.");
                    panic!("{:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host : &str,
                port_range      : connection::PortRange,
                remote_path     : &str,
                local_path      : PathBuf)
{
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
                         .arg(remote_ssh_host.to_owned())
                         .arg(cmd)
                         .output()
                         .unwrap_or_else(|e| {
                             panic!("failed to execute process: {}", e);
                         });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(" ").collect();
    if info.len() != 5 {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        panic!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..]).unwrap());
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            },
            Err(e) => {
                panic!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        match conn.send(&wtr[..]) {
            Err(_) => { conn.close().unwrap(); continue; }
            _      => {}
        }

        match conn.recv() {
            Ok(msg) => {
                if msg.len() == 0 {
                    panic!("failed to get filesize from server, probable timeout.");
                }
                let mut rdr = Cursor::new(msg);
                filesize = filesize.or(Some(rdr.read_u64::<LittleEndian>().unwrap()));
                overprint!(" + downloading {} ({:.1}MB)\n", local_path.to_string_lossy(), (filesize.unwrap() as f64)/(1024f64*1024f64));
                match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                     Ok(_) => {
                         break;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Severed, msg: _, finished}) => {
                         println!(" * [[SEVERED]]");
                         offset = finished;
                     }
                     Err(ShoopErr{ kind: ShoopErrKind::Fatal, msg, finished: _}) => {
                         panic!("{:?}", msg);
                     }
                }
            }
            Err(_) => {}
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time);
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_)     => false
    }
}

fn recv_file(conn: &connection::Client, filesize: u64, filename: &PathBuf, offset: u64) -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
                           .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 / ((speed_elapsed.as_secs() as f64) + (speed_elapsed.subsec_nanos() as f64) / 1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]", (total as f64)/(1024f64*1024f64), (filesize as f64)/(1024f64*1024f64), (total as f64) / (filesize as f64) * 100f64, speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]", (filesize as f64)/(1024f64*1024f64), ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            println!("\ndone.");
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}

<|endoftext|>


<commit_before>//! # `derive_more`
//! Rust has lots of builtin traits that are implemented for its basic types, such as [`Add`],
//! [`Not`] or [`From`].
//! However, when wrapping these types inside your own structs or enums you lose the
//! implementations of these traits and are required to recreate them.
//! This is especially annoying when your own structures are very simple, such as when using the
//! commonly advised newtype pattern (e.g. `MyInt(i32)`).
//!
//! This library tries to remove these annoyances and the corresponding boilerplate code.
//! It does this by allowing you to derive lots of commonly used traits for both structs and enums.
//!
//! ## Example code
//!
//! By using this library the following code just works:
//!
//!
//! ```rust
//! #[macro_use]
//! extern crate derive_more;
//!
//! #[derive(Debug, Eq, PartialEq, From, Add)]
//! struct MyInt(i32);
//!
//! #[derive(Debug, Eq, PartialEq, From, Into, Constructor, Mul)]
//! struct Point2D {
//!     x: i32,
//!     y: i32,
//! }
//!
//! #[derive(Debug, Eq, PartialEq, From, Add)]
//! enum MyEnum {
//!     Int(i32),
//!     UnsignedInt(u32),
//!     Nothing,
//! }
//!
//! fn main() {
//!     let my_11 = MyInt(5) + 6.into();
//!     assert_eq!(MyInt(11), MyInt(5) + 6.into());
//!     assert_eq!(Point2D { x: 5, y: 6 } * 10, (50, 60).into());
//!     assert_eq!((5, 6), Point2D { x: 5, y: 6 }.into());
//!     assert_eq!(Point2D { x: 5, y: 6 }, Point2D::new(5, 6));
//!     assert_eq!(MyEnum::Int(15), (MyEnum::Int(8) + 7.into()).unwrap())
//! }
//! ```
//!
//! ## The derivable traits
//!
//! Below are all the traits that you can derive using this library.
//! Some trait derivations are so similar that the further documentation will only show a single one
//! of them.
//! You can recognize these by the "-like" suffix in their name.
//! The trait name before that will be the only one that is used throughout the further
//! documentation.
//!
//! **NOTE**: You still have to derive each trait separately. So `#[derive(Mul)]` doesn't
//! automatically derive `Div` as well. To derive both you should do `#[derive(Mul, Div)]`
//!
//! ### Conversion traits
//! These are traits that are used to convert automatically between types.
//!
//! 1. [`From`]
//! 2. [`Into`]
//! 3. [`FromStr`]
//! 4. [`TryInto`] (nightly-only as of writing)
//!
//! ### Formatting traits
//! These traits are used for converting a struct to a string in different ways.
//!
//! 1. `Display`-like, contains [`Display`], [`Binary`], [`Octal`], [`LowerHex`], [`UpperHex`],
//!    [`LowerExp`], [`UpperExp`], [`Pointer`]
//!
//! ### Operators
//! These are traits that can be used for operator overloading.
//!
//! 1. [`Index`]
//! 2. [`Deref`]
//! 3. `Not`-like, contains [`Not`] and [`Neg`]
//! 4. `Add`-like, contains [`Add`], [`Sub`], [`BitAnd`], [`BitOr`] and [`BitXor`]
//! 5. `Mul`-like, contains [`Mul`], [`Div`], [`Rem`], [`Shr`] and [`Shl`]
//! 6. [`IndexMut`]
//! 7. [`DerefMut`]
//! 8. `AddAssign`-like, contains [`AddAssign`], [`SubAssign`], [`BitAndAssign`], [`BitOrAssign`]
//!    and [`BitXorAssign`]
//! 9. `MulAssign`-like, contains [`MulAssign`], [`DivAssign`], [`RemAssign`], [`ShrAssign`] and
//!    [`ShlAssign`]
//!
//! ### Static methods
//! These don't derive traits, but derive static methods instead.
//!
//! 1. `Constructor`, this derives a `new` method that can be used as a constructor. This is very
//!    basic if you need more customization for your constructor, check out the [`derive-new`] crate.
//!
//!
//! ## Generated code
//!
//! It is important to understand what code gets generated when using one of the derives from this
//! crate.
//! That is why the links below explain what code gets generated for a trait for each group from
//! before.
//!
//! 1. [`#[derive(From)]`](from.html)
//! 2. [`#[derive(Into)]`](into.html)
//! 3. [`#[derive(FromStr)]`](from_str.html)
//! 4. [`#[derive(TryInto)]`](try_into.html)
//! 5. [`#[derive(Display)]`](display.html)
//! 6. [`#[derive(Index)]`](index_op.html)
//! 7. [`#[derive(Deref)]`](deref.html)
//! 8. [`#[derive(Not)]`](not.html)
//! 9. [`#[derive(Add)]`](add.html)
//! 10. [`#[derive(Mul)]`](mul.html)
//! 11. [`#[derive(IndexMut)]`](index_mut.html)
//! 12. [`#[derive(DerefMut)]`](deref_mut.html)
//! 13. [`#[derive(AddAssign)]`](add_assign.html)
//! 14. [`#[derive(MulAssign)]`](mul_assign.html)
//! 15. [`#[derive(Constructor)]`](constructor.html)
//!
//! If you want to be sure what code is generated for your specific type I recommend using the
//! [`cargo-expand`] utility.
//! This will show you your code with all macros and derives expanded.
//!
//! ## Installation
//!
//! This library requires Rust 1.15 or higher, so this needs to be installed.
//! Then add the following to `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! derive_more = "0.10.0"
//! ```
//!
//! And this to the top of your Rust file:
//!
//! ```rust
//! #[macro_use]
//! extern crate derive_more;
//! # fn main () {}
//! ```
//!
//! [`cargo-expand`]: https://github.com/dtolnay/cargo-expand
//! [`derive-new`]: https://github.com/nrc/derive-new
//! [`From`]: https://doc.rust-lang.org/core/convert/trait.From.html
//! [`Into`]: https://doc.rust-lang.org/core/convert/trait.Into.html
//! [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
//! [`TryInto`]: https://doc.rust-lang.org/core/convert/trait.TryInto.html
//! [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [`Binary`]: https://doc.rust-lang.org/std/fmt/trait.Binary.html
//! [`Octal`]: https://doc.rust-lang.org/std/fmt/trait.Octal.html
//! [`LowerHex`]: https://doc.rust-lang.org/std/fmt/trait.LowerHex.html
//! [`UpperHex`]: https://doc.rust-lang.org/std/fmt/trait.UpperHex.html
//! [`LowerExp`]: https://doc.rust-lang.org/std/fmt/trait.LowerExp.html
//! [`UpperExp`]: https://doc.rust-lang.org/std/fmt/trait.UpperExp.html
//! [`Pointer`]: https://doc.rust-lang.org/std/fmt/trait.Pointer.html
//! [`Index`]: https://doc.rust-lang.org/std/ops/trait.Index.html
//! [`Deref`]: https://doc.rust-lang.org/std/ops/trait.Deref.html
//! [`Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html
//! [`Neg`]: https://doc.rust-lang.org/std/ops/trait.Neg.html
//! [`Add`]: https://doc.rust-lang.org/std/ops/trait.Add.html
//! [`Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
//! [`BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
//! [`BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
//! [`BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html
//! [`Mul`]: https://doc.rust-lang.org/std/ops/trait.Mul.html
//! [`Div`]: https://doc.rust-lang.org/std/ops/trait.Div.html
//! [`Rem`]: https://doc.rust-lang.org/std/ops/trait.Rem.html
//! [`Shr`]: https://doc.rust-lang.org/std/ops/trait.Shr.html
//! [`Shl`]: https://doc.rust-lang.org/std/ops/trait.Shl.html
//! [`IndexMut`]: https://doc.rust-lang.org/std/ops/trait.IndexMut.html
//! [`DerefMut`]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html
//! [`AddAssign`]: https://doc.rust-lang.org/std/ops/trait.AddAssign.html
//! [`SubAssign`]: https://doc.rust-lang.org/std/ops/trait.SubAssign.html
//! [`BitAndAssign`]: https://doc.rust-lang.org/std/ops/trait.BitAndAssign.html
//! [`BitOrAssign`]: https://doc.rust-lang.org/std/ops/trait.BitOrAssign.html
//! [`BitXorAssign`]: https://doc.rust-lang.org/std/ops/trait.BitXorAssign.html
//! [`MulAssign`]: https://doc.rust-lang.org/std/ops/trait.MulAssign.html
//! [`DivAssign`]: https://doc.rust-lang.org/std/ops/trait.DivAssign.html
//! [`RemAssign`]: https://doc.rust-lang.org/std/ops/trait.RemAssign.html
//! [`ShrAssign`]: https://doc.rust-lang.org/std/ops/trait.ShrAssign.html
//! [`ShlAssign`]: https://doc.rust-lang.org/std/ops/trait.ShlAssign.html

extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

mod utils;

mod add_assign_like;
mod add_like;
mod constructor;
mod deref;
mod deref_mut;
mod display;
mod from;
mod from_str;
mod index;
mod index_mut;
mod into;
mod mul_assign_like;
mod mul_like;
mod not_like;
mod try_into;

macro_rules! create_derive(
    ($mod_:ident, $trait_:ident, $fn_name: ident) => {
        #[proc_macro_derive($trait_)]
        #[doc(hidden)]
        pub fn $fn_name(input: TokenStream) -> TokenStream {
            let ast = syn::parse(input).unwrap();
            $mod_::expand(&ast, stringify!($trait_)).into()
        }
    }
);

create_derive!(from, From, from_derive);

create_derive!(into, Into, into_derive);

create_derive!(constructor, Constructor, constructor_derive);

create_derive!(not_like, Not, not_derive);
create_derive!(not_like, Neg, neg_derive);

create_derive!(add_like, Add, add_derive);
create_derive!(add_like, Sub, sub_derive);
create_derive!(add_like, BitAnd, bit_and_derive);
create_derive!(add_like, BitOr, bit_or_derive);
create_derive!(add_like, BitXor, bit_xor_derive);

create_derive!(mul_like, Mul, mul_derive);
create_derive!(mul_like, Div, div_derive);
create_derive!(mul_like, Rem, rem_derive);
create_derive!(mul_like, Shr, shr_derive);
create_derive!(mul_like, Shl, shl_derive);

create_derive!(add_assign_like, AddAssign, add_assign_derive);
create_derive!(add_assign_like, SubAssign, sub_assign_derive);
create_derive!(add_assign_like, BitAndAssign, bit_and_assign_derive);
create_derive!(add_assign_like, BitOrAssign, bit_or_assign_derive);
create_derive!(add_assign_like, BitXorAssign, bit_xor_assign_derive);

create_derive!(mul_assign_like, MulAssign, mul_assign_derive);
create_derive!(mul_assign_like, DivAssign, div_assign_derive);
create_derive!(mul_assign_like, RemAssign, rem_assign_derive);
create_derive!(mul_assign_like, ShrAssign, shr_assign_derive);
create_derive!(mul_assign_like, ShlAssign, shl_assign_derive);

create_derive!(from_str, FromStr, from_str_derive);

create_derive!(display, Display, display_derive);
create_derive!(display, Binary, binary_derive);
create_derive!(display, Octal, octal_derive);
create_derive!(display, LowerHex, lower_hex_derive);
create_derive!(display, UpperHex, upper_hex_derive);
create_derive!(display, LowerExp, lower_exp_derive);
create_derive!(display, UpperExp, upper_exp_derive);
create_derive!(display, Pointer, pointer_derive);

create_derive!(index, Index, index_derive);
create_derive!(index_mut, IndexMut, index_mut_derive);

create_derive!(try_into, TryInto, try_into_derive);

create_derive!(deref, Deref, deref_derive);
create_derive!(deref_mut, DerefMut, deref_mut_derive);
<commit_msg>Update URLs for release on docs.rs<commit_after>//! # `derive_more`
//! Rust has lots of builtin traits that are implemented for its basic types, such as [`Add`],
//! [`Not`] or [`From`].
//! However, when wrapping these types inside your own structs or enums you lose the
//! implementations of these traits and are required to recreate them.
//! This is especially annoying when your own structures are very simple, such as when using the
//! commonly advised newtype pattern (e.g. `MyInt(i32)`).
//!
//! This library tries to remove these annoyances and the corresponding boilerplate code.
//! It does this by allowing you to derive lots of commonly used traits for both structs and enums.
//!
//! ## Example code
//!
//! By using this library the following code just works:
//!
//!
//! ```rust
//! #[macro_use]
//! extern crate derive_more;
//!
//! #[derive(Debug, Eq, PartialEq, From, Add)]
//! struct MyInt(i32);
//!
//! #[derive(Debug, Eq, PartialEq, From, Into, Constructor, Mul)]
//! struct Point2D {
//!     x: i32,
//!     y: i32,
//! }
//!
//! #[derive(Debug, Eq, PartialEq, From, Add)]
//! enum MyEnum {
//!     Int(i32),
//!     UnsignedInt(u32),
//!     Nothing,
//! }
//!
//! fn main() {
//!     let my_11 = MyInt(5) + 6.into();
//!     assert_eq!(MyInt(11), MyInt(5) + 6.into());
//!     assert_eq!(Point2D { x: 5, y: 6 } * 10, (50, 60).into());
//!     assert_eq!((5, 6), Point2D { x: 5, y: 6 }.into());
//!     assert_eq!(Point2D { x: 5, y: 6 }, Point2D::new(5, 6));
//!     assert_eq!(MyEnum::Int(15), (MyEnum::Int(8) + 7.into()).unwrap())
//! }
//! ```
//!
//! ## The derivable traits
//!
//! Below are all the traits that you can derive using this library.
//! Some trait derivations are so similar that the further documentation will only show a single one
//! of them.
//! You can recognize these by the "-like" suffix in their name.
//! The trait name before that will be the only one that is used throughout the further
//! documentation.
//!
//! **NOTE**: You still have to derive each trait separately. So `#[derive(Mul)]` doesn't
//! automatically derive `Div` as well. To derive both you should do `#[derive(Mul, Div)]`
//!
//! ### Conversion traits
//! These are traits that are used to convert automatically between types.
//!
//! 1. [`From`]
//! 2. [`Into`]
//! 3. [`FromStr`]
//! 4. [`TryInto`] (nightly-only as of writing)
//!
//! ### Formatting traits
//! These traits are used for converting a struct to a string in different ways.
//!
//! 1. `Display`-like, contains [`Display`], [`Binary`], [`Octal`], [`LowerHex`], [`UpperHex`],
//!    [`LowerExp`], [`UpperExp`], [`Pointer`]
//!
//! ### Operators
//! These are traits that can be used for operator overloading.
//!
//! 1. [`Index`]
//! 2. [`Deref`]
//! 3. `Not`-like, contains [`Not`] and [`Neg`]
//! 4. `Add`-like, contains [`Add`], [`Sub`], [`BitAnd`], [`BitOr`] and [`BitXor`]
//! 5. `Mul`-like, contains [`Mul`], [`Div`], [`Rem`], [`Shr`] and [`Shl`]
//! 6. [`IndexMut`]
//! 7. [`DerefMut`]
//! 8. `AddAssign`-like, contains [`AddAssign`], [`SubAssign`], [`BitAndAssign`], [`BitOrAssign`]
//!    and [`BitXorAssign`]
//! 9. `MulAssign`-like, contains [`MulAssign`], [`DivAssign`], [`RemAssign`], [`ShrAssign`] and
//!    [`ShlAssign`]
//!
//! ### Static methods
//! These don't derive traits, but derive static methods instead.
//!
//! 1. `Constructor`, this derives a `new` method that can be used as a constructor. This is very
//!    basic if you need more customization for your constructor, check out the [`derive-new`] crate.
//!
//!
//! ## Generated code
//!
//! It is important to understand what code gets generated when using one of the derives from this
//! crate.
//! That is why the links below explain what code gets generated for a trait for each group from
//! before.
//!
//! 1. [`#[derive(From)]`](https://jeltef.github.io/derive_more/derive_more/from.html)
//! 2. [`#[derive(Into)]`](https://jeltef.github.io/derive_more/derive_more/into.html)
//! 3. [`#[derive(FromStr)]`](https://jeltef.github.io/derive_more/derive_more/from_str.html)
//! 4. [`#[derive(TryInto)]`](https://jeltef.github.io/derive_more/derive_more/try_into.html)
//! 5. [`#[derive(Display)]`](https://jeltef.github.io/derive_more/derive_more/display.html)
//! 6. [`#[derive(Index)]`](https://jeltef.github.io/derive_more/derive_more/index_op.html)
//! 7. [`#[derive(Deref)]`](https://jeltef.github.io/derive_more/derive_more/deref.html)
//! 8. [`#[derive(Not)]`](https://jeltef.github.io/derive_more/derive_more/not.html)
//! 9. [`#[derive(Add)]`](https://jeltef.github.io/derive_more/derive_more/add.html)
//! 10. [`#[derive(Mul)]`](https://jeltef.github.io/derive_more/derive_more/mul.html)
//! 11. [`#[derive(IndexMut)]`](https://jeltef.github.io/derive_more/derive_more/index_mut.html)
//! 12. [`#[derive(DerefMut)]`](https://jeltef.github.io/derive_more/derive_more/deref_mut.html)
//! 13. [`#[derive(AddAssign)]`](https://jeltef.github.io/derive_more/derive_more/add_assign.html)
//! 14. [`#[derive(MulAssign)]`](https://jeltef.github.io/derive_more/derive_more/mul_assign.html)
//! 15. [`#[derive(Constructor)]`](https://jeltef.github.io/derive_more/derive_more/constructor.html)
//!
//! If you want to be sure what code is generated for your specific type I recommend using the
//! [`cargo-expand`] utility.
//! This will show you your code with all macros and derives expanded.
//!
//! ## Installation
//!
//! This library requires Rust 1.15 or higher, so this needs to be installed.
//! Then add the following to `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! derive_more = "0.10.0"
//! ```
//!
//! And this to the top of your Rust file:
//!
//! ```rust
//! #[macro_use]
//! extern crate derive_more;
//! # fn main () {}
//! ```
//!
//! [`cargo-expand`]: https://github.com/dtolnay/cargo-expand
//! [`derive-new`]: https://github.com/nrc/derive-new
//! [`From`]: https://doc.rust-lang.org/core/convert/trait.From.html
//! [`Into`]: https://doc.rust-lang.org/core/convert/trait.Into.html
//! [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
//! [`TryInto`]: https://doc.rust-lang.org/core/convert/trait.TryInto.html
//! [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [`Binary`]: https://doc.rust-lang.org/std/fmt/trait.Binary.html
//! [`Octal`]: https://doc.rust-lang.org/std/fmt/trait.Octal.html
//! [`LowerHex`]: https://doc.rust-lang.org/std/fmt/trait.LowerHex.html
//! [`UpperHex`]: https://doc.rust-lang.org/std/fmt/trait.UpperHex.html
//! [`LowerExp`]: https://doc.rust-lang.org/std/fmt/trait.LowerExp.html
//! [`UpperExp`]: https://doc.rust-lang.org/std/fmt/trait.UpperExp.html
//! [`Pointer`]: https://doc.rust-lang.org/std/fmt/trait.Pointer.html
//! [`Index`]: https://doc.rust-lang.org/std/ops/trait.Index.html
//! [`Deref`]: https://doc.rust-lang.org/std/ops/trait.Deref.html
//! [`Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html
//! [`Neg`]: https://doc.rust-lang.org/std/ops/trait.Neg.html
//! [`Add`]: https://doc.rust-lang.org/std/ops/trait.Add.html
//! [`Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
//! [`BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
//! [`BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
//! [`BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html
//! [`Mul`]: https://doc.rust-lang.org/std/ops/trait.Mul.html
//! [`Div`]: https://doc.rust-lang.org/std/ops/trait.Div.html
//! [`Rem`]: https://doc.rust-lang.org/std/ops/trait.Rem.html
//! [`Shr`]: https://doc.rust-lang.org/std/ops/trait.Shr.html
//! [`Shl`]: https://doc.rust-lang.org/std/ops/trait.Shl.html
//! [`IndexMut`]: https://doc.rust-lang.org/std/ops/trait.IndexMut.html
//! [`DerefMut`]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html
//! [`AddAssign`]: https://doc.rust-lang.org/std/ops/trait.AddAssign.html
//! [`SubAssign`]: https://doc.rust-lang.org/std/ops/trait.SubAssign.html
//! [`BitAndAssign`]: https://doc.rust-lang.org/std/ops/trait.BitAndAssign.html
//! [`BitOrAssign`]: https://doc.rust-lang.org/std/ops/trait.BitOrAssign.html
//! [`BitXorAssign`]: https://doc.rust-lang.org/std/ops/trait.BitXorAssign.html
//! [`MulAssign`]: https://doc.rust-lang.org/std/ops/trait.MulAssign.html
//! [`DivAssign`]: https://doc.rust-lang.org/std/ops/trait.DivAssign.html
//! [`RemAssign`]: https://doc.rust-lang.org/std/ops/trait.RemAssign.html
//! [`ShrAssign`]: https://doc.rust-lang.org/std/ops/trait.ShrAssign.html
//! [`ShlAssign`]: https://doc.rust-lang.org/std/ops/trait.ShlAssign.html

extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

mod utils;

mod add_assign_like;
mod add_like;
mod constructor;
mod deref;
mod deref_mut;
mod display;
mod from;
mod from_str;
mod index;
mod index_mut;
mod into;
mod mul_assign_like;
mod mul_like;
mod not_like;
mod try_into;

macro_rules! create_derive(
    ($mod_:ident, $trait_:ident, $fn_name: ident) => {
        #[proc_macro_derive($trait_)]
        #[doc(hidden)]
        pub fn $fn_name(input: TokenStream) -> TokenStream {
            let ast = syn::parse(input).unwrap();
            $mod_::expand(&ast, stringify!($trait_)).into()
        }
    }
);

create_derive!(from, From, from_derive);

create_derive!(into, Into, into_derive);

create_derive!(constructor, Constructor, constructor_derive);

create_derive!(not_like, Not, not_derive);
create_derive!(not_like, Neg, neg_derive);

create_derive!(add_like, Add, add_derive);
create_derive!(add_like, Sub, sub_derive);
create_derive!(add_like, BitAnd, bit_and_derive);
create_derive!(add_like, BitOr, bit_or_derive);
create_derive!(add_like, BitXor, bit_xor_derive);

create_derive!(mul_like, Mul, mul_derive);
create_derive!(mul_like, Div, div_derive);
create_derive!(mul_like, Rem, rem_derive);
create_derive!(mul_like, Shr, shr_derive);
create_derive!(mul_like, Shl, shl_derive);

create_derive!(add_assign_like, AddAssign, add_assign_derive);
create_derive!(add_assign_like, SubAssign, sub_assign_derive);
create_derive!(add_assign_like, BitAndAssign, bit_and_assign_derive);
create_derive!(add_assign_like, BitOrAssign, bit_or_assign_derive);
create_derive!(add_assign_like, BitXorAssign, bit_xor_assign_derive);

create_derive!(mul_assign_like, MulAssign, mul_assign_derive);
create_derive!(mul_assign_like, DivAssign, div_assign_derive);
create_derive!(mul_assign_like, RemAssign, rem_assign_derive);
create_derive!(mul_assign_like, ShrAssign, shr_assign_derive);
create_derive!(mul_assign_like, ShlAssign, shl_assign_derive);

create_derive!(from_str, FromStr, from_str_derive);

create_derive!(display, Display, display_derive);
create_derive!(display, Binary, binary_derive);
create_derive!(display, Octal, octal_derive);
create_derive!(display, LowerHex, lower_hex_derive);
create_derive!(display, UpperHex, upper_hex_derive);
create_derive!(display, LowerExp, lower_exp_derive);
create_derive!(display, UpperExp, upper_exp_derive);
create_derive!(display, Pointer, pointer_derive);

create_derive!(index, Index, index_derive);
create_derive!(index_mut, IndexMut, index_mut_derive);

create_derive!(try_into, TryInto, try_into_derive);

create_derive!(deref, Deref, deref_derive);
create_derive!(deref_mut, DerefMut, deref_mut_derive);
<|endoftext|>


<commit_before>pub mod color;
pub mod piece;
pub mod square;
pub mod chess_move;
pub mod bitboard;
pub mod castle_rights;
pub mod board;
pub mod magic;
extern crate rand;
#[macro_use]
extern crate lazy_static;

<commit_msg>More travis stuff?  I have no idea what I'm doing...<commit_after>pub mod color;
pub mod piece;
pub mod square;
pub mod chess_move;
pub mod bitboard;
pub mod castle_rights;
pub mod board;
pub mod magic;
extern crate rand;
#[macro_use]
extern crate lazy_static;
#![doc(html_root_url = "https://jordanbray.github.io/chess/")]

<|endoftext|>


<commit_before>#![cfg_attr(unix, feature(old_io, core, old_path))]
#![feature(os, std_misc, path)]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

use std::path::PathBuf;
use std::env;
use std::ffi::{self, OsStr, AsOsStr, OsString};
use std::convert::AsRef;

fn home_dir() -> PathBuf {
    PathBuf::from(env::home_dir().unwrap().as_os_str())
}

fn split_paths<P: ?Sized>(paths: &P) -> Vec<PathBuf>
    where P: AsRef<OsStr>
{
    env::split_paths(paths).map(|x| PathBuf::from(x.as_os_str())).collect()
}

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_DATA_HOME")
        .unwrap_or(home_dir().join(".local/share"))
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> PathBuf {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/usr/local/share:/usr/share".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_DATA_DIRS") {
        Some(paths) => {
            if paths != ffi::OsString::from("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CONFIG_HOME")
        .unwrap_or(home_dir().join(".config"))
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> PathBuf {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/etc/xdg".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_CONFIG_DIRS") {
        Some(paths) => {
            if paths != OsString::from("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CACHE_HOME")
        .unwrap_or(home_dir().join(".cache"))
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> PathBuf {
    get_cache_home_from_env(&env::var_os)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<PathBuf> {
    getenv_path(&env::var_os, "XDG_RUNTIME_DIR")
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(unix)]
pub fn test_runtime_dir(path: &PathBuf) -> Result<(), String> {
    use std::old_path;
    use std::old_io;
    use std::os::unix::prelude::OsStrExt;
    use std::old_io::fs::PathExtensions;
    use std::error::Error;
    // FIXME: https://github.com/rust-lang/rfcs/issues/905
    match old_path::Path::new(path.as_os_str().as_bytes()).stat() {
        Ok(stat) => {
            if stat.perm.intersects(old_io::GROUP_RWX | old_io::OTHER_RWX) {
                Err("Incorrect permissions".to_string())
            } else {
                Ok(())
            }
        },

        Err(error) => Err(error.description().to_string())
    }
}

#[cfg(not(unix))]
pub fn test_runtime_dir(_path: &PathBuf) -> Result<(), String> {
    Ok(())
}

/// Get an environment variable's value as a PathBuf.
fn getenv_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let path = (*get_env_var)(env_var);
    match path {
        Some(path) => {
            let path = PathBuf::from(&path);
            if path.is_absolute() {
                Some(path)
            } else {
                None
            }
        },

        None => None
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::env;
    use std::path::PathBuf;
    use std::ffi::AsOsStr;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = |var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::from("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = |var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::from("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").as_os_str().to_os_string());
        custom_env.insert("XDG_DATA_DIRS", env::join_paths(
                vec![cwd.join("share/data"),cwd.join("local/data")].into_iter()).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").as_os_str().to_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", env::join_paths(
                vec![cwd.join("config"), cwd.join("local/config")].into_iter()).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").as_os_str().to_os_string());

        let f = |var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(super::get_data_dirs_from_env(&f)
                == (super::split_paths(&custom_env["XDG_DATA_DIRS"])));
        assert!(super::get_config_home_from_env(&f)
                == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(super::get_config_dirs_from_env(&f)
                == super::split_paths(&custom_env["XDG_CONFIG_DIRS"]));
        assert!(super::get_cache_home_from_env(&f)
                == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<commit_msg>remove no longer needed features<commit_after>#![cfg_attr(unix, feature(old_io, old_path))]
#![feature(std_misc)]

//! xdg-rs is a utility library to make conforming to the [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

use std::path::PathBuf;
use std::env;
use std::ffi::{self, OsStr, AsOsStr, OsString};
use std::convert::AsRef;

fn home_dir() -> PathBuf {
    PathBuf::from(env::home_dir().unwrap().as_os_str())
}

fn split_paths<P: ?Sized>(paths: &P) -> Vec<PathBuf>
    where P: AsRef<OsStr>
{
    env::split_paths(paths).map(|x| PathBuf::from(x.as_os_str())).collect()
}

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_DATA_HOME")
        .unwrap_or(home_dir().join(".local/share"))
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> PathBuf {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/usr/local/share:/usr/share".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_DATA_DIRS") {
        Some(paths) => {
            if paths != ffi::OsString::from("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CONFIG_HOME")
        .unwrap_or(home_dir().join(".config"))
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> PathBuf {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<F>(get_env_var: &F) -> Vec<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let default_paths = "/etc/xdg".as_os_str().to_os_string();
    let paths = match (*get_env_var)("XDG_CONFIG_DIRS") {
        Some(paths) => {
            if paths != OsString::from("") {
                paths
            } else {
                default_paths
            }
        },
        None => default_paths
    };

    split_paths(&paths)
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<F>(get_env_var: &F) -> PathBuf where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_CACHE_HOME")
        .unwrap_or(home_dir().join(".cache"))
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> PathBuf {
    get_cache_home_from_env(&env::var_os)
}

/// Get $XDG_RUNTIME_DIR if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_PATH``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<F>(get_env_var: &F) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    getenv_path(get_env_var, "XDG_RUNTIME_DIR")
}

pub fn get_runtime_dir() -> Option<PathBuf> {
    getenv_path(&env::var_os, "XDG_RUNTIME_DIR")
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` meets the requirements of the specification
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications, a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(unix)]
pub fn test_runtime_dir(path: &PathBuf) -> Result<(), String> {
    use std::old_path;
    use std::old_io;
    use std::os::unix::prelude::OsStrExt;
    use std::old_io::fs::PathExtensions;
    use std::error::Error;
    // FIXME: https://github.com/rust-lang/rfcs/issues/905
    match old_path::Path::new(path.as_os_str().as_bytes()).stat() {
        Ok(stat) => {
            if stat.perm.intersects(old_io::GROUP_RWX | old_io::OTHER_RWX) {
                Err("Incorrect permissions".to_string())
            } else {
                Ok(())
            }
        },

        Err(error) => Err(error.description().to_string())
    }
}

#[cfg(not(unix))]
pub fn test_runtime_dir(_path: &PathBuf) -> Result<(), String> {
    Ok(())
}

/// Get an environment variable's value as a PathBuf.
fn getenv_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf> where
    F: Fn(&str) -> Option<OsString>
{
    let path = (*get_env_var)(env_var);
    match path {
        Some(path) => {
            let path = PathBuf::from(&path);
            if path.is_absolute() {
                Some(path)
            } else {
                None
            }
        },

        None => None
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::env;
    use std::path::PathBuf;
    use std::ffi::AsOsStr;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = |var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::from("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = |var: &str| { custom_env.get(var).map(|x| x.as_os_str().to_os_string()) };
        assert!(super::get_data_home_from_env(&f)
                == super::home_dir().join(".local/share"));
        assert!(super::get_data_dirs_from_env(&f)
                == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(super::get_config_home_from_env(&f)
                == super::home_dir().join(".config"));
        assert!(super::get_config_dirs_from_env(&f)
                == vec![PathBuf::from("/etc/xdg")]);
        assert!(super::get_cache_home_from_env(&f)
                == super::home_dir().join(".cache"));
        assert!(super::get_runtime_dir_from_env(&f)
                == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").as_os_str().to_os_string());
        custom_env.insert("XDG_DATA_DIRS", env::join_paths(
                vec![cwd.join("share/data"),cwd.join("local/data")].into_iter()).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").as_os_str().to_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", env::join_paths(
                vec![cwd.join("config"), cwd.join("local/config")].into_iter()).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").as_os_str().to_os_string());

        let f = |var: &str| { custom_env.get(var).map(|x| x.clone()) };
        assert!(super::get_data_home_from_env(&f)
                == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(super::get_data_dirs_from_env(&f)
                == (super::split_paths(&custom_env["XDG_DATA_DIRS"])));
        assert!(super::get_config_home_from_env(&f)
                == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(super::get_config_dirs_from_env(&f)
                == super::split_paths(&custom_env["XDG_CONFIG_DIRS"]));
        assert!(super::get_cache_home_from_env(&f)
                == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<|endoftext|>