<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Reference(u64),
    Absolute(u64),
    Extend(u64),
    Reduce(u64),
    AtMost(u64),
    AtLeast(u64),
    RoundDown(u64),
    RoundUp(u64),
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
}

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            truncate(no_create, io_blocks, reference, size, files);
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) {
    let mode = match size {
        Some(size_string) => match parse_mode_and_size(&size_string) {
            Ok(m) => m,
            Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
        },
        None => TruncateMode::Reference(0),
    };

    let refsize = match reference {
        Some(ref rfilename) => {
            match mode {
                // Only Some modes work with a reference
                TruncateMode::Reference(_) => (), //No --size was given
                TruncateMode::Extend(_) => (),
                TruncateMode::Reduce(_) => (),
                _ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
            };
            match metadata(rfilename) {
                Ok(meta) => meta.len(),
                Err(f) => match f.kind() {
                    ErrorKind::NotFound => {
                        crash!(1, "cannot stat '{}': No such file or directory", rfilename)
                    }
                    _ => crash!(1, "{}", f.to_string()),
                },
            }
        }
        None => 0,
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new().write(true).create(!no_create).open(path) {
            Ok(file) => {
                let fsize = match reference {
                    Some(_) => refsize,
                    None => match metadata(filename) {
                        Ok(meta) => meta.len(),
                        Err(f) => {
                            show_warning!("{}", f.to_string());
                            continue;
                        }
                    },
                };
                let tsize: u64 = match mode {
                    TruncateMode::Absolute(modsize) => modsize,
                    TruncateMode::Reference(_) => fsize,
                    TruncateMode::Extend(modsize) => fsize + modsize,
                    TruncateMode::Reduce(modsize) => fsize - modsize,
                    TruncateMode::AtMost(modsize) => fsize.min(modsize),
                    TruncateMode::AtLeast(modsize) => fsize.max(modsize),
                    TruncateMode::RoundDown(modsize) => fsize - fsize % modsize,
                    TruncateMode::RoundUp(modsize) => fsize + fsize % modsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
    // Trim any whitespace.
    let size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    let c = size_string.chars().next().unwrap();
    let size_string = if is_modifier(c) {
        &size_string[1..]
    } else {
        size_string
    };
    parse_size(size_string).map(match c {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '%' => TruncateMode::RoundUp,
        _ => TruncateMode::Absolute,
    })
}

/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z, or Y (powers of 1024) or KB, MB,
/// etc. (powers of 1000).
///
/// # Errors
///
/// This function returns an error if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_size("123").unwrap(), 123);
/// assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
/// assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
/// ```
fn parse_size(size: &str) -> Result<u64, ()> {
    // Get the numeric part of the size argument. For example, if the
    // argument is "123K", then the numeric part is "123".
    let numeric_string: String = size.chars().take_while(|c| c.is_digit(10)).collect();
    let number: u64 = match numeric_string.parse() {
        Ok(n) => n,
        Err(_) => return Err(()),
    };

    // Get the alphabetic units part of the size argument and compute
    // the factor it represents. For example, if the argument is "123K",
    // then the unit part is "K" and the factor is 1024. This may be the
    // empty string, in which case, the factor is 1.
    let n = numeric_string.len();
    let (base, exponent): (u64, u32) = match &size[n..] {
        "" => (1, 0),
        "K" | "k" => (1024, 1),
        "M" | "m" => (1024, 2),
        "G" | "g" => (1024, 3),
        "T" | "t" => (1024, 4),
        "P" | "p" => (1024, 5),
        "E" | "e" => (1024, 6),
        "Z" | "z" => (1024, 7),
        "Y" | "y" => (1024, 8),
        "KB" | "kB" => (1000, 1),
        "MB" | "mB" => (1000, 2),
        "GB" | "gB" => (1000, 3),
        "TB" | "tB" => (1000, 4),
        "PB" | "pB" => (1000, 5),
        "EB" | "eB" => (1000, 6),
        "ZB" | "zB" => (1000, 7),
        "YB" | "yB" => (1000, 8),
        _ => return Err(()),
    };
    let factor = base.pow(exponent);
    Ok(number * factor)
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::parse_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_size_zero() {
        assert_eq!(parse_size("0").unwrap(), 0);
        assert_eq!(parse_size("0K").unwrap(), 0);
        assert_eq!(parse_size("0KB").unwrap(), 0);
    }

    #[test]
    fn test_parse_size_without_factor() {
        assert_eq!(parse_size("123").unwrap(), 123);
    }

    #[test]
    fn test_parse_size_kilobytes() {
        assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
        assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
    }

    #[test]
    fn test_parse_size_megabytes() {
        assert_eq!(parse_size("123").unwrap(), 123);
        assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
        assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
    }

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<commit_msg>truncate: create TruncateMode::to_size() method<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Reference(u64),
    Absolute(u64),
    Extend(u64),
    Reduce(u64),
    AtMost(u64),
    AtLeast(u64),
    RoundDown(u64),
    RoundUp(u64),
}

impl TruncateMode {
    /// Compute a target size in bytes for this truncate mode.
    ///
    /// `fsize` is the size of the reference file, in bytes.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let mode = TruncateMode::Extend(5);
    /// let fsize = 10;
    /// assert_eq!(mode.to_size(fsize), 15);
    /// ```
    fn to_size(&self, fsize: u64) -> u64 {
        match self {
            TruncateMode::Absolute(modsize) => *modsize,
            TruncateMode::Reference(_) => fsize,
            TruncateMode::Extend(modsize) => fsize + modsize,
            TruncateMode::Reduce(modsize) => fsize - modsize,
            TruncateMode::AtMost(modsize) => fsize.min(*modsize),
            TruncateMode::AtLeast(modsize) => fsize.max(*modsize),
            TruncateMode::RoundDown(modsize) => fsize - fsize % modsize,
            TruncateMode::RoundUp(modsize) => fsize + fsize % modsize,
        }
    }
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
}

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            truncate(no_create, io_blocks, reference, size, files);
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) {
    let mode = match size {
        Some(size_string) => match parse_mode_and_size(&size_string) {
            Ok(m) => m,
            Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
        },
        None => TruncateMode::Reference(0),
    };

    let refsize = match reference {
        Some(ref rfilename) => {
            match mode {
                // Only Some modes work with a reference
                TruncateMode::Reference(_) => (), //No --size was given
                TruncateMode::Extend(_) => (),
                TruncateMode::Reduce(_) => (),
                _ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
            };
            match metadata(rfilename) {
                Ok(meta) => meta.len(),
                Err(f) => match f.kind() {
                    ErrorKind::NotFound => {
                        crash!(1, "cannot stat '{}': No such file or directory", rfilename)
                    }
                    _ => crash!(1, "{}", f.to_string()),
                },
            }
        }
        None => 0,
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new().write(true).create(!no_create).open(path) {
            Ok(file) => {
                let fsize = match reference {
                    Some(_) => refsize,
                    None => match metadata(filename) {
                        Ok(meta) => meta.len(),
                        Err(f) => {
                            show_warning!("{}", f.to_string());
                            continue;
                        }
                    },
                };
                let tsize = mode.to_size(fsize);
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
    // Trim any whitespace.
    let size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    let c = size_string.chars().next().unwrap();
    let size_string = if is_modifier(c) {
        &size_string[1..]
    } else {
        size_string
    };
    parse_size(size_string).map(match c {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '%' => TruncateMode::RoundUp,
        _ => TruncateMode::Absolute,
    })
}

/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z, or Y (powers of 1024) or KB, MB,
/// etc. (powers of 1000).
///
/// # Errors
///
/// This function returns an error if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_size("123").unwrap(), 123);
/// assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
/// assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
/// ```
fn parse_size(size: &str) -> Result<u64, ()> {
    // Get the numeric part of the size argument. For example, if the
    // argument is "123K", then the numeric part is "123".
    let numeric_string: String = size.chars().take_while(|c| c.is_digit(10)).collect();
    let number: u64 = match numeric_string.parse() {
        Ok(n) => n,
        Err(_) => return Err(()),
    };

    // Get the alphabetic units part of the size argument and compute
    // the factor it represents. For example, if the argument is "123K",
    // then the unit part is "K" and the factor is 1024. This may be the
    // empty string, in which case, the factor is 1.
    let n = numeric_string.len();
    let (base, exponent): (u64, u32) = match &size[n..] {
        "" => (1, 0),
        "K" | "k" => (1024, 1),
        "M" | "m" => (1024, 2),
        "G" | "g" => (1024, 3),
        "T" | "t" => (1024, 4),
        "P" | "p" => (1024, 5),
        "E" | "e" => (1024, 6),
        "Z" | "z" => (1024, 7),
        "Y" | "y" => (1024, 8),
        "KB" | "kB" => (1000, 1),
        "MB" | "mB" => (1000, 2),
        "GB" | "gB" => (1000, 3),
        "TB" | "tB" => (1000, 4),
        "PB" | "pB" => (1000, 5),
        "EB" | "eB" => (1000, 6),
        "ZB" | "zB" => (1000, 7),
        "YB" | "yB" => (1000, 8),
        _ => return Err(()),
    };
    let factor = base.pow(exponent);
    Ok(number * factor)
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::parse_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_size_zero() {
        assert_eq!(parse_size("0").unwrap(), 0);
        assert_eq!(parse_size("0K").unwrap(), 0);
        assert_eq!(parse_size("0KB").unwrap(), 0);
    }

    #[test]
    fn test_parse_size_without_factor() {
        assert_eq!(parse_size("123").unwrap(), 123);
    }

    #[test]
    fn test_parse_size_kilobytes() {
        assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
        assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
    }

    #[test]
    fn test_parse_size_megabytes() {
        assert_eq!(parse_size("123").unwrap(), 123);
        assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
        assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
    }

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<|endoftext|>


<commit_before>extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;
extern mod noise;

use std::cast;
use std::ptr;
use std::hashmap::HashMap;
use std;

use gl::types::*;

use cgmath::vector::Vector;
use cgmath::vector::Vec3;
use cgmath::vector::Vec4;

use noise::Perlin;

use CHUNK_SIZE;
use WORLD_SIZE;
use GraphicsResources;

static MAX_CHUNKS : uint = 32;

pub struct ChunkLoader<'a> {
    seed : u32,
    graphics_resources : &'a GraphicsResources,
    cache : HashMap<(i64, i64), ~Chunk>
}

impl<'a> ChunkLoader<'a> {
    pub fn new<'a>(seed : u32, graphics_resources : &'a GraphicsResources) -> ChunkLoader<'a> {
        ChunkLoader {
            seed: seed,
            graphics_resources : graphics_resources,
            cache: HashMap::new(),
        }
    }

    pub fn update(&mut self, x : i64, z: i64) {
        let mask : i64 = !(CHUNK_SIZE as i64 - 1);
        let mut loaded = false;

        for ix in std::iter::range_inclusive(-(WORLD_SIZE as i64)/2, (WORLD_SIZE as i64)/2) {
            for iz in std::iter::range_inclusive(-(WORLD_SIZE as i64)/2, (WORLD_SIZE as i64)/2) {
                let cx : i64 = (x & mask) + ix*CHUNK_SIZE as i64;
                let cz : i64 = (z & mask) + iz*CHUNK_SIZE as i64;
                if !self.cache.contains_key(&(cx, cz)) {
                    if !loaded {
                        println!("loading chunk ({}, {})", cx, cz);
                        let chunk = chunk_gen(self.graphics_resources, self.seed, cx, cz);
                        self.cache.insert((cx, cz), chunk);
                        loaded = true;
                    }
                } else {
                    let chunk = self.cache.find_mut(&(cx, cz)).unwrap();
                    chunk.touch();
                }
            }
        }

        while self.cache.len() > MAX_CHUNKS {
            let (&k, _) = self.cache.iter().min_by(|&(_, chunk)| chunk.used_time).unwrap();
            self.cache.remove(&k);
        }
    }
}

pub struct Chunk {
    x: i64,
    z: i64,
    map: ~Map,
    vao: GLuint,
    vertex_buffer: GLuint,
    normal_buffer: GLuint,
    element_buffer: GLuint,
    num_elements: uint,
    used_time: u64,
}

impl Chunk {
    fn touch(&mut self) {
        self.used_time = extra::time::precise_time_ns();
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        unsafe {
            println!("unloading chunk ({}, {})", self.x, self.z);
            gl::DeleteBuffers(1, &self.vertex_buffer);
            gl::DeleteBuffers(1, &self.normal_buffer);
            gl::DeleteBuffers(1, &self.element_buffer);
            gl::DeleteVertexArrays(1, &self.vao);
        }
    }
}

struct Block {
    visible: bool,
}

struct Map {
    blocks: [[[Block, ..CHUNK_SIZE], ..CHUNK_SIZE], ..CHUNK_SIZE],
}

struct Face {
    normal: Vec3<f32>,
    vertices: [Vec3<f32>, ..4],
}

pub fn chunk_gen(res: &GraphicsResources, seed: u32, chunk_x: i64, chunk_z: i64) -> ~Chunk {
    let def_block = Block { visible: false };
    let mut map = ~Map {
        blocks: [[[def_block, ..CHUNK_SIZE], ..CHUNK_SIZE], ..CHUNK_SIZE],
    };

    let block_exists = |x: int, y: int, z: int| -> bool {
        if x < 0 || x >= CHUNK_SIZE as int || y < 0 || y >= CHUNK_SIZE as int || z < 0 || z >= CHUNK_SIZE as int {
            return false;
        }

        map.blocks[x][y][z].visible
    };

    let perlin = Perlin::from_seed([seed as uint]);

    for block_x in range(0, CHUNK_SIZE) {
        for block_z in range(0, CHUNK_SIZE) {
            let noise = perlin.gen([
                (chunk_x + block_x as i64) as f64 * 0.1,
                (chunk_z + block_z as i64) as f64 * 0.1
            ]);
            let height = ((noise + 1.0) * (CHUNK_SIZE as f64 / 8.0)) as uint;
            for y in range(0, height) {
                map.blocks[block_x][y][block_z] = Block { visible: true };
            }
        }
    }

    let mut vertices : ~[Vec3<f32>] = ~[];
    let mut normals : ~[Vec3<f32>] = ~[];
    let mut elements : ~[GLuint] = ~[];

    let mut idx = 0;

    for x in range(0, CHUNK_SIZE) {
        for y in range(0, CHUNK_SIZE) {
            for z in range(0, CHUNK_SIZE) {
                let block = &map.blocks[x][y][z];

                if (!block.visible) {
                    continue;
                }

                let block_position = Vec3 { x: x as f32, y: y as f32, z: z as f32 };

                for face in faces.iter() {
                    let neighbor_position = block_position.add_v(&face.normal);
                    if block_exists(neighbor_position.x as int, neighbor_position.y as int, neighbor_position.z as int) {
                        continue;
                    }

                    for v in face.vertices.iter() {
                        vertices.push(v.add_v(&block_position));
                        normals.push(face.normal);
                    }

                    for e in face_elements.iter() {
                        elements.push((idx * face.vertices.len()) as GLuint + *e);
                    }

                    idx += 1;
                }
            }
        }
    }

    let mut vao = 0;
    let mut vertex_buffer = 0;
    let mut normal_buffer = 0;
    let mut element_buffer = 0;

    unsafe {
        // Create Vertex Array Object
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a Vertex Buffer Object and copy the vertex data to it
        gl::GenBuffers(1, &mut vertex_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
        gl::BufferData(gl::ARRAY_BUFFER,
                        (vertices.len() * std::mem::size_of::<Vec3<f32>>()) as GLsizeiptr,
                        cast::transmute(&vertices[0]),
                        gl::STATIC_DRAW);

        // Create a Vertex Buffer Object and copy the normal data to it
        gl::GenBuffers(1, &mut normal_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, normal_buffer);
        gl::BufferData(gl::ARRAY_BUFFER,
                        (normals.len() * std::mem::size_of::<Vec3<f32>>()) as GLsizeiptr,
                        cast::transmute(&normals[0]),
                        gl::STATIC_DRAW);

        // Create a Vertex Buffer Object and copy the element data to it
        gl::GenBuffers(1, &mut element_buffer);
        gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, element_buffer);
        gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                        (elements.len() * std::mem::size_of::<GLuint>()) as GLsizeiptr,
                        cast::transmute(&elements[0]),
                        gl::STATIC_DRAW);

        // Specify the layout of the vertex data
        let vert_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(res.program, ptr));
        assert!(vert_attr as u32 != gl::INVALID_VALUE);
        gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
        gl::EnableVertexAttribArray(vert_attr as GLuint);
        gl::VertexAttribPointer(vert_attr as GLuint, 3, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());

        let normal_attr = "normal".with_c_str(|ptr| gl::GetAttribLocation(res.program, ptr));
        assert!(normal_attr as u32 != gl::INVALID_VALUE);
        gl::BindBuffer(gl::ARRAY_BUFFER, normal_buffer);
        gl::EnableVertexAttribArray(normal_attr as GLuint);
        gl::VertexAttribPointer(normal_attr as GLuint, 3, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());
    }

    gl::BindVertexArray(0);

    return ~Chunk {
        x: chunk_x,
        z: chunk_z,
        map: map,
        vao: vao,
        vertex_buffer: vertex_buffer,
        normal_buffer: normal_buffer,
        element_buffer: element_buffer,
        num_elements: elements.len(),
        used_time: extra::time::precise_time_ns(),
    };
}

static face_elements : [GLuint, ..6] = [
    0, 1, 2, 3, 2, 1,
];

static faces : [Face, ..6] = [
    /* front */
    Face {
        normal: Vec3 { x: 0.0, y: 0.0, z: 1.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* bottom left */
            Vec3 { x: 1.0, y: 0.0, z: 1.0 },  /* bottom right */
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* top left */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 },  /* top right */
        ],
    },

    /* back */
    Face {
        normal: Vec3 { x: 0.0, y: 0.0, z: -1.0 },
        vertices: [
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* bottom right */
            Vec3 { x: 0.0, y: 0.0, z: 0.0 },  /* bottom left */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* top right */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 },  /* top left */
        ],
    },

    /* right */
    Face {
        normal: Vec3 { x: 1.0, y: 0.0, z: 0.0 },
        vertices: [
            Vec3 { x: 1.0, y: 0.0, z: 1.0 }, /* bottom front */
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* bottom back */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 }, /* top front */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* top back */
        ],
    },

    /* left */
    Face {
        normal: Vec3 { x: -1.0, y: 0.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 0.0 }, /* bottom back */
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* bottom front */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 }, /* top back */
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* top front */
        ],
    },

    /* top */
    Face {
        normal: Vec3 { x: 0.0, y: 1.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* front left */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 }, /* front right */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 }, /* back left */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* back right */
        ],
    },

    /* bottom */
    Face {
        normal: Vec3 { x: 0.0, y: -1.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 0.0 }, /* back left */
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* back right */
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* front left */
            Vec3 { x: 1.0, y: 0.0, z: 1.0 }, /* front right */
        ],
    },
];
<commit_msg>profile chunk loading<commit_after>extern mod extra;
extern mod glfw;
extern mod gl;
extern mod cgmath;
extern mod noise;

use std::cast;
use std::ptr;
use std::hashmap::HashMap;
use std;

use gl::types::*;

use cgmath::vector::Vector;
use cgmath::vector::Vec3;
use cgmath::vector::Vec4;

use noise::Perlin;

use CHUNK_SIZE;
use WORLD_SIZE;
use GraphicsResources;

static MAX_CHUNKS : uint = 32;

pub struct ChunkLoader<'a> {
    seed : u32,
    graphics_resources : &'a GraphicsResources,
    cache : HashMap<(i64, i64), ~Chunk>
}

impl<'a> ChunkLoader<'a> {
    pub fn new<'a>(seed : u32, graphics_resources : &'a GraphicsResources) -> ChunkLoader<'a> {
        ChunkLoader {
            seed: seed,
            graphics_resources : graphics_resources,
            cache: HashMap::new(),
        }
    }

    pub fn update(&mut self, x : i64, z: i64) {
        let mask : i64 = !(CHUNK_SIZE as i64 - 1);
        let mut loaded = false;

        for ix in std::iter::range_inclusive(-(WORLD_SIZE as i64)/2, (WORLD_SIZE as i64)/2) {
            for iz in std::iter::range_inclusive(-(WORLD_SIZE as i64)/2, (WORLD_SIZE as i64)/2) {
                let cx : i64 = (x & mask) + ix*CHUNK_SIZE as i64;
                let cz : i64 = (z & mask) + iz*CHUNK_SIZE as i64;
                if !self.cache.contains_key(&(cx, cz)) {
                    if !loaded {
                        println!("loading chunk ({}, {})", cx, cz);
                        let chunk = chunk_gen(self.graphics_resources, self.seed, cx, cz);
                        self.cache.insert((cx, cz), chunk);
                        loaded = true;
                    }
                } else {
                    let chunk = self.cache.find_mut(&(cx, cz)).unwrap();
                    chunk.touch();
                }
            }
        }

        while self.cache.len() > MAX_CHUNKS {
            let (&k, _) = self.cache.iter().min_by(|&(_, chunk)| chunk.used_time).unwrap();
            self.cache.remove(&k);
        }
    }
}

pub struct Chunk {
    x: i64,
    z: i64,
    map: ~Map,
    vao: GLuint,
    vertex_buffer: GLuint,
    normal_buffer: GLuint,
    element_buffer: GLuint,
    num_elements: uint,
    used_time: u64,
}

impl Chunk {
    fn touch(&mut self) {
        self.used_time = extra::time::precise_time_ns();
    }
}

impl Drop for Chunk {
    fn drop(&mut self) {
        unsafe {
            println!("unloading chunk ({}, {})", self.x, self.z);
            gl::DeleteBuffers(1, &self.vertex_buffer);
            gl::DeleteBuffers(1, &self.normal_buffer);
            gl::DeleteBuffers(1, &self.element_buffer);
            gl::DeleteVertexArrays(1, &self.vao);
        }
    }
}

struct Block {
    visible: bool,
}

struct Map {
    blocks: [[[Block, ..CHUNK_SIZE], ..CHUNK_SIZE], ..CHUNK_SIZE],
}

struct Face {
    normal: Vec3<f32>,
    vertices: [Vec3<f32>, ..4],
}

pub fn chunk_gen(res: &GraphicsResources, seed: u32, chunk_x: i64, chunk_z: i64) -> ~Chunk {
    let def_block = Block { visible: false };
    let mut map = ~Map {
        blocks: [[[def_block, ..CHUNK_SIZE], ..CHUNK_SIZE], ..CHUNK_SIZE],
    };

    let block_exists = |x: int, y: int, z: int| -> bool {
        if x < 0 || x >= CHUNK_SIZE as int || y < 0 || y >= CHUNK_SIZE as int || z < 0 || z >= CHUNK_SIZE as int {
            return false;
        }

        map.blocks[x][y][z].visible
    };

    let start_time = extra::time::precise_time_ns();

    let perlin = Perlin::from_seed([seed as uint]);

    for block_x in range(0, CHUNK_SIZE) {
        for block_z in range(0, CHUNK_SIZE) {
            let noise = perlin.gen([
                (chunk_x + block_x as i64) as f64 * 0.1,
                (chunk_z + block_z as i64) as f64 * 0.1
            ]);
            let height = ((noise + 1.0) * (CHUNK_SIZE as f64 / 8.0)) as uint;
            for y in range(0, height) {
                map.blocks[block_x][y][block_z] = Block { visible: true };
            }
        }
    }

    let after_noise_time = extra::time::precise_time_ns();

    let mut vertices : ~[Vec3<f32>] = ~[];
    let mut normals : ~[Vec3<f32>] = ~[];
    let mut elements : ~[GLuint] = ~[];

    let mut idx = 0;

    for x in range(0, CHUNK_SIZE) {
        for y in range(0, CHUNK_SIZE) {
            for z in range(0, CHUNK_SIZE) {
                let block = &map.blocks[x][y][z];

                if (!block.visible) {
                    continue;
                }

                let block_position = Vec3 { x: x as f32, y: y as f32, z: z as f32 };

                for face in faces.iter() {
                    let neighbor_position = block_position.add_v(&face.normal);
                    if block_exists(neighbor_position.x as int, neighbor_position.y as int, neighbor_position.z as int) {
                        continue;
                    }

                    for v in face.vertices.iter() {
                        vertices.push(v.add_v(&block_position));
                        normals.push(face.normal);
                    }

                    for e in face_elements.iter() {
                        elements.push((idx * face.vertices.len()) as GLuint + *e);
                    }

                    idx += 1;
                }
            }
        }
    }

    let after_mesh_time = extra::time::precise_time_ns();

    let mut vao = 0;
    let mut vertex_buffer = 0;
    let mut normal_buffer = 0;
    let mut element_buffer = 0;

    unsafe {
        // Create Vertex Array Object
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a Vertex Buffer Object and copy the vertex data to it
        gl::GenBuffers(1, &mut vertex_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
        gl::BufferData(gl::ARRAY_BUFFER,
                        (vertices.len() * std::mem::size_of::<Vec3<f32>>()) as GLsizeiptr,
                        cast::transmute(&vertices[0]),
                        gl::STATIC_DRAW);

        // Create a Vertex Buffer Object and copy the normal data to it
        gl::GenBuffers(1, &mut normal_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, normal_buffer);
        gl::BufferData(gl::ARRAY_BUFFER,
                        (normals.len() * std::mem::size_of::<Vec3<f32>>()) as GLsizeiptr,
                        cast::transmute(&normals[0]),
                        gl::STATIC_DRAW);

        // Create a Vertex Buffer Object and copy the element data to it
        gl::GenBuffers(1, &mut element_buffer);
        gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, element_buffer);
        gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                        (elements.len() * std::mem::size_of::<GLuint>()) as GLsizeiptr,
                        cast::transmute(&elements[0]),
                        gl::STATIC_DRAW);

        // Specify the layout of the vertex data
        let vert_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(res.program, ptr));
        assert!(vert_attr as u32 != gl::INVALID_VALUE);
        gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
        gl::EnableVertexAttribArray(vert_attr as GLuint);
        gl::VertexAttribPointer(vert_attr as GLuint, 3, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());

        let normal_attr = "normal".with_c_str(|ptr| gl::GetAttribLocation(res.program, ptr));
        assert!(normal_attr as u32 != gl::INVALID_VALUE);
        gl::BindBuffer(gl::ARRAY_BUFFER, normal_buffer);
        gl::EnableVertexAttribArray(normal_attr as GLuint);
        gl::VertexAttribPointer(normal_attr as GLuint, 3, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());
    }

    gl::BindVertexArray(0);

    let after_buffer_time = extra::time::precise_time_ns();

    println!("chunk loading profile (us): noise={} mesh={} buffer={}",
             (after_noise_time - start_time)/1000,
             (after_mesh_time - after_noise_time)/1000,
             (after_buffer_time - after_mesh_time)/1000)

    return ~Chunk {
        x: chunk_x,
        z: chunk_z,
        map: map,
        vao: vao,
        vertex_buffer: vertex_buffer,
        normal_buffer: normal_buffer,
        element_buffer: element_buffer,
        num_elements: elements.len(),
        used_time: extra::time::precise_time_ns(),
    };
}

static face_elements : [GLuint, ..6] = [
    0, 1, 2, 3, 2, 1,
];

static faces : [Face, ..6] = [
    /* front */
    Face {
        normal: Vec3 { x: 0.0, y: 0.0, z: 1.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* bottom left */
            Vec3 { x: 1.0, y: 0.0, z: 1.0 },  /* bottom right */
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* top left */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 },  /* top right */
        ],
    },

    /* back */
    Face {
        normal: Vec3 { x: 0.0, y: 0.0, z: -1.0 },
        vertices: [
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* bottom right */
            Vec3 { x: 0.0, y: 0.0, z: 0.0 },  /* bottom left */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* top right */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 },  /* top left */
        ],
    },

    /* right */
    Face {
        normal: Vec3 { x: 1.0, y: 0.0, z: 0.0 },
        vertices: [
            Vec3 { x: 1.0, y: 0.0, z: 1.0 }, /* bottom front */
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* bottom back */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 }, /* top front */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* top back */
        ],
    },

    /* left */
    Face {
        normal: Vec3 { x: -1.0, y: 0.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 0.0 }, /* bottom back */
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* bottom front */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 }, /* top back */
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* top front */
        ],
    },

    /* top */
    Face {
        normal: Vec3 { x: 0.0, y: 1.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 1.0, z: 1.0 }, /* front left */
            Vec3 { x: 1.0, y: 1.0, z: 1.0 }, /* front right */
            Vec3 { x: 0.0, y: 1.0, z: 0.0 }, /* back left */
            Vec3 { x: 1.0, y: 1.0, z: 0.0 }, /* back right */
        ],
    },

    /* bottom */
    Face {
        normal: Vec3 { x: 0.0, y: -1.0, z: 0.0 },
        vertices: [
            Vec3 { x: 0.0, y: 0.0, z: 0.0 }, /* back left */
            Vec3 { x: 1.0, y: 0.0, z: 0.0 }, /* back right */
            Vec3 { x: 0.0, y: 0.0, z: 1.0 }, /* front left */
            Vec3 { x: 1.0, y: 0.0, z: 1.0 }, /* front right */
        ],
    },
];
<|endoftext|>


<commit_before>use std::boxed::Box;
use std::cell::UnsafeCell;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

use {Async, Future, Poll};
use task::{self, Task};

/// A type of futures-powered synchronization primitive which is a mutex between
/// two possible owners.
///
/// This primitive is not as generic as a full-blown mutex but is sufficient for
/// many use cases where there are only two possible owners of a resource. The
/// implementation of `BiLock` can be more optimized for just the two possible
/// owners.
///
/// Note that it's possible to use this lock through a poll-style interface with
/// the `poll_lock` method but you can also use it as a future with the `lock`
/// method that consumes a `BiLock` and returns a future that will resolve when
/// it's locked.
///
/// A `BiLock` is typically used for "split" operations where data which serves
/// two purposes wants to be split into two to be worked with separately. For
/// example a TCP stream could be both a reader and a writer or a framing layer
/// could be both a stream and a sink for messages. A `BiLock` enables splitting
/// these two and then using each independently in a futures-powered fashion.
#[derive(Debug)]
pub struct BiLock<T> {
    inner: Arc<Inner<T>>,
}

#[derive(Debug)]
struct Inner<T> {
    state: AtomicUsize,
    inner: UnsafeCell<T>,
}

unsafe impl<T: Send> Send for Inner<T> {}
unsafe impl<T: Send> Sync for Inner<T> {}

impl<T> BiLock<T> {
    /// Creates a new `BiLock` protecting the provided data.
    ///
    /// Two handles to the lock are returned, and these are the only two handles
    /// that will ever be available to the lock. These can then be sent to separate
    /// tasks to be managed there.
    pub fn new(t: T) -> (BiLock<T>, BiLock<T>) {
        let inner = Arc::new(Inner {
            state: AtomicUsize::new(0),
            inner: UnsafeCell::new(t),
        });

        (BiLock { inner: inner.clone() }, BiLock { inner: inner })
    }

    /// Attempt to acquire this lock, returning `NotReady` if it can't be
    /// acquired.
    ///
    /// This function will acquire the lock in a nonblocking fashion, returning
    /// immediately if the lock is already held. If the lock is successfully
    /// acquired then `Async::Ready` is returned with a value that represents
    /// the locked value (and can be used to access the protected data). The
    /// lock is unlocked when the returned `BiLockGuard` is dropped.
    ///
    /// If the lock is already held then this function will return
    /// `Async::NotReady`. In this case the current task will also be scheduled
    /// to receive a notification when the lock would otherwise become
    /// available.
    ///
    /// # Panics
    ///
    /// This function will panic if called outside the context of a future's
    /// task.
    pub fn poll_lock(&self) -> Async<BiLockGuard<T>> {
        loop {
            match self.inner.state.swap(1, SeqCst) {
                // Woohoo, we grabbed the lock!
                0 => return Async::Ready(BiLockGuard { inner: self }),

                // Oops, someone else has locked the lock
                1 => {}

                // A task was previously blocked on this lock, likely our task,
                // so we need to update that task.
                n => unsafe {
                    drop(Box::from_raw(n as *mut Task));
                }
            }

            let me = Box::new(task::current());
            let me = Box::into_raw(me) as usize;

            match self.inner.state.compare_exchange(1, me, SeqCst, SeqCst) {
                // The lock is still locked, but we've now parked ourselves, so
                // just report that we're scheduled to receive a notification.
                Ok(_) => return Async::NotReady,

                // Oops, looks like the lock was unlocked after our swap above
                // and before the compare_exchange. Deallocate what we just
                // allocated and go through the loop again.
                Err(0) => unsafe {
                    drop(Box::from_raw(me as *mut Task));
                },

                // The top of this loop set the previous state to 1, so if we
                // failed the CAS above then it's because the previous value was
                // *not* zero or one. This indicates that a task was blocked,
                // but we're trying to acquire the lock and there's only one
                // other reference of the lock, so it should be impossible for
                // that task to ever block itself.
                Err(n) => panic!("invalid state: {}", n),
            }
        }
    }

    /// Perform a "blocking lock" of this lock, consuming this lock handle and
    /// returning a future to the acquired lock.
    ///
    /// This function consumes the `BiLock<T>` and returns a sentinel future,
    /// `BiLockAcquire<T>`. The returned future will resolve to
    /// `BiLockAcquired<T>` which represents a locked lock similarly to
    /// `BiLockGuard<T>`.
    ///
    /// Note that the returned future will never resolve to an error.
    pub fn lock(self) -> BiLockAcquire<T> {
        BiLockAcquire {
            inner: self,
        }
    }

    fn unlock(&self) {
        match self.inner.state.swap(0, SeqCst) {
            // we've locked the lock, shouldn't be possible for us to see an
            // unlocked lock.
            0 => panic!("invalid unlocked state"),

            // Ok, no one else tried to get the lock, we're done.
            1 => {}

            // Another task has parked themselves on this lock, let's wake them
            // up as its now their turn.
            n => unsafe {
                Box::from_raw(n as *mut Task).notify();
            }
        }
    }
}

impl<T> Drop for Inner<T> {
    fn drop(&mut self) {
        assert_eq!(self.state.load(SeqCst), 0);
    }
}

/// Returned RAII guard from the `poll_lock` method.
///
/// This structure acts as a sentinel to the data in the `BiLock<T>` itself,
/// implementing `Deref` and `DerefMut` to `T`. When dropped, the lock will be
/// unlocked.
#[derive(Debug)]
pub struct BiLockGuard<'a, T: 'a> {
    inner: &'a BiLock<T>,
}

impl<'a, T> Deref for BiLockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.inner.inner.inner.get() }
    }
}

impl<'a, T> DerefMut for BiLockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.inner.inner.inner.get() }
    }
}

impl<'a, T> Drop for BiLockGuard<'a, T> {
    fn drop(&mut self) {
        self.inner.unlock();
    }
}

/// Future returned by `BiLock::lock` which will resolve when the lock is
/// acquired.
#[derive(Debug)]
pub struct BiLockAcquire<T> {
    inner: BiLock<T>,
}

impl<T> Future for BiLockAcquire<T> {
    type Item = BiLockAcquired<T>;
    type Error = ();

    fn poll(&mut self) -> Poll<BiLockAcquired<T>, ()> {
        match self.inner.poll_lock() {
            Async::Ready(r) => {
                mem::forget(r);
                Ok(BiLockAcquired {
                    inner: BiLock { inner: self.inner.inner.clone() },
                }.into())
            }
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}

/// Resolved value of the `BiLockAcquire<T>` future.
///
/// This value, like `BiLockGuard<T>`, is a sentinel to the value `T` through
/// implementations of `Deref` and `DerefMut`. When dropped will unlock the
/// lock, and the original unlocked `BiLock<T>` can be recovered through the
/// `unlock` method.
#[derive(Debug)]
pub struct BiLockAcquired<T> {
    inner: BiLock<T>,
}

impl<T> BiLockAcquired<T> {
    /// Recovers the original `BiLock<T>`, unlocking this lock.
    pub fn unlock(mut self) -> BiLock<T> {
        let bi_lock = mem::replace(&mut self.inner, unsafe { mem::uninitialized() });

        mem::forget(self);

        bi_lock.unlock();

        bi_lock
    }
}

impl<T> Deref for BiLockAcquired<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.inner.inner.inner.get() }
    }
}

impl<T> DerefMut for BiLockAcquired<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.inner.inner.inner.get() }
    }
}

impl<T> Drop for BiLockAcquired<T> {
    fn drop(&mut self) {
        self.inner.unlock();
    }
}
<commit_msg>Unnecessary atomic operations in BiLockAcquire.poll<commit_after>use std::boxed::Box;
use std::cell::UnsafeCell;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

use {Async, Future, Poll};
use task::{self, Task};

/// A type of futures-powered synchronization primitive which is a mutex between
/// two possible owners.
///
/// This primitive is not as generic as a full-blown mutex but is sufficient for
/// many use cases where there are only two possible owners of a resource. The
/// implementation of `BiLock` can be more optimized for just the two possible
/// owners.
///
/// Note that it's possible to use this lock through a poll-style interface with
/// the `poll_lock` method but you can also use it as a future with the `lock`
/// method that consumes a `BiLock` and returns a future that will resolve when
/// it's locked.
///
/// A `BiLock` is typically used for "split" operations where data which serves
/// two purposes wants to be split into two to be worked with separately. For
/// example a TCP stream could be both a reader and a writer or a framing layer
/// could be both a stream and a sink for messages. A `BiLock` enables splitting
/// these two and then using each independently in a futures-powered fashion.
#[derive(Debug)]
pub struct BiLock<T> {
    inner: Arc<Inner<T>>,
}

#[derive(Debug)]
struct Inner<T> {
    state: AtomicUsize,
    inner: UnsafeCell<T>,
}

unsafe impl<T: Send> Send for Inner<T> {}
unsafe impl<T: Send> Sync for Inner<T> {}

impl<T> BiLock<T> {
    /// Creates a new `BiLock` protecting the provided data.
    ///
    /// Two handles to the lock are returned, and these are the only two handles
    /// that will ever be available to the lock. These can then be sent to separate
    /// tasks to be managed there.
    pub fn new(t: T) -> (BiLock<T>, BiLock<T>) {
        let inner = Arc::new(Inner {
            state: AtomicUsize::new(0),
            inner: UnsafeCell::new(t),
        });

        (BiLock { inner: inner.clone() }, BiLock { inner: inner })
    }

    /// Attempt to acquire this lock, returning `NotReady` if it can't be
    /// acquired.
    ///
    /// This function will acquire the lock in a nonblocking fashion, returning
    /// immediately if the lock is already held. If the lock is successfully
    /// acquired then `Async::Ready` is returned with a value that represents
    /// the locked value (and can be used to access the protected data). The
    /// lock is unlocked when the returned `BiLockGuard` is dropped.
    ///
    /// If the lock is already held then this function will return
    /// `Async::NotReady`. In this case the current task will also be scheduled
    /// to receive a notification when the lock would otherwise become
    /// available.
    ///
    /// # Panics
    ///
    /// This function will panic if called outside the context of a future's
    /// task.
    pub fn poll_lock(&self) -> Async<BiLockGuard<T>> {
        loop {
            match self.inner.state.swap(1, SeqCst) {
                // Woohoo, we grabbed the lock!
                0 => return Async::Ready(BiLockGuard { inner: self }),

                // Oops, someone else has locked the lock
                1 => {}

                // A task was previously blocked on this lock, likely our task,
                // so we need to update that task.
                n => unsafe {
                    drop(Box::from_raw(n as *mut Task));
                }
            }

            let me = Box::new(task::current());
            let me = Box::into_raw(me) as usize;

            match self.inner.state.compare_exchange(1, me, SeqCst, SeqCst) {
                // The lock is still locked, but we've now parked ourselves, so
                // just report that we're scheduled to receive a notification.
                Ok(_) => return Async::NotReady,

                // Oops, looks like the lock was unlocked after our swap above
                // and before the compare_exchange. Deallocate what we just
                // allocated and go through the loop again.
                Err(0) => unsafe {
                    drop(Box::from_raw(me as *mut Task));
                },

                // The top of this loop set the previous state to 1, so if we
                // failed the CAS above then it's because the previous value was
                // *not* zero or one. This indicates that a task was blocked,
                // but we're trying to acquire the lock and there's only one
                // other reference of the lock, so it should be impossible for
                // that task to ever block itself.
                Err(n) => panic!("invalid state: {}", n),
            }
        }
    }

    /// Perform a "blocking lock" of this lock, consuming this lock handle and
    /// returning a future to the acquired lock.
    ///
    /// This function consumes the `BiLock<T>` and returns a sentinel future,
    /// `BiLockAcquire<T>`. The returned future will resolve to
    /// `BiLockAcquired<T>` which represents a locked lock similarly to
    /// `BiLockGuard<T>`.
    ///
    /// Note that the returned future will never resolve to an error.
    pub fn lock(self) -> BiLockAcquire<T> {
        BiLockAcquire {
            inner: Some(self),
        }
    }

    fn unlock(&self) {
        match self.inner.state.swap(0, SeqCst) {
            // we've locked the lock, shouldn't be possible for us to see an
            // unlocked lock.
            0 => panic!("invalid unlocked state"),

            // Ok, no one else tried to get the lock, we're done.
            1 => {}

            // Another task has parked themselves on this lock, let's wake them
            // up as its now their turn.
            n => unsafe {
                Box::from_raw(n as *mut Task).notify();
            }
        }
    }
}

impl<T> Drop for Inner<T> {
    fn drop(&mut self) {
        assert_eq!(self.state.load(SeqCst), 0);
    }
}

/// Returned RAII guard from the `poll_lock` method.
///
/// This structure acts as a sentinel to the data in the `BiLock<T>` itself,
/// implementing `Deref` and `DerefMut` to `T`. When dropped, the lock will be
/// unlocked.
#[derive(Debug)]
pub struct BiLockGuard<'a, T: 'a> {
    inner: &'a BiLock<T>,
}

impl<'a, T> Deref for BiLockGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.inner.inner.inner.get() }
    }
}

impl<'a, T> DerefMut for BiLockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.inner.inner.inner.get() }
    }
}

impl<'a, T> Drop for BiLockGuard<'a, T> {
    fn drop(&mut self) {
        self.inner.unlock();
    }
}

/// Future returned by `BiLock::lock` which will resolve when the lock is
/// acquired.
#[derive(Debug)]
pub struct BiLockAcquire<T> {
    inner: Option<BiLock<T>>,
}

impl<T> Future for BiLockAcquire<T> {
    type Item = BiLockAcquired<T>;
    type Error = ();

    fn poll(&mut self) -> Poll<BiLockAcquired<T>, ()> {
        match self.inner.as_ref().expect("cannot poll after Ready").poll_lock() {
            Async::Ready(r) => {
                mem::forget(r);
            }
            Async::NotReady => return Ok(Async::NotReady),
        }
        Ok(Async::Ready(BiLockAcquired { inner: self.inner.take() }))
    }
}

/// Resolved value of the `BiLockAcquire<T>` future.
///
/// This value, like `BiLockGuard<T>`, is a sentinel to the value `T` through
/// implementations of `Deref` and `DerefMut`. When dropped will unlock the
/// lock, and the original unlocked `BiLock<T>` can be recovered through the
/// `unlock` method.
#[derive(Debug)]
pub struct BiLockAcquired<T> {
    inner: Option<BiLock<T>>,
}

impl<T> BiLockAcquired<T> {
    /// Recovers the original `BiLock<T>`, unlocking this lock.
    pub fn unlock(mut self) -> BiLock<T> {
        let bi_lock = self.inner.take().unwrap();

        bi_lock.unlock();

        bi_lock
    }
}

impl<T> Deref for BiLockAcquired<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.inner.as_ref().unwrap().inner.inner.get() }
    }
}

impl<T> DerefMut for BiLockAcquired<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.inner.as_mut().unwrap().inner.inner.get() }
    }
}

impl<T> Drop for BiLockAcquired<T> {
    fn drop(&mut self) {
        if let Some(ref bi_lock) = self.inner {
            bi_lock.unlock();
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt::Write;
use xml;

pub struct Transaction {
    pub typ: TransactionType,
    pub amount: String, // change to a decmial?
    pub billing_address_id: Option<String>,
    pub billing: Option<::address::Address>,
    pub credit_card: Option<::credit_card::CreditCard>,
    pub custom_fields: HashMap<String, String>,
    pub customer: Option<::customer::Customer>,
    pub customer_id: Option<String>,
    pub descriptor: Option<::descriptor::Descriptor>,
    pub options: Option<Options>,
    pub payment_method_nonce: Option<String>,
    pub payment_method_token: Option<String>,
    pub purchase_order_number: Option<String>,
    pub recurring: Option<bool>,
    pub service_fee_amount: Option<String>,
    pub shipping: Option<::address::Address>,
    pub shipping_address_id: Option<String>,
    pub tax_amount: Option<String>,
    pub tax_exempt: Option<bool>,
}

impl Default for Transaction {
    fn default() -> Transaction {
        Transaction{
            typ: TransactionType::Sale,
            amount: String::from("0"),
            billing_address_id: None,
            billing: None,
            credit_card: None,
            custom_fields: HashMap::new(),
            customer: None,
            customer_id: None,
            descriptor: None,
            options: None,
            payment_method_nonce: None,
            payment_method_token: None,
            purchase_order_number: None,
            recurring: None,
            service_fee_amount: None,
            shipping: None,
            shipping_address_id: None,
            tax_amount: None,
            tax_exempt: None,
        }
    }
}

impl ::ToXml for Transaction {
    fn to_xml(&self, name: Option<&str>) -> String {
        let name = xml::escape(&name.unwrap_or("transaction"));
        let mut s = String::new();
        write!(s, "<{}>", name).unwrap();

        write!(s, "<{}>{}</{}>", "type", match self.typ {
            TransactionType::Sale => "sale",
            TransactionType::Refund => "refund",
        }, "type").unwrap();

        write!(s, "<amount>{}</amount>", xml::escape(&self.amount)).unwrap();

        write_xml!(s, "billing-address-id", self.billing_address_id);

        if let Some(ref billing) = self.billing { write!(s, "{}", billing.to_xml(Some("billing"))).unwrap(); }

        if let Some(ref credit_card) = self.credit_card { write!(s, "{}", credit_card.to_xml(None)).unwrap(); }

        if !self.custom_fields.is_empty() {
            write!(s, "<custom-fields>").unwrap();
            for (k, v) in &self.custom_fields {
                let k = xml::escape(k);
                let v = xml::escape(v);
                write!(s, "<{}>{}</{}>", k, v, k).unwrap();
            }
            write!(s, "</custom-fields>").unwrap();
        }

        if let Some(ref customer) = self.customer { write!(s, "{}", customer.to_xml(None)).unwrap(); }

        write_xml!(s, "customer-id", self.customer_id);

        if let Some(ref descriptor) = self.descriptor { write!(s, "{}", descriptor.to_xml(None)).unwrap(); }

        if let Some(ref options) = self.options { write!(s, "{}", options.to_xml(None)).unwrap(); }

        write_xml!(s, "payment-method-nonce", self.payment_method_nonce);
        write_xml!(s, "payment-method-token", self.payment_method_token);
        write_xml!(s, "purchase-order-number", self.purchase_order_number);
        write_xml!(s, "recurring", self.recurring);
        write_xml!(s, "service-fee-amount", self.recurring);

        if let Some(ref shipping) = self.shipping { write!(s, "{}", shipping.to_xml(Some("shipping"))).unwrap(); }

        write_xml!(s, "shipping-address-id", self.shipping_address_id);
        write_xml!(s, "tax-amount", self.tax_amount);
        write_xml!(s, "tax-exempt", self.tax_exempt);

        write!(s, "</{}>", name).unwrap();
        s
    }
}

pub struct Options {
    pub add_billing_address_to_payment_method: Option<bool>,
    pub hold_in_escrow: Option<bool>,
    // pub paypal: Option<PayPalOptions>,
    pub skip_avs: Option<bool>,
    pub skip_cvv: Option<bool>,
    pub store_in_vault: Option<bool>,
    pub store_in_vault_on_success: Option<bool>,
    pub store_shipping_address_in_vault: Option<bool>,
    pub submit_for_settlement: Option<bool>,
}

impl Default for Options {
    fn default() -> Options {
        Options{
            add_billing_address_to_payment_method: None,
            hold_in_escrow: None,
            paypal: None,
            skip_avs: None,
            skip_cvv: None,
            store_in_vault: None,
            store_in_vault_on_success: None,
            store_shipping_address_in_vault: None,
            submit_for_settlement: None,
        }
    }
}

impl ::ToXml for Options {
    fn to_xml(&self, name: Option<&str>) -> String {
        let name = xml::escape(&name.unwrap_or("options"));
        let mut s = String::new();
        write!(s, "<{}>", name).unwrap();

        write_xml!(s, "add-billing-address-to-payment-method", self.add_billing_address_to_payment_method);
        write_xml!(s, "hold-in-escrow", self.hold_in_escrow);
        // write_xml!(s, "paypal", self.paypal);
        write_xml!(s, "skip-avs", self.skip_avs);
        write_xml!(s, "skip-cvv", self.skip_cvv);
        write_xml!(s, "store-in-vault", self.store_in_vault);
        write_xml!(s, "store-in-vault-on-success", self.store_in_vault_on_success);
        write_xml!(s, "store-shipping-address-in-vault", self.store_shipping_address_in_vault);
        write_xml!(s, "submit-for-settlement", self.submit_for_settlement);

        write!(s, "</{}>", name).unwrap();
        s
    }
}

// TODO: implement this and add it to Options above
// pub struct PayPalOptions {
//
// }

pub enum TransactionType {
    Sale,
    Refund,
}
<commit_msg>Fix nonexistent field reference<commit_after>use std::collections::HashMap;
use std::fmt::Write;
use xml;

pub struct Transaction {
    pub typ: TransactionType,
    pub amount: String, // change to a decmial?
    pub billing_address_id: Option<String>,
    pub billing: Option<::address::Address>,
    pub credit_card: Option<::credit_card::CreditCard>,
    pub custom_fields: HashMap<String, String>,
    pub customer: Option<::customer::Customer>,
    pub customer_id: Option<String>,
    pub descriptor: Option<::descriptor::Descriptor>,
    pub options: Option<Options>,
    pub payment_method_nonce: Option<String>,
    pub payment_method_token: Option<String>,
    pub purchase_order_number: Option<String>,
    pub recurring: Option<bool>,
    pub service_fee_amount: Option<String>,
    pub shipping: Option<::address::Address>,
    pub shipping_address_id: Option<String>,
    pub tax_amount: Option<String>,
    pub tax_exempt: Option<bool>,
}

impl Default for Transaction {
    fn default() -> Transaction {
        Transaction{
            typ: TransactionType::Sale,
            amount: String::from("0"),
            billing_address_id: None,
            billing: None,
            credit_card: None,
            custom_fields: HashMap::new(),
            customer: None,
            customer_id: None,
            descriptor: None,
            options: None,
            payment_method_nonce: None,
            payment_method_token: None,
            purchase_order_number: None,
            recurring: None,
            service_fee_amount: None,
            shipping: None,
            shipping_address_id: None,
            tax_amount: None,
            tax_exempt: None,
        }
    }
}

impl ::ToXml for Transaction {
    fn to_xml(&self, name: Option<&str>) -> String {
        let name = xml::escape(&name.unwrap_or("transaction"));
        let mut s = String::new();
        write!(s, "<{}>", name).unwrap();

        write!(s, "<{}>{}</{}>", "type", match self.typ {
            TransactionType::Sale => "sale",
            TransactionType::Refund => "refund",
        }, "type").unwrap();

        write!(s, "<amount>{}</amount>", xml::escape(&self.amount)).unwrap();

        write_xml!(s, "billing-address-id", self.billing_address_id);

        if let Some(ref billing) = self.billing { write!(s, "{}", billing.to_xml(Some("billing"))).unwrap(); }

        if let Some(ref credit_card) = self.credit_card { write!(s, "{}", credit_card.to_xml(None)).unwrap(); }

        if !self.custom_fields.is_empty() {
            write!(s, "<custom-fields>").unwrap();
            for (k, v) in &self.custom_fields {
                let k = xml::escape(k);
                let v = xml::escape(v);
                write!(s, "<{}>{}</{}>", k, v, k).unwrap();
            }
            write!(s, "</custom-fields>").unwrap();
        }

        if let Some(ref customer) = self.customer { write!(s, "{}", customer.to_xml(None)).unwrap(); }

        write_xml!(s, "customer-id", self.customer_id);

        if let Some(ref descriptor) = self.descriptor { write!(s, "{}", descriptor.to_xml(None)).unwrap(); }

        if let Some(ref options) = self.options { write!(s, "{}", options.to_xml(None)).unwrap(); }

        write_xml!(s, "payment-method-nonce", self.payment_method_nonce);
        write_xml!(s, "payment-method-token", self.payment_method_token);
        write_xml!(s, "purchase-order-number", self.purchase_order_number);
        write_xml!(s, "recurring", self.recurring);
        write_xml!(s, "service-fee-amount", self.recurring);

        if let Some(ref shipping) = self.shipping { write!(s, "{}", shipping.to_xml(Some("shipping"))).unwrap(); }

        write_xml!(s, "shipping-address-id", self.shipping_address_id);
        write_xml!(s, "tax-amount", self.tax_amount);
        write_xml!(s, "tax-exempt", self.tax_exempt);

        write!(s, "</{}>", name).unwrap();
        s
    }
}

pub struct Options {
    pub add_billing_address_to_payment_method: Option<bool>,
    pub hold_in_escrow: Option<bool>,
    // pub paypal: Option<PayPalOptions>,
    pub skip_avs: Option<bool>,
    pub skip_cvv: Option<bool>,
    pub store_in_vault: Option<bool>,
    pub store_in_vault_on_success: Option<bool>,
    pub store_shipping_address_in_vault: Option<bool>,
    pub submit_for_settlement: Option<bool>,
}

impl Default for Options {
    fn default() -> Options {
        Options{
            add_billing_address_to_payment_method: None,
            hold_in_escrow: None,
            // paypal: None,
            skip_avs: None,
            skip_cvv: None,
            store_in_vault: None,
            store_in_vault_on_success: None,
            store_shipping_address_in_vault: None,
            submit_for_settlement: None,
        }
    }
}

impl ::ToXml for Options {
    fn to_xml(&self, name: Option<&str>) -> String {
        let name = xml::escape(&name.unwrap_or("options"));
        let mut s = String::new();
        write!(s, "<{}>", name).unwrap();

        write_xml!(s, "add-billing-address-to-payment-method", self.add_billing_address_to_payment_method);
        write_xml!(s, "hold-in-escrow", self.hold_in_escrow);
        // write_xml!(s, "paypal", self.paypal);
        write_xml!(s, "skip-avs", self.skip_avs);
        write_xml!(s, "skip-cvv", self.skip_cvv);
        write_xml!(s, "store-in-vault", self.store_in_vault);
        write_xml!(s, "store-in-vault-on-success", self.store_in_vault_on_success);
        write_xml!(s, "store-shipping-address-in-vault", self.store_shipping_address_in_vault);
        write_xml!(s, "submit-for-settlement", self.submit_for_settlement);

        write!(s, "</{}>", name).unwrap();
        s
    }
}

// TODO: implement this and add it to Options above
// pub struct PayPalOptions {
//
// }

pub enum TransactionType {
    Sale,
    Refund,
}
<|endoftext|>


<commit_before>extern crate getopts;

use std::io::Write;
use std::path::PathBuf;
use self::getopts::Options;
use command;
use git;

pub enum ParsedArgv {
    Help,
    Version,
    Parsed(command::Options, bool),
}

type ErrorMsg = String;

fn normalize_repo_format(mut s: String, git_dir: &PathBuf) -> Result<String, ErrorMsg> {
    if let Ok(url) = git::new(git_dir)?.remote_url(&s) {
        return Ok(url);
    }

    if !s.ends_with(".git") {
        s.push_str(".git");
    }

    if s.starts_with("git@") || s.starts_with("https://") || s.starts_with("http://") {
        return Ok(s);
    }

    match s.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}", s)),
        2 => Ok(format!("https://{}", s)),
        _ => Err(format!("Error: Invalid repository format '{}'. Format must be one of 'user/repo', 'service/user/repo' or remote name or Git URL.", s)),
    }
}

pub fn parse_options(argv: Vec<String>) -> Result<ParsedArgv, ErrorMsg> {
    let program = argv[0].clone();
    let mut opts = Options::new();

    opts.optopt("r", "repo", "Shorthand format (user/repo, service/user/repo) or remote name (e.g. origin) or Git URL you want to see", "REPO");
    opts.optopt("b", "branch", "Branch name of the repository", "BRANCH");
    opts.optopt("d", "dir", "Directory path to your repository", "PATH");
    opts.optflag("u", "url", "Output URL to STDOUT instead of opening in browser");
    opts.optflag("h", "help", "Print this help");
    opts.optflag("v", "version", "Show version");

    let matches = opts.parse(&argv[1..]).map_err(|f| format!("{}", f))?;

    if matches.opt_present("h") {
        let brief = format!("Usage: {} [Options] {{Args}}", program);
        errorln!("{}", opts.usage(&brief));
        return Ok(ParsedArgv::Help);
    }

    if matches.opt_present("v") {
        println!("{}", option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"));
        return Ok(ParsedArgv::Version);
    }

    let git_dir = git::git_dir(matches.opt_str("d"))?;

    let (repo, branch) = match (matches.opt_str("r"), matches.opt_str("b")) {
        (Some(r), Some(b)) => (normalize_repo_format(r, &git_dir)?, b),
        (Some(r), None) => (normalize_repo_format(r, &git_dir)?, git::new(&git_dir)?.tracking_remote()?.1),
        (None, Some(b)) => (git::new(&git_dir)?.tracking_remote()?.0, b),
        (None, None) => git::new(&git_dir)?.tracking_remote()?,
    };

    let show_url = matches.opt_present("u");

    Ok(ParsedArgv::Parsed(command::Options {
        repo: repo,
        branch: branch,
        git_dir: git_dir,
        args: matches.free,
    }, show_url))
}

<commit_msg>more usage on --help<commit_after>extern crate getopts;

use std::io::Write;
use std::path::PathBuf;
use self::getopts::Options;
use command;
use git;

pub enum ParsedArgv {
    Help,
    Version,
    Parsed(command::Options, bool),
}

type ErrorMsg = String;

fn normalize_repo_format(mut s: String, git_dir: &PathBuf) -> Result<String, ErrorMsg> {
    if let Ok(url) = git::new(git_dir)?.remote_url(&s) {
        return Ok(url);
    }

    if !s.ends_with(".git") {
        s.push_str(".git");
    }

    if s.starts_with("git@") || s.starts_with("https://") || s.starts_with("http://") {
        return Ok(s);
    }

    match s.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}", s)),
        2 => Ok(format!("https://{}", s)),
        _ => Err(format!("Error: Invalid repository format '{}'. Format must be one of 'user/repo', 'service/user/repo' or remote name or Git URL.", s)),
    }
}

fn usage(program: &String) -> String {
    format!(
        r#"Usage: {} [Options] {{Args}}

  Open a repository, file, commit or diff in your web browser from command line.
  Please see https://github.com/rhysd/git-brws for more detail.

Example:
  - Open current repository:

    $ git brws

  - Open a file:

    $ git brws some/file.txt

  - Open specific commit:

    $ git brws HEAD~3

  - Open diff between commits:

    $ git brws HEAD..HEAD~3"#, program)
}

pub fn parse_options(argv: Vec<String>) -> Result<ParsedArgv, ErrorMsg> {
    let program = argv[0].clone();
    let mut opts = Options::new();

    opts.optopt("r", "repo", "Shorthand format (user/repo, service/user/repo) or remote name (e.g. origin) or Git URL you want to see", "REPO");
    opts.optopt("b", "branch", "Branch name of the repository", "BRANCH");
    opts.optopt("d", "dir", "Directory path to your repository", "PATH");
    opts.optflag("u", "url", "Output URL to STDOUT instead of opening in browser");
    opts.optflag("h", "help", "Print this help");
    opts.optflag("v", "version", "Show version");

    let matches = opts.parse(&argv[1..]).map_err(|f| format!("{}", f))?;

    if matches.opt_present("h") {
        let brief = usage(&program);
        errorln!("{}", opts.usage(&brief));
        return Ok(ParsedArgv::Help);
    }

    if matches.opt_present("v") {
        println!("{}", option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"));
        return Ok(ParsedArgv::Version);
    }

    let git_dir = git::git_dir(matches.opt_str("d"))?;

    let (repo, branch) = match (matches.opt_str("r"), matches.opt_str("b")) {
        (Some(r), Some(b)) => (normalize_repo_format(r, &git_dir)?, b),
        (Some(r), None) => (normalize_repo_format(r, &git_dir)?, git::new(&git_dir)?.tracking_remote()?.1),
        (None, Some(b)) => (git::new(&git_dir)?.tracking_remote()?.0, b),
        (None, None) => git::new(&git_dir)?.tracking_remote()?,
    };

    let show_url = matches.opt_present("u");

    Ok(ParsedArgv::Parsed(command::Options {
        repo: repo,
        branch: branch,
        git_dir: git_dir,
        args: matches.free,
    }, show_url))
}

<|endoftext|>


<commit_before>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Align;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Align,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Align) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		return Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		};
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		return Cell::new_align(string, Align::LEFT);
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Align) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		return self;
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Align::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Align::CENTER),
					'l' => self.align(Align::LEFT),
					'r' => self.align(Align::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		return self.content.len();
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		return self.width;
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		return self.content.join("\n");
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize) -> Result<(), Error> {
		let c = match self.content.get(idx) {
			Some(s) => s.as_ref(),
			None => ""
		};
		try!(write!(out, " "));
		try!(print_align(out, self.align, c, ' ', col_width));
		return write!(out, " ");
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(ok) => ok,
				Err(::term::Error::NotSupported) => (), // Ignore unsupported atrributes
				Err(::term::Error::Io(why)) => return Err(why),
				Err(e) => return Err(Error::new(::std::io::ErrorKind::Other, e))
			};
		}
		try!(self.print(out, idx, col_width));
		try!(out.reset().map_err(term_error_to_io_error));
		return Ok(());
	}
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		return Cell::new(&f.to_string());
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		return self.get_content();
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		return Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Align::LEFT,
			style: Vec::new()
		};
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl:"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident : $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Align;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " hello      ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " привет     ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20);
		assert_eq!(out.as_string(), " 由系统自动更新       ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Align::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " test       ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Align::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), "    test    ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Align::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), "       test ");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Align::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Align::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Align::LEFT);
	}
}
<commit_msg>Ooops, restored those lines I deleted #16<commit_after>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Align;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Align,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Align) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		return Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		};
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		return Cell::new_align(string, Align::LEFT);
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Align) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		return self;
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Align::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Align::CENTER),
					'l' => self.align(Align::LEFT),
					'r' => self.align(Align::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		return self.content.len();
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		return self.width;
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		return self.content.join("\n");
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize) -> Result<(), Error> {
		let c = match self.content.get(idx) {
			Some(s) => s.as_ref(),
			None => ""
		};
		try!(write!(out, " "));
		try!(print_align(out, self.align, c, ' ', col_width));
		return write!(out, " ");
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(ok) => ok,
				Err(::term::Error::NotSupported) => (), // Ignore unsupported atrributes
				Err(e) => return Err(term_error_to_io_error(e))
			};
		}
		try!(self.print(out, idx, col_width));
		try!(out.reset().map_err(term_error_to_io_error));
		return Ok(());
	}
}

fn term_error_to_io_error(te: ::term::Error) -> Error {
    match te {
        ::term::Error::Io(why) => why,
        _ => Error::new(::std::io::ErrorKind::Other, te),
    }
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		return Cell::new(&f.to_string());
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		return self.get_content();
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		return Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Align::LEFT,
			style: Vec::new()
		};
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl:"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident : $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Align;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " hello      ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " привет     ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20);
		assert_eq!(out.as_string(), " 由系统自动更新       ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Align::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), " test       ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Align::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), "    test    ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Align::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10);
		assert_eq!(out.as_string(), "       test ");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Align::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Align::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Align::LEFT);
	}
}
<|endoftext|>