<commit_before>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
#[derive(Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is a non-standard feature that may not be supported on all servers
    /// or clients.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `</nil>`
    ///
    /// Ref: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

impl From<Vec<u8>> for Value {
    fn from(other: Vec<u8>) -> Self {
        Value::Base64(other)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str;
    use std::collections::BTreeMap;

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }
}
<commit_msg>Add From<i64> implementation for Value.<commit_after>//! Contains the different types of values understood by XML-RPC.

use utils::{escape_xml, format_datetime};

use base64::encode;
use iso8601::DateTime;

use std::collections::BTreeMap;
use std::io::{self, Write};

/// The possible XML-RPC values.
#[derive(Debug, PartialEq)]
pub enum Value {
    /// `<i4>` or `<int>`, 32-bit signed integer.
    Int(i32),
    /// `<i8>`, 64-bit signed integer.
    ///
    /// This is a non-standard feature that may not be supported on all servers
    /// or clients.
    Int64(i64),
    /// `<boolean>`, 0 == `false`, 1 == `true`.
    Bool(bool),
    /// `<string>`
    // FIXME zero-copy? `Cow<'static, ..>`?
    String(String),
    /// `<double>`
    Double(f64),
    /// `<dateTime.iso8601>`, an ISO 8601 formatted date/time value.
    DateTime(DateTime),
    /// `<base64>`, base64-encoded binary data.
    Base64(Vec<u8>),

    /// `<struct>`, a mapping of named values.
    Struct(BTreeMap<String, Value>),
    /// `<array>`, a list of arbitrary (heterogeneous) values.
    Array(Vec<Value>),

    /// `</nil>`
    ///
    /// Ref: https://web.archive.org/web/20050911054235/http://ontosys.com/xml-rpc/extensions.php
    Nil
}

impl Value {
    /// Formats this `Value` as an XML `<value>` element.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        writeln!(fmt, "<value>")?;

        match *self {
            Value::Int(i) => {
                writeln!(fmt, "<i4>{}</i4>", i)?;
            }
            Value::Int64(i) => {
                writeln!(fmt, "<i8>{}</i8>", i)?;
            }
            Value::Bool(b) => {
                writeln!(fmt, "<boolean>{}</boolean>", if b { "1" } else { "0" })?;
            }
            Value::String(ref s) => {
                writeln!(fmt, "<string>{}</string>", escape_xml(s))?;
            }
            Value::Double(d) => {
                writeln!(fmt, "<double>{}</double>", d)?;
            }
            Value::DateTime(date_time) => {
                writeln!(fmt, "<dateTime.iso8601>{}</dateTime.iso8601>", format_datetime(&date_time))?;
            }
            Value::Base64(ref data) => {
                writeln!(fmt, "<base64>{}</base64>", encode(data))?;
            }
            Value::Struct(ref map) => {
                writeln!(fmt, "<struct>")?;
                for (ref name, ref value) in map {
                    writeln!(fmt, "<member>")?;
                    writeln!(fmt, "<name>{}</name>", escape_xml(name))?;
                    value.write_as_xml(fmt)?;
                    writeln!(fmt, "</member>")?;
                }
                writeln!(fmt, "</struct>")?;
            }
            Value::Array(ref array) => {
                writeln!(fmt, "<array>")?;
                writeln!(fmt, "<data>")?;
                for value in array {
                    value.write_as_xml(fmt)?;
                }
                writeln!(fmt, "</data>")?;
                writeln!(fmt, "</array>")?;
            }
            Value::Nil => {
                writeln!(fmt, "<nil/>")?;
            }
        }

        writeln!(fmt, "</value>")?;
        Ok(())
    }
}

impl From<i32> for Value {
    fn from(other: i32) -> Self {
        Value::Int(other)
    }
}

impl From<i64> for Value {
    fn from(other: i64) -> Self {
        Value::Int64(other)
    }
}

impl From<bool> for Value {
    fn from(other: bool) -> Self {
        Value::Bool(other)
    }
}

impl From<String> for Value {
    fn from(other: String) -> Self {
        Value::String(other)
    }
}

impl<'a> From<&'a str> for Value {
    fn from(other: &'a str) -> Self {
        Value::String(other.to_string())
    }
}

impl From<f64> for Value {
    fn from(other: f64) -> Self {
        Value::Double(other)
    }
}

impl From<DateTime> for Value {
    fn from(other: DateTime) -> Self {
        Value::DateTime(other)
    }
}

impl From<Vec<u8>> for Value {
    fn from(other: Vec<u8>) -> Self {
        Value::Base64(other)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str;
    use std::collections::BTreeMap;

    #[test]
    fn escapes_strings() {
        let mut output: Vec<u8> = Vec::new();

        Value::from("<xml> string").write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<string><xml>&nbsp;string</string>\n</value>\n");
    }

    #[test]
    fn escapes_struct_member_names() {
        let mut output: Vec<u8> = Vec::new();
        let mut map: BTreeMap<String, Value> = BTreeMap::new();
        map.insert("x&<x".to_string(), Value::from(true));

        Value::Struct(map).write_as_xml(&mut output).unwrap();
        assert_eq!(str::from_utf8(&output).unwrap(), "<value>\n<struct>\n<member>\n<name>x&<x</name>\n<value>\n<boolean>1</boolean>\n</value>\n</member>\n</struct>\n</value>\n");
    }
}
<|endoftext|>


<commit_before>#![crate_name = "wc"]
#![feature(path_ext)]

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

extern crate getopts;
extern crate libc;

use getopts::{Matches, Options};
use std::ascii::AsciiExt;
use std::fs::{File, PathExt};
use std::io::{stdin, BufRead, BufReader, Read, Write};
use std::path::Path;
use std::result::Result as StdResult;
use std::str::from_utf8;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

struct Settings {
    show_bytes: bool,
    show_chars: bool,
    show_lines: bool,
    show_words: bool,
    show_max_line_length: bool,
}

impl Settings {
    fn new(matches: &Matches) -> Settings {
        let settings = Settings {
            show_bytes: matches.opt_present("bytes"),
            show_chars: matches.opt_present("chars"),
            show_lines: matches.opt_present("lines"),
            show_words: matches.opt_present("words"),
            show_max_line_length: matches.opt_present("L"),
        };

        if settings.show_bytes
            || settings.show_chars
            || settings.show_lines
            || settings.show_words
            || settings.show_max_line_length {
            return settings;
        }

        Settings {
            show_bytes: true,
            show_chars: false,
            show_lines: true,
            show_words: true,
            show_max_line_length: false,
        }
    }
}

struct Result {
    title: String,
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

static NAME: &'static str = "wc";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("c", "bytes", "print the byte counts");
    opts.optflag("m", "chars", "print the character counts");
    opts.optflag("l", "lines", "print the newline counts");
    opts.optflag("L", "max-line-length", "print the length of the longest line");
    opts.optflag("w", "words", "print the word counts");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let mut matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!("");
        println!("{}", opts.usage("Print newline, word and byte counts for each FILE"));
        println!("With no FILE, or when FILE is -, read standard input.");
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.free.is_empty() {
        matches.free.push("-".to_string());
    }

    let settings = Settings::new(&matches);

    match wc(matches.free, &settings) {
        Ok(()) => ( /* pass */ ),
        Err(e) => return e
    }

    0
}

const CR: u8 = '\r' as u8;
const LF: u8 = '\n' as u8;
const SPACE: u8 = ' ' as u8;
const TAB: u8 = '\t' as u8;
const SYN: u8 = 0x16 as u8;
const FF: u8 = 0x0C as u8;

#[inline(always)]
fn is_word_seperator(byte: u8) -> bool {
    byte == SPACE || byte == TAB || byte == CR || byte == SYN || byte == FF
}

fn wc(files: Vec<String>, settings: &Settings) -> StdResult<(), i32> {
    let mut total_line_count: usize = 0;
    let mut total_word_count: usize = 0;
    let mut total_char_count: usize = 0;
    let mut total_byte_count: usize = 0;
    let mut total_longest_line_length: usize = 0;

    let mut results = vec!();
    let mut max_str_len: usize = 0;

    for path in files.iter() {
        let mut reader = try!(open(&path[..]));

        let mut line_count: usize = 0;
        let mut word_count: usize = 0;
        let mut byte_count: usize = 0;
        let mut char_count: usize = 0;
        let mut longest_line_length: usize = 0;
        let mut raw_line = Vec::new();

        // reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
        // hence the option wrapped in a result here
        while match reader.read_until(LF, &mut raw_line) {
            Ok(n) if n > 0 => true,
            Err(ref e) if raw_line.len() > 0 => {
                show_warning!("Error while reading {}: {}", path, e);
                raw_line.len() > 0
            },
            _ => false,
        } {
            // GNU 'wc' only counts lines that end in LF as lines
            if *raw_line.last().unwrap() == LF {
                line_count += 1;
            }

            byte_count += raw_line.len();

            // try and convert the bytes to UTF-8 first
            let current_char_count;
            match from_utf8(&raw_line[..]) {
                Ok(line) => {
                    word_count += line.split_whitespace().count();
                    current_char_count = line.chars().count();
                },
                Err(..) => {
                    word_count += raw_line.split(|&x| is_word_seperator(x)).count();
                    current_char_count = raw_line.iter().filter(|c|c.is_ascii()).count()
                }
            }
            char_count += current_char_count;

            if current_char_count > longest_line_length {
                // we subtract one here because `line.len()` includes the LF
                // matches GNU 'wc' behaviour
                longest_line_length = current_char_count - 1;
            }

            raw_line.truncate(0);
        }

        results.push(Result {
            title: path.to_string(),
            bytes: byte_count,
            chars: char_count,
            lines: line_count,
            words: word_count,
            max_line_length: longest_line_length,
        });

        total_line_count += line_count;
        total_word_count += word_count;
        total_char_count += char_count;
        total_byte_count += byte_count;

        if longest_line_length > total_longest_line_length {
            total_longest_line_length = longest_line_length;
        }

        // used for formatting
        max_str_len = total_byte_count.to_string().len();
    }

    for result in results.iter() {
        print_stats(settings, &result, max_str_len);
    }

    if files.len() > 1 {
        let result = Result {
            title: "total".to_string(),
            bytes: total_byte_count,
            chars: total_char_count,
            lines: total_line_count,
            words: total_word_count,
            max_line_length: total_longest_line_length,
        };
        print_stats(settings, &result, max_str_len);
    }

    Ok(())
}

fn print_stats(settings: &Settings, result: &Result, max_str_len: usize) {
    if settings.show_lines {
        print!("{:1$}", result.lines, max_str_len);
    }
    if settings.show_words {
        print!("{:1$}", result.words, max_str_len);
    }
    if settings.show_bytes {
        print!("{:1$}", result.bytes, max_str_len);
    }
    if settings.show_chars {
        print!("{:1$}", result.chars, max_str_len);
    }
    if settings.show_max_line_length {
        print!("{:1$}", result.max_line_length, max_str_len);
    }

    if result.title != "-" {
        println!(" {}", result.title);
    }
    else {
        println!("");
    }
}

fn open(path: &str) -> StdResult<BufReader<Box<Read+'static>>, i32> {
    if "-" == path {
        let reader = Box::new(stdin()) as Box<Read>;
        return Ok(BufReader::new(reader));
    }

    let fpath = Path::new(path);
    if fpath.is_dir() {
        show_info!("{}: is a directory", path);
    }
    match File::open(&fpath) {
        Ok(fd) => {
            let reader = Box::new(fd) as Box<Read>;
            Ok(BufReader::new(reader))
        }
        Err(e) => {
            show_error!("wc: {}: {}", path, e);
            Err(1)
        }
    }
}
<commit_msg>Align formatting with GNU implementation.<commit_after>#![crate_name = "wc"]
#![feature(path_ext)]

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

extern crate getopts;
extern crate libc;

use getopts::{Matches, Options};
use std::ascii::AsciiExt;
use std::fs::{File, PathExt};
use std::io::{stdin, BufRead, BufReader, Read, Write};
use std::path::Path;
use std::result::Result as StdResult;
use std::str::from_utf8;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

struct Settings {
    show_bytes: bool,
    show_chars: bool,
    show_lines: bool,
    show_words: bool,
    show_max_line_length: bool,
}

impl Settings {
    fn new(matches: &Matches) -> Settings {
        let settings = Settings {
            show_bytes: matches.opt_present("bytes"),
            show_chars: matches.opt_present("chars"),
            show_lines: matches.opt_present("lines"),
            show_words: matches.opt_present("words"),
            show_max_line_length: matches.opt_present("L"),
        };

        if settings.show_bytes
            || settings.show_chars
            || settings.show_lines
            || settings.show_words
            || settings.show_max_line_length {
            return settings;
        }

        Settings {
            show_bytes: true,
            show_chars: false,
            show_lines: true,
            show_words: true,
            show_max_line_length: false,
        }
    }
}

struct Result {
    title: String,
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

static NAME: &'static str = "wc";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("c", "bytes", "print the byte counts");
    opts.optflag("m", "chars", "print the character counts");
    opts.optflag("l", "lines", "print the newline counts");
    opts.optflag("L", "max-line-length", "print the length of the longest line");
    opts.optflag("w", "words", "print the word counts");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let mut matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!("");
        println!("{}", opts.usage("Print newline, word and byte counts for each FILE"));
        println!("With no FILE, or when FILE is -, read standard input.");
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.free.is_empty() {
        matches.free.push("-".to_string());
    }

    let settings = Settings::new(&matches);

    match wc(matches.free, &settings) {
        Ok(()) => ( /* pass */ ),
        Err(e) => return e
    }

    0
}

const CR: u8 = '\r' as u8;
const LF: u8 = '\n' as u8;
const SPACE: u8 = ' ' as u8;
const TAB: u8 = '\t' as u8;
const SYN: u8 = 0x16 as u8;
const FF: u8 = 0x0C as u8;

#[inline(always)]
fn is_word_seperator(byte: u8) -> bool {
    byte == SPACE || byte == TAB || byte == CR || byte == SYN || byte == FF
}

fn wc(files: Vec<String>, settings: &Settings) -> StdResult<(), i32> {
    let mut total_line_count: usize = 0;
    let mut total_word_count: usize = 0;
    let mut total_char_count: usize = 0;
    let mut total_byte_count: usize = 0;
    let mut total_longest_line_length: usize = 0;

    let mut results = vec!();
    let mut max_width: usize = 0;

    for path in files.iter() {
        let mut reader = try!(open(&path[..]));

        let mut line_count: usize = 0;
        let mut word_count: usize = 0;
        let mut byte_count: usize = 0;
        let mut char_count: usize = 0;
        let mut longest_line_length: usize = 0;
        let mut raw_line = Vec::new();

        // reading from a TTY seems to raise a condition on, rather than return Some(0) like a file.
        // hence the option wrapped in a result here
        while match reader.read_until(LF, &mut raw_line) {
            Ok(n) if n > 0 => true,
            Err(ref e) if raw_line.len() > 0 => {
                show_warning!("Error while reading {}: {}", path, e);
                raw_line.len() > 0
            },
            _ => false,
        } {
            // GNU 'wc' only counts lines that end in LF as lines
            if *raw_line.last().unwrap() == LF {
                line_count += 1;
            }

            byte_count += raw_line.len();

            // try and convert the bytes to UTF-8 first
            let current_char_count;
            match from_utf8(&raw_line[..]) {
                Ok(line) => {
                    word_count += line.split_whitespace().count();
                    current_char_count = line.chars().count();
                },
                Err(..) => {
                    word_count += raw_line.split(|&x| is_word_seperator(x)).count();
                    current_char_count = raw_line.iter().filter(|c|c.is_ascii()).count()
                }
            }
            char_count += current_char_count;

            if current_char_count > longest_line_length {
                // we subtract one here because `line.len()` includes the LF
                // matches GNU 'wc' behaviour
                longest_line_length = current_char_count - 1;
            }

            raw_line.truncate(0);
        }

        results.push(Result {
            title: path.to_string(),
            bytes: byte_count,
            chars: char_count,
            lines: line_count,
            words: word_count,
            max_line_length: longest_line_length,
        });

        total_line_count += line_count;
        total_word_count += word_count;
        total_char_count += char_count;
        total_byte_count += byte_count;

        if longest_line_length > total_longest_line_length {
            total_longest_line_length = longest_line_length;
        }

        // used for formatting
        max_width = total_byte_count.to_string().len() + 1;
    }

    for result in results.iter() {
        print_stats(settings, &result, max_width);
    }

    if files.len() > 1 {
        let result = Result {
            title: "total".to_string(),
            bytes: total_byte_count,
            chars: total_char_count,
            lines: total_line_count,
            words: total_word_count,
            max_line_length: total_longest_line_length,
        };
        print_stats(settings, &result, max_width);
    }

    Ok(())
}

fn print_stats(settings: &Settings, result: &Result, max_width: usize) {
    if settings.show_lines {
        print!("{:1$}", result.lines, max_width);
    }
    if settings.show_words {
        print!("{:1$}", result.words, max_width);
    }
    if settings.show_bytes {
        print!("{:1$}", result.bytes, max_width);
    }
    if settings.show_chars {
        print!("{:1$}", result.chars, max_width);
    }
    if settings.show_max_line_length {
        print!("{:1$}", result.max_line_length, max_width);
    }

    if result.title != "-" {
        println!(" {}", result.title);
    }
    else {
        println!("");
    }
}

fn open(path: &str) -> StdResult<BufReader<Box<Read+'static>>, i32> {
    if "-" == path {
        let reader = Box::new(stdin()) as Box<Read>;
        return Ok(BufReader::new(reader));
    }

    let fpath = Path::new(path);
    if fpath.is_dir() {
        show_info!("{}: is a directory", path);
    }
    match File::open(&fpath) {
        Ok(fd) => {
            let reader = Box::new(fd) as Box<Read>;
            Ok(BufReader::new(reader))
        }
        Err(e) => {
            show_error!("wc: {}: {}", path, e);
            Err(1)
        }
    }
}
<|endoftext|>


<commit_before>use std::cast::transmute;
use std::libc::{c_int};
use std::uint;

use audio::{AudioFormat, Channels, Mono, Stereo};
use video::ll::SDL_RWFromFile; // XXX refactoring
use get_error;

pub mod ll {
    use video::ll::SDL_RWops; // XXX refactoring

    use std::libc::c_int;

    pub struct Mix_Chunk {
        allocated: c_int,
        abuf: *u8,
        alen: u32,
        volume: u8,
    }

    #[link_args = "-lSDL_mixer"]
    extern {
        pub fn Mix_OpenAudio(frequency: c_int, format: u16, channels: c_int, chunksize: c_int)
                             -> c_int;
        pub fn Mix_QuerySpec(frequency: *mut c_int, format: *mut u16, channels: *mut c_int)
                             -> c_int;
        pub fn Mix_LoadWAV_RW(src: *SDL_RWops, freesrc: c_int) -> *Mix_Chunk;
        pub fn Mix_FreeChunk(chunk: *Mix_Chunk);
        pub fn Mix_AllocateChannels(numchans: c_int) -> c_int;
        pub fn Mix_Playing(channel: c_int) -> c_int;
        pub fn Mix_PlayChannelTimed(channel: c_int, chunk: *Mix_Chunk, loops: c_int, ticks: c_int)
                                    -> c_int;
        pub fn Mix_GetChunk(channel: c_int) -> *Mix_Chunk;
        pub fn Mix_CloseAudio();
        pub fn Mix_HaltChannel(channel: c_int) -> c_int;
    }
}

pub struct Chunk {
    priv data: ChunkData
}

enum ChunkData {
    Borrowed(*ll::Mix_Chunk),
    Allocated(*ll::Mix_Chunk),
    OwnedBuffer(ChunkAndBuffer)
}

struct ChunkAndBuffer {
    buffer: ~[u8],
    ll_chunk: ll::Mix_Chunk
}

unsafe fn check_if_not_playing(ll_chunk_addr: *ll::Mix_Chunk) {
    // Verify that the chunk is not currently playing.
    //
    // TODO: I can't prove to myself that this is not racy, although I believe it is not
    // as long as all SDL calls are happening from the same thread. Somebody with better
    // knowledge of how SDL_mixer works internally should double check this, though.

    let mut frequency = 0;
    let mut format = 0;
    let mut channels = 0;
    if ll::Mix_QuerySpec(&mut frequency, &mut format, &mut channels) == 0 {
        channels = 0;
    }

    for uint::range(0, channels as uint) |ch| {
        if ll::Mix_GetChunk(ch as i32) == ll_chunk_addr {
            fail!(~"attempt to free a channel that's playing!")
        }
    }
}

impl Drop for Chunk {
    fn drop(&self) {
        unsafe {
            match self.data {
                Borrowed(_) => (),
                Allocated(ll_chunk) => {
                    check_if_not_playing(ll_chunk);
                    ll::Mix_FreeChunk(ll_chunk);
                },
                OwnedBuffer(ref chunk) => {
                    check_if_not_playing(&chunk.ll_chunk);
                }
            }
        }
    }
}

impl Chunk {
    pub fn new(buffer: ~[u8], volume: u8) -> ~Chunk {
        unsafe {
            let buffer_addr: *u8 = transmute(&buffer[0]);
            let buffer_len = buffer.len() as u32;
            ~Chunk {
                data: OwnedBuffer(
                    ChunkAndBuffer {
                        buffer: buffer,
                        ll_chunk: ll::Mix_Chunk {
                            allocated: 0,
                            abuf: buffer_addr,
                            alen: buffer_len,
                            volume: volume
                        }
                    }
                )
            }
        }
    }

    pub fn from_wav(path: &Path) -> Result<~Chunk, ~str> {
        let raw = unsafe {
            do path.to_str().as_c_str |buf| {
                do "rb".as_c_str |mode_buf| {
                    ll::Mix_LoadWAV_RW(SDL_RWFromFile(buf, mode_buf), 1)
                }
            }
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Chunk { data: Allocated(raw) }) }
    }

    pub fn to_ll_chunk(&self) -> *ll::Mix_Chunk {
        match self.data {
            Borrowed(ll_chunk) => ll_chunk,
            Allocated(ll_chunk) => ll_chunk,
            OwnedBuffer(ref chunk) => {
                let ll_chunk: *ll::Mix_Chunk = &chunk.ll_chunk; ll_chunk
            }
        }
    }

    pub fn volume(&self) -> u8 {
        let ll_chunk: *ll::Mix_Chunk = self.to_ll_chunk();
        unsafe { (*ll_chunk).volume }
    }

    pub fn play_timed(&self, channel: Option<c_int>, loops: c_int, ticks: c_int) -> c_int {
        unsafe {
            let ll_channel = match channel {
                None => -1,
                Some(channel) => channel,
            };
            ll::Mix_PlayChannelTimed(ll_channel, self.to_ll_chunk(), loops, ticks)
        }
    }

    pub fn play(&self, channel: Option<c_int>, loops: c_int) -> c_int {
        self.play_timed(channel, loops, -1)
    }
}

pub fn open(frequency: c_int, format: AudioFormat, channels: Channels, chunksize: c_int)
         -> Result<(),()> {
    unsafe {
        if ll::Mix_OpenAudio(frequency, format.to_ll_format(), channels.count(), chunksize) == 0 {
            Ok(())
        } else {
            Err(())
        }
    }
}

pub fn close() {
    unsafe {
        ll::Mix_CloseAudio()
    }
}

struct Query {
    frequency: c_int,
    format: AudioFormat,
    channels: Channels,
}

pub fn query() -> Option<Query> {
    unsafe {
        let mut frequency = 0;
        let mut ll_format = 0;
        let mut ll_channels = 0;
        if ll::Mix_QuerySpec(&mut frequency, &mut ll_format, &mut ll_channels) == 0 {
            return None;
        }
        Some(Query {
            frequency: frequency,
            format: AudioFormat::from_ll_format(ll_format),
            channels: if ll_channels == 1 { Mono } else { Stereo }
        })
    }
}

pub fn allocate_channels(numchans: c_int) -> c_int {
    unsafe {
        ll::Mix_AllocateChannels(numchans)
    }
}

pub fn playing(channel: Option<c_int>) -> bool {
    unsafe {
        match channel {
            Some(channel) => ll::Mix_Playing(channel) as bool,
            None => ll::Mix_Playing(-1) as bool
        }
    }
}

pub fn halt_channel(channel: c_int) -> c_int {
    unsafe {
        ll::Mix_HaltChannel(channel)
    }
}

<commit_msg>update range to external iterator as per https://github.com/mozilla/rust/pull/8216<commit_after>use std::cast::transmute;
use std::libc::{c_int};
use std::uint;

use audio::{AudioFormat, Channels, Mono, Stereo};
use video::ll::SDL_RWFromFile; // XXX refactoring
use get_error;

pub mod ll {
    use video::ll::SDL_RWops; // XXX refactoring

    use std::libc::c_int;

    pub struct Mix_Chunk {
        allocated: c_int,
        abuf: *u8,
        alen: u32,
        volume: u8,
    }

    #[link_args = "-lSDL_mixer"]
    extern {
        pub fn Mix_OpenAudio(frequency: c_int, format: u16, channels: c_int, chunksize: c_int)
                             -> c_int;
        pub fn Mix_QuerySpec(frequency: *mut c_int, format: *mut u16, channels: *mut c_int)
                             -> c_int;
        pub fn Mix_LoadWAV_RW(src: *SDL_RWops, freesrc: c_int) -> *Mix_Chunk;
        pub fn Mix_FreeChunk(chunk: *Mix_Chunk);
        pub fn Mix_AllocateChannels(numchans: c_int) -> c_int;
        pub fn Mix_Playing(channel: c_int) -> c_int;
        pub fn Mix_PlayChannelTimed(channel: c_int, chunk: *Mix_Chunk, loops: c_int, ticks: c_int)
                                    -> c_int;
        pub fn Mix_GetChunk(channel: c_int) -> *Mix_Chunk;
        pub fn Mix_CloseAudio();
        pub fn Mix_HaltChannel(channel: c_int) -> c_int;
    }
}

pub struct Chunk {
    priv data: ChunkData
}

enum ChunkData {
    Borrowed(*ll::Mix_Chunk),
    Allocated(*ll::Mix_Chunk),
    OwnedBuffer(ChunkAndBuffer)
}

struct ChunkAndBuffer {
    buffer: ~[u8],
    ll_chunk: ll::Mix_Chunk
}

unsafe fn check_if_not_playing(ll_chunk_addr: *ll::Mix_Chunk) {
    // Verify that the chunk is not currently playing.
    //
    // TODO: I can't prove to myself that this is not racy, although I believe it is not
    // as long as all SDL calls are happening from the same thread. Somebody with better
    // knowledge of how SDL_mixer works internally should double check this, though.

    let mut frequency = 0;
    let mut format = 0;
    let mut channels = 0;
    if ll::Mix_QuerySpec(&mut frequency, &mut format, &mut channels) == 0 {
        channels = 0;
    }

    for ch in range(0, (channels as uint)) {
        if ll::Mix_GetChunk(ch as i32) == ll_chunk_addr {
            fail!(~"attempt to free a channel that's playing!")
        }
    }
}

impl Drop for Chunk {
    fn drop(&self) {
        unsafe {
            match self.data {
                Borrowed(_) => (),
                Allocated(ll_chunk) => {
                    check_if_not_playing(ll_chunk);
                    ll::Mix_FreeChunk(ll_chunk);
                },
                OwnedBuffer(ref chunk) => {
                    check_if_not_playing(&chunk.ll_chunk);
                }
            }
        }
    }
}

impl Chunk {
    pub fn new(buffer: ~[u8], volume: u8) -> ~Chunk {
        unsafe {
            let buffer_addr: *u8 = transmute(&buffer[0]);
            let buffer_len = buffer.len() as u32;
            ~Chunk {
                data: OwnedBuffer(
                    ChunkAndBuffer {
                        buffer: buffer,
                        ll_chunk: ll::Mix_Chunk {
                            allocated: 0,
                            abuf: buffer_addr,
                            alen: buffer_len,
                            volume: volume
                        }
                    }
                )
            }
        }
    }

    pub fn from_wav(path: &Path) -> Result<~Chunk, ~str> {
        let raw = unsafe {
            do path.to_str().as_c_str |buf| {
                do "rb".as_c_str |mode_buf| {
                    ll::Mix_LoadWAV_RW(SDL_RWFromFile(buf, mode_buf), 1)
                }
            }
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Chunk { data: Allocated(raw) }) }
    }

    pub fn to_ll_chunk(&self) -> *ll::Mix_Chunk {
        match self.data {
            Borrowed(ll_chunk) => ll_chunk,
            Allocated(ll_chunk) => ll_chunk,
            OwnedBuffer(ref chunk) => {
                let ll_chunk: *ll::Mix_Chunk = &chunk.ll_chunk; ll_chunk
            }
        }
    }

    pub fn volume(&self) -> u8 {
        let ll_chunk: *ll::Mix_Chunk = self.to_ll_chunk();
        unsafe { (*ll_chunk).volume }
    }

    pub fn play_timed(&self, channel: Option<c_int>, loops: c_int, ticks: c_int) -> c_int {
        unsafe {
            let ll_channel = match channel {
                None => -1,
                Some(channel) => channel,
            };
            ll::Mix_PlayChannelTimed(ll_channel, self.to_ll_chunk(), loops, ticks)
        }
    }

    pub fn play(&self, channel: Option<c_int>, loops: c_int) -> c_int {
        self.play_timed(channel, loops, -1)
    }
}

pub fn open(frequency: c_int, format: AudioFormat, channels: Channels, chunksize: c_int)
         -> Result<(),()> {
    unsafe {
        if ll::Mix_OpenAudio(frequency, format.to_ll_format(), channels.count(), chunksize) == 0 {
            Ok(())
        } else {
            Err(())
        }
    }
}

pub fn close() {
    unsafe {
        ll::Mix_CloseAudio()
    }
}

struct Query {
    frequency: c_int,
    format: AudioFormat,
    channels: Channels,
}

pub fn query() -> Option<Query> {
    unsafe {
        let mut frequency = 0;
        let mut ll_format = 0;
        let mut ll_channels = 0;
        if ll::Mix_QuerySpec(&mut frequency, &mut ll_format, &mut ll_channels) == 0 {
            return None;
        }
        Some(Query {
            frequency: frequency,
            format: AudioFormat::from_ll_format(ll_format),
            channels: if ll_channels == 1 { Mono } else { Stereo }
        })
    }
}

pub fn allocate_channels(numchans: c_int) -> c_int {
    unsafe {
        ll::Mix_AllocateChannels(numchans)
    }
}

pub fn playing(channel: Option<c_int>) -> bool {
    unsafe {
        match channel {
            Some(channel) => ll::Mix_Playing(channel) as bool,
            None => ll::Mix_Playing(-1) as bool
        }
    }
}

pub fn halt_channel(channel: c_int) -> c_int {
    unsafe {
        ll::Mix_HaltChannel(channel)
    }
}

<|endoftext|>


<commit_before>use luminance::tessellation;
use luminance_gl::gl33::Tessellation;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::iter::IntoIterator;
use std::path::{Path, PathBuf};
use std::vec;
use wavefront_obj::{self, obj};

// FIXME: implement materials
pub type Material = ();

pub type Vertex = (VertexPos, VertexNor, VertexTexCoord);
pub type VertexPos = [f32; 3];
pub type VertexNor = [f32; 3];
pub type VertexTexCoord = [f32; 2];

pub struct ModelBase<'a> {
  pub parts: Vec<Part<'a>>
}

impl<'a> ModelBase<'a> {
  pub fn from_parts(parts: Vec<Part<'a>>) -> Self {
    ModelBase {
      parts: parts
    }
  }
}

impl<'a> IntoIterator for ModelBase<'a> {
  type Item = Part<'a>;
  type IntoIter = vec::IntoIter<Part<'a>>;

  fn into_iter(self) -> Self::IntoIter {
    self.parts.into_iter()
  }
}

pub struct Part<'a> {
  pub tess: Tessellation,
  pub mat: Option<&'a Material>
}

impl<'a> Part<'a> {
  pub fn new(tess: Tessellation, mat: Option<&'a Material>) -> Self {
    Part {
      tess: tess,
      mat: mat
    }
  }
}

fn load<'a, P>(path: P) -> Result<ModelBase<'a>, ModelError> where P: AsRef<Path> {
  let path = path.as_ref();

  info!("loading model: \x1b[35m{:?}", path);

  let mut input = String::new();

  // load the data directly into memory; no buffering nor streaming
  {
    let mut file = try!(File::open(path).map_err(|e| ModelError::FileNotFound(path.to_path_buf(), format!("{:?}", e))));
    let _ = file.read_to_string(&mut input);
  }

  // parse the obj file and convert it
  let obj_set = try!(obj::parse(input).map_err(ModelError::ParseFailed));

  convert_obj(obj_set)
}

// Turn a wavefront obj object into a `Model`
fn convert_obj<'a>(obj_set: obj::ObjSet) -> Result<ModelBase<'a>, ModelError> {
  if obj_set.objects.len() != 1 {
    return Err(ModelError::MultiObjects);
  }

  let obj = &obj_set.objects[0];
  info!("  converting object \x1b[35m{}", obj.name);

  // convert all the geometries
  let mut parts = Vec::with_capacity(obj.geometry.len());

  for geometry in &obj.geometry {
    let (vertices, indices, mode) = try!(convert_geometry(geometry, &obj.vertices, &obj.normals, &obj.tex_vertices));
    let part = Part::new(Tessellation::new(mode, &vertices, Some(&indices)), None); // FIXME: material
    parts.push(part);
  }

  Ok(ModelBase::from_parts(parts))
}

// Convert wavefront_obj’s Geometry into a pair of vertices and indices.
//
// This function will regenerate the indices on the fly based on which are used in the shapes in the
// geometry. It’s used to create independent tessellation.
fn convert_geometry(geo: &obj::Geometry, positions: &[obj::Vertex], normals: &[obj::Normal], tvertices: &[obj::TVertex]) -> Result<(Vec<Vertex>, Vec<u32>, tessellation::Mode), ModelError> {
  if geo.shapes.is_empty() {
    return Err(ModelError::NoShape);
  }

  let mut vertices = Vec::new(); // FIXME: better allocation scheme?
  let mut indices = Vec::new();
  let mut index_map = BTreeMap::new();

  info!("    converting geometry");

  let mode = guess_mode(geo.shapes[0].primitive);

  for prim in geo.shapes.iter().map(|s| s.primitive) {
    let keys = try!(create_keys_from_primitive(prim));

    for key in keys {
      match index_map.get(&key).map(|&i| i) {
        Some(index) => {
          // that triplet already exists; just append the index in the indices buffer
          indices.push(index);
        },
        None => {
          // this is a new, not yet discovered triplet; create the corresponding vertex and add it
          // to the vertices buffer, and map the triplet to the index in the indices buffer
          let vertex = interleave_vertex(&positions[key.0], &normals[key.1], &tvertices[key.2]);
          let index = vertices.len() as u32;

          vertices.push(vertex);
          indices.push(index);
          index_map.insert(key, index);
        }
      }
    }
  }

  Ok((vertices, indices, mode))
}

// Create triplet keys from wavefront_obj primitives. If any primitive doesn’t have all the triplet
// information (position, normal, tex), a ModelError::UnsupportedVertex error is returned instead.
fn create_keys_from_primitive(prim: obj::Primitive) -> Result<Vec<(usize, usize, usize)>, ModelError> {
  match prim {
    obj::Primitive::Point(i) => {
      let a = try!(vtnindex_to_key(i));
      Ok(vec![a])
    },
    obj::Primitive::Line(i, j) => {
      let a = try!(vtnindex_to_key(i));
      let b = try!(vtnindex_to_key(j));
      Ok(vec![a, b])
    },
    obj::Primitive::Triangle(i, j, k) => {
      let a = try!(vtnindex_to_key(i));
      let b = try!(vtnindex_to_key(j));
      let c = try!(vtnindex_to_key(k));
      Ok(vec![a, b, c])
    }
  }
}

// Convert from a wavefront_obj VTNIndex into our triplet, raising error if not possible.
fn vtnindex_to_key(i: obj::VTNIndex) -> Result<(usize, usize, usize), ModelError> {
  match i {
    (pi, Some(ti), Some(ni)) => Ok((pi, ni, ti)),
    _ => Err(ModelError::UnsupportedVertex)
  }
}

fn interleave_vertex(p: &obj::Vertex, n: &obj::Normal, t: &obj::TVertex) -> Vertex {
  (convert_vertex(p), convert_nor(n), convert_tvertex(t))
}

fn convert_vertex(v: &obj::Vertex) -> VertexPos {
  [v.x as f32, v.y as f32, v.z as f32]
}

fn convert_nor(n: &obj::Normal) -> VertexNor {
  convert_vertex(n)
}

fn convert_tvertex(t: &obj::TVertex) -> VertexTexCoord {
  [t.x as f32, t.y as f32]
}

fn guess_mode(prim: obj::Primitive) -> tessellation::Mode {
  match prim {
    obj::Primitive::Point(_) => tessellation::Mode::Point,
    obj::Primitive::Line(_, _) => tessellation::Mode::Line,
    obj::Primitive::Triangle(_, _, _) => tessellation::Mode::Triangle
  }
}

#[derive(Debug)]
pub enum ModelError {
  FileNotFound(PathBuf, String),
  ParseFailed(wavefront_obj::ParseError),
  MultiObjects,
  UnsupportedVertex,
  NoShape
}

#[cfg(feature = "hot-resource")]
mod hot {
  use std::ops::Deref;
  use std::path::{Path, PathBuf};
  use std::sync::mpsc;

  use super::{ModelBase, ModelError};

  use resource::ResourceManager;

  pub struct Model<'a> {
    rx: mpsc::Receiver<()>,
    model: ModelBase<'a>,
    path: PathBuf
  }

  impl<'a> Model<'a> {
    pub fn load<P>(manager: &mut ResourceManager, path: P) -> Result<Self, ModelError> where P: AsRef<Path> {
      let path = path.as_ref();
      let model = try!(super::load(path));

      let (sx, rx) = mpsc::channel();
      manager.monitor(path, sx);

      Ok(Model {
        rx: rx,
        model: model,
        path: path.to_owned()
      })
    }

    pub fn sync(&mut self) {
      if self.rx.try_recv().is_ok() {
        self.reload();
      }
    }

    fn reload(&mut self) {
      match super::load(self.path.as_path()) {
        Ok(model) => {
          self.model = model;
          info!("reloaded model");
        },
        Err(e) => {
          err!("reloading model has failed: {:?}", e);
        }
      }
    }
  }

  impl<'a> Deref for Model<'a> {
    type Target = ModelBase<'a>;

    fn deref(&self) -> &Self::Target {
      &self.model
    }
  }
}

#[cfg(not(feature = "hot-resource"))]
mod cold {
  use std::ops::Deref;
  use std::path::Path;

  use super::{ModelBase, ModelError};

  use resource::ResourceManager;

  pub struct Model<'a>(ModelBase<'a>);

  impl<'a> Model<'a> {
    pub fn load<P>(_: &mut ResourceManager, path: P) -> Result<Self, ModelError> where P: AsRef<Path> {
      super::load(path).map(|m| Model(m))
    }

    pub fn sync(&mut self) {
    }
  }

  impl<'a> Deref for Model<'a> {
    type Target = ModelBase<'a>;

    fn deref(&self) -> &Self::Target {
      &self.0
    }
  }
}

#[cfg(feature = "hot-resource")]
pub use self::hot::*;
#[cfg(not(feature = "hot-resource"))]
pub use self::cold::*;
<commit_msg>Revert "Removed model::load from the interface."<commit_after>use luminance::tessellation;
use luminance_gl::gl33::Tessellation;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::iter::IntoIterator;
use std::path::{Path, PathBuf};
use std::vec;
use wavefront_obj::{self, obj};

// FIXME: implement materials
pub type Material = ();

pub type Vertex = (VertexPos, VertexNor, VertexTexCoord);
pub type VertexPos = [f32; 3];
pub type VertexNor = [f32; 3];
pub type VertexTexCoord = [f32; 2];

pub struct ModelBase<'a> {
  pub parts: Vec<Part<'a>>
}

impl<'a> ModelBase<'a> {
  pub fn from_parts(parts: Vec<Part<'a>>) -> Self {
    ModelBase {
      parts: parts
    }
  }
}

impl<'a> IntoIterator for ModelBase<'a> {
  type Item = Part<'a>;
  type IntoIter = vec::IntoIter<Part<'a>>;

  fn into_iter(self) -> Self::IntoIter {
    self.parts.into_iter()
  }
}

pub struct Part<'a> {
  pub tess: Tessellation,
  pub mat: Option<&'a Material>
}

impl<'a> Part<'a> {
  pub fn new(tess: Tessellation, mat: Option<&'a Material>) -> Self {
    Part {
      tess: tess,
      mat: mat
    }
  }
}

pub fn load<'a, P>(path: P) -> Result<ModelBase<'a>, ModelError> where P: AsRef<Path> {
  let path = path.as_ref();

  info!("loading model: \x1b[35m{:?}", path);

  let mut input = String::new();

  // load the data directly into memory; no buffering nor streaming
  {
    let mut file = try!(File::open(path).map_err(|e| ModelError::FileNotFound(path.to_path_buf(), format!("{:?}", e))));
    let _ = file.read_to_string(&mut input);
  }

  // parse the obj file and convert it
  let obj_set = try!(obj::parse(input).map_err(ModelError::ParseFailed));

  convert_obj(obj_set)
}

// Turn a wavefront obj object into a `Model`
fn convert_obj<'a>(obj_set: obj::ObjSet) -> Result<ModelBase<'a>, ModelError> {
  if obj_set.objects.len() != 1 {
    return Err(ModelError::MultiObjects);
  }

  let obj = &obj_set.objects[0];
  info!("  converting object \x1b[35m{}", obj.name);

  // convert all the geometries
  let mut parts = Vec::with_capacity(obj.geometry.len());

  for geometry in &obj.geometry {
    let (vertices, indices, mode) = try!(convert_geometry(geometry, &obj.vertices, &obj.normals, &obj.tex_vertices));
    let part = Part::new(Tessellation::new(mode, &vertices, Some(&indices)), None); // FIXME: material
    parts.push(part);
  }

  Ok(ModelBase::from_parts(parts))
}

// Convert wavefront_obj’s Geometry into a pair of vertices and indices.
//
// This function will regenerate the indices on the fly based on which are used in the shapes in the
// geometry. It’s used to create independent tessellation.
fn convert_geometry(geo: &obj::Geometry, positions: &[obj::Vertex], normals: &[obj::Normal], tvertices: &[obj::TVertex]) -> Result<(Vec<Vertex>, Vec<u32>, tessellation::Mode), ModelError> {
  if geo.shapes.is_empty() {
    return Err(ModelError::NoShape);
  }

  let mut vertices = Vec::new(); // FIXME: better allocation scheme?
  let mut indices = Vec::new();
  let mut index_map = BTreeMap::new();

  info!("    converting geometry");

  let mode = guess_mode(geo.shapes[0].primitive);

  for prim in geo.shapes.iter().map(|s| s.primitive) {
    let keys = try!(create_keys_from_primitive(prim));

    for key in keys {
      match index_map.get(&key).map(|&i| i) {
        Some(index) => {
          // that triplet already exists; just append the index in the indices buffer
          indices.push(index);
        },
        None => {
          // this is a new, not yet discovered triplet; create the corresponding vertex and add it
          // to the vertices buffer, and map the triplet to the index in the indices buffer
          let vertex = interleave_vertex(&positions[key.0], &normals[key.1], &tvertices[key.2]);
          let index = vertices.len() as u32;

          vertices.push(vertex);
          indices.push(index);
          index_map.insert(key, index);
        }
      }
    }
  }

  Ok((vertices, indices, mode))
}

// Create triplet keys from wavefront_obj primitives. If any primitive doesn’t have all the triplet
// information (position, normal, tex), a ModelError::UnsupportedVertex error is returned instead.
fn create_keys_from_primitive(prim: obj::Primitive) -> Result<Vec<(usize, usize, usize)>, ModelError> {
  match prim {
    obj::Primitive::Point(i) => {
      let a = try!(vtnindex_to_key(i));
      Ok(vec![a])
    },
    obj::Primitive::Line(i, j) => {
      let a = try!(vtnindex_to_key(i));
      let b = try!(vtnindex_to_key(j));
      Ok(vec![a, b])
    },
    obj::Primitive::Triangle(i, j, k) => {
      let a = try!(vtnindex_to_key(i));
      let b = try!(vtnindex_to_key(j));
      let c = try!(vtnindex_to_key(k));
      Ok(vec![a, b, c])
    }
  }
}

// Convert from a wavefront_obj VTNIndex into our triplet, raising error if not possible.
fn vtnindex_to_key(i: obj::VTNIndex) -> Result<(usize, usize, usize), ModelError> {
  match i {
    (pi, Some(ti), Some(ni)) => Ok((pi, ni, ti)),
    _ => Err(ModelError::UnsupportedVertex)
  }
}

fn interleave_vertex(p: &obj::Vertex, n: &obj::Normal, t: &obj::TVertex) -> Vertex {
  (convert_vertex(p), convert_nor(n), convert_tvertex(t))
}

fn convert_vertex(v: &obj::Vertex) -> VertexPos {
  [v.x as f32, v.y as f32, v.z as f32]
}

fn convert_nor(n: &obj::Normal) -> VertexNor {
  convert_vertex(n)
}

fn convert_tvertex(t: &obj::TVertex) -> VertexTexCoord {
  [t.x as f32, t.y as f32]
}

fn guess_mode(prim: obj::Primitive) -> tessellation::Mode {
  match prim {
    obj::Primitive::Point(_) => tessellation::Mode::Point,
    obj::Primitive::Line(_, _) => tessellation::Mode::Line,
    obj::Primitive::Triangle(_, _, _) => tessellation::Mode::Triangle
  }
}

#[derive(Debug)]
pub enum ModelError {
  FileNotFound(PathBuf, String),
  ParseFailed(wavefront_obj::ParseError),
  MultiObjects,
  UnsupportedVertex,
  NoShape
}

#[cfg(feature = "hot-resource")]
mod hot {
  use std::ops::Deref;
  use std::path::{Path, PathBuf};
  use std::sync::mpsc;

  use super::{ModelBase, ModelError};

  use resource::ResourceManager;

  pub struct Model<'a> {
    rx: mpsc::Receiver<()>,
    model: ModelBase<'a>,
    path: PathBuf
  }

  impl<'a> Model<'a> {
    pub fn load<P>(manager: &mut ResourceManager, path: P) -> Result<Self, ModelError> where P: AsRef<Path> {
      let path = path.as_ref();
      let model = try!(super::load(path));

      let (sx, rx) = mpsc::channel();
      manager.monitor(path, sx);

      Ok(Model {
        rx: rx,
        model: model,
        path: path.to_owned()
      })
    }

    pub fn sync(&mut self) {
      if self.rx.try_recv().is_ok() {
        self.reload();
      }
    }

    fn reload(&mut self) {
      match super::load(self.path.as_path()) {
        Ok(model) => {
          self.model = model;
          info!("reloaded model");
        },
        Err(e) => {
          err!("reloading model has failed: {:?}", e);
        }
      }
    }
  }

  impl<'a> Deref for Model<'a> {
    type Target = ModelBase<'a>;

    fn deref(&self) -> &Self::Target {
      &self.model
    }
  }
}

#[cfg(not(feature = "hot-resource"))]
mod cold {
  use std::ops::Deref;
  use std::path::Path;

  use super::{ModelBase, ModelError};

  use resource::ResourceManager;

  pub struct Model<'a>(ModelBase<'a>);

  impl<'a> Model<'a> {
    pub fn load<P>(_: &mut ResourceManager, path: P) -> Result<Self, ModelError> where P: AsRef<Path> {
      super::load(path).map(|m| Model(m))
    }

    pub fn sync(&mut self) {
    }
  }

  impl<'a> Deref for Model<'a> {
    type Target = ModelBase<'a>;

    fn deref(&self) -> &Self::Target {
      &self.0
    }
  }
}

#[cfg(feature = "hot-resource")]
pub use self::hot::*;
#[cfg(not(feature = "hot-resource"))]
pub use self::cold::*;
<|endoftext|>


<commit_before>//! Mouse utility functions.

use context::Context;
use error::GameResult;
use graphics;
use graphics::Point2;

/// Stores state information for the mouse,
/// what little of it there is.
#[derive(Clone, Debug)]
pub struct MouseContext {
    last_position: Point2,
}

impl MouseContext {
    /// Creates a new `MouseContext`.
    pub fn new() -> Self {
        Self {
            last_position: Point2::origin(),
        }
    }

    pub(crate) fn set_last_position(&mut self, p: Point2) {
        self.last_position = p;
    }
}

/// Get whether or not the mouse is "grabbed", ie, confined to the window.
pub fn get_grabbed(ctx: &Context) -> bool {
    graphics::get_window(ctx).grab()
}

/// Set whether or not the mouse is "grabbed", ie, confined to the window.
pub fn set_grabbed(ctx: &mut Context, grabbed: bool) {
    graphics::get_window_mut(ctx).set_grab(grabbed)
}

/// Get whether or not the mouse is in relative mode.
///
/// In relative mode, the cursor is hidden and doesn't move when the mouse
/// does, but relative motion events are still generated.  This is useful
/// for things such as implementing mouselook in an FPS.
pub fn get_relative_mode(ctx: &Context) -> bool {
    ctx.sdl_context.mouse().relative_mouse_mode()
}

/// Set whether or not the mouse is in relative mode.
pub fn set_relative_mode(ctx: &Context, mode: bool) {
    ctx.sdl_context.mouse().set_relative_mouse_mode(mode)
}

/// Get the current position of the mouse cursor, in pixels.
/// Complement to `set_position()`.
/// Uses strictly window-only coordinates.
pub fn get_position(ctx: &Context) -> GameResult<Point2> {
    // TODO: Next time we can break the API, remove the GameResult here.
    Ok(ctx.mouse_context.last_position)
}

/// Set the current position of the mouse cursor, in pixels.
/// Uses strictly window-only coordinates.
pub fn set_position(ctx: &Context, point: Point2) {
    let window = graphics::get_window(ctx);
    ctx.sdl_context
        .mouse()
        .warp_mouse_in_window(window, point.x as i32, point.y as i32)
}
<commit_msg>Add redundant documentation to set_relative_mode<commit_after>//! Mouse utility functions.

use context::Context;
use error::GameResult;
use graphics;
use graphics::Point2;

/// Stores state information for the mouse,
/// what little of it there is.
#[derive(Clone, Debug)]
pub struct MouseContext {
    last_position: Point2,
}

impl MouseContext {
    /// Creates a new `MouseContext`.
    pub fn new() -> Self {
        Self {
            last_position: Point2::origin(),
        }
    }

    pub(crate) fn set_last_position(&mut self, p: Point2) {
        self.last_position = p;
    }
}

/// Get whether or not the mouse is "grabbed", ie, confined to the window.
pub fn get_grabbed(ctx: &Context) -> bool {
    graphics::get_window(ctx).grab()
}

/// Set whether or not the mouse is "grabbed", ie, confined to the window.
pub fn set_grabbed(ctx: &mut Context, grabbed: bool) {
    graphics::get_window_mut(ctx).set_grab(grabbed)
}

/// Get whether or not the mouse is in relative mode.
///
/// In relative mode, the cursor is hidden and doesn't move when the mouse
/// does, but relative motion events are still generated.  This is useful
/// for things such as implementing mouselook in an FPS.
pub fn get_relative_mode(ctx: &Context) -> bool {
    ctx.sdl_context.mouse().relative_mouse_mode()
}

/// Set whether or not the mouse is in relative mode.
///
/// In relative mode, the cursor is hidden and doesn't move when the mouse
/// does, but relative motion events are still generated.  This is useful
/// for things such as implementing mouselook in an FPS.
pub fn set_relative_mode(ctx: &Context, mode: bool) {
    ctx.sdl_context.mouse().set_relative_mouse_mode(mode)
}

/// Get the current position of the mouse cursor, in pixels.
/// Complement to `set_position()`.
/// Uses strictly window-only coordinates.
pub fn get_position(ctx: &Context) -> GameResult<Point2> {
    // TODO: Next time we can break the API, remove the GameResult here.
    Ok(ctx.mouse_context.last_position)
}

/// Set the current position of the mouse cursor, in pixels.
/// Uses strictly window-only coordinates.
pub fn set_position(ctx: &Context, point: Point2) {
    let window = graphics::get_window(ctx);
    ctx.sdl_context
        .mouse()
        .warp_mouse_in_window(window, point.x as i32, point.y as i32)
}
<|endoftext|>


<commit_before>extern crate revord;
use std::cmp::Ordering;
use self::revord::RevOrd;
use std::collections::BinaryHeap;

fn bucket_distance(fi: f64, hi: f64, delta: i32, W: f64) -> f64 {
    if delta == -1 {
        W*(fi - hi)
    } else if delta == 1 {
        W - W*(fi - hi)
    } else {
        0 as f64
    }
}
fn compute_pi_j<T>(q: &[T], f_sig: &[f64], h_sig: &[f64], W: f64) -> Vec<(usize, i32)> {
    let mut intermediate_vec: Vec<((usize,i32), f64)> = f_sig.iter()
        .zip(h_sig.iter())
        .enumerate()
        .flat_map(|(i, (fi, hi))| {
            vec![((i, 1), bucket_distance(*fi, *hi, 1, W)),
                 ((i, -1), bucket_distance(*fi, *hi, -1, W))].into_iter()
        }).collect();
    intermediate_vec.sort_by(|a, b| {
        if a.1 > b.1 {
            Ordering::Greater
        } else if a.1 < b.1 {
            Ordering::Less
        } else {
            Ordering::Equal
        }
    });
    intermediate_vec.iter().map(|a| {a.0}).collect()
}
#[test]
fn sorted_delta_test() {
    let test_q = vec![1.0,2.0,3.0,4.0,5.0];
    let f_sig = vec![1.5,1.2,2.2];
    let h_sig = vec![1.0,1.0,2.0];
    let W = 10.0;
    compute_pi_j(&test_q, &f_sig, &h_sig, W);
}
fn score_set(perturbation_set: &[usize], square_zj_list: &[f64]) -> f64 {
    perturbation_set.iter().map(|ind| {square_zj_list[*ind]}).sum()
}

fn gen_perturbation_sets(zj_l: &[f64]) -> PerturbationIterator {
    let mut new_heap = BinaryHeap::new();
    let zero_vec = vec![0];
    let a0 = PerturbationSet {
        data: zero_vec,
        zj_list: zj_l,
        max_m: zj_l.len() / 2
    };
    new_heap.push(RevOrd(a0));
    PerturbationIterator {
        heap: new_heap,
        zj_list: zj_l
    }
}
struct PerturbationIterator<'a> {
    heap: BinaryHeap<RevOrd<PerturbationSet<'a>>>,
    zj_list: &'a [f64]
}

#[test]
fn test_perturbation_iterator() {
    let zjl = vec![1.0,2.0,3.0,4.0];
    let ii = gen_perturbation_sets(&zjl);
    let z: Vec<_> = ii.take(3).collect();
}
impl<'a> Iterator for PerturbationIterator<'a> {
    type Item = PerturbationSet<'a>;
    fn next(&mut self) -> Option<PerturbationSet<'a>> {
        loop {
            match self.heap.pop() {
                Some(revord_val) => {
                    let a_i = revord_val.0;
                    let a_s = a_i.shift();
                    if a_s.valid() {
                        self.heap.push(RevOrd(a_s));
                    }
                    let a_e = a_i.expand();
                    if a_e.valid() {
                        self.heap.push(RevOrd(a_e));
                    }

                    if a_i.valid() {
                        return Some(a_i);
                    }
                },
                None => return None
            };
        }
    }
}

#[derive(PartialEq, Debug)]
struct PerturbationSet<'a> {
    data: Vec<usize>,
    zj_list: &'a [f64],
    max_m:  usize
}
impl<'a> Eq for PerturbationSet<'a> {}

impl<'a> Ord for PerturbationSet<'a> {
    fn cmp(&self, other: &PerturbationSet) -> Ordering {
        let self_score = score_set(&(self.data), self.zj_list);
        let other_score = score_set(&(other.data), other.zj_list);
        if (self_score - other_score) <= 1e-6 {
            Ordering::Equal
        } else if self_score > other_score {
            Ordering::Greater
        } else {
            Ordering::Less
        }
    }
}

impl<'a> PartialOrd for PerturbationSet<'a> {
    fn partial_cmp(&self, other: &PerturbationSet) -> Option<Ordering> {
        let self_score = score_set(&(self.data), self.zj_list);
        let other_score = score_set(&(other.data), other.zj_list);
        self_score.partial_cmp(&other_score)
    }
}
impl<'a> PerturbationSet<'a> {
    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn shift<'b>(&'b self) -> PerturbationSet<'a> {
        let mut new_data = Vec::new();
        let max_val = self.data.iter().max().unwrap();
        let newlist = self.zj_list;
        for &x in &self.data {
            let y = x;
            if y == *max_val {
                new_data.push(y+1);
            } else {
                new_data.push(y);
            }
        }
        PerturbationSet {
            data: new_data,
            zj_list: newlist,
            max_m: self.max_m
        }
    }
    pub fn valid(&self) -> bool {
        let &max_val = self.data.iter().max().unwrap();
        if max_val >= self.max_m {
            return false;
        }
        for &val in &self.data {
            let other_should_be_missing = 2 * self.max_m + 1 - val;
            if let Some(_) = self.data.iter().position(|&y| { y == other_should_be_missing}) {
                return false;
            }
        }
        return true;
    }
    pub fn expand<'b>(&'b self) -> PerturbationSet<'a> {
        let mut new_data = Vec::new();
        let max_val = self.data.iter().max().unwrap();
        for &x in &self.data {
            new_data.push(x);
        }
        new_data.push(*max_val + 1);
        PerturbationSet {
            data: new_data,
            zj_list: self.zj_list,
            max_m: self.max_m
        }
    }
}

fn expected_zj_squared(j: usize, M: usize, W: f64) -> f64 {
    if j <= M {
        (((j * (j + 1)) as f64)
            / ((4 * (M + 1) * (M + 2)) as f64)) * (W * W)
    } else {
        (W*W) * (1.0 - ((2 * M + 1 - j) as f64) / ((M + 1) as f64) +
                 (((2 * M + 1 - j) * (2 * M + 2 - j)) as f64) / ((4 * (M + 1) * (M + 2)) as f64))
    }
}

#[test]
fn test_expected_zj_squared() {
    expected_zj_squared(1,10,1.0);
}

#[test]
fn test_perturbation_iterator_zj() {
    let mut zj_vals = Vec::new();
    for j in 1..20 {
        zj_vals.push(expected_zj_squared(j, 10, 1.0));
    }
    let perturbation_iterator = gen_perturbation_sets(&zj_vals);
    for pert in perturbation_iterator {
        println!("{}", pert.len());
    }
}
<commit_msg>more small cleanup of multiprobe<commit_after>extern crate revord;
use std::cmp::Ordering;
use self::revord::RevOrd;
use std::collections::BinaryHeap;

fn bucket_distance(fi: f64, hi: f64, delta: i32, W: f64) -> f64 {
    if delta == -1 {
        W*(fi - hi)
    } else if delta == 1 {
        W - W*(fi - hi)
    } else {
        0 as f64
    }
}
fn compute_pi_j<T>(q: &[T], f_sig: &[f64], h_sig: &[f64], W: f64) -> Vec<(usize, i32)> {
    let mut intermediate_vec: Vec<((usize,i32), f64)> = f_sig.iter()
        .zip(h_sig.iter())
        .enumerate()
        .flat_map(|(i, (fi, hi))| {
            vec![((i, 1), bucket_distance(*fi, *hi, 1, W)),
                 ((i, -1), bucket_distance(*fi, *hi, -1, W))].into_iter()
        }).collect();
    intermediate_vec.sort_by(|a, b| {
        if a.1 > b.1 {
            Ordering::Greater
        } else if a.1 < b.1 {
            Ordering::Less
        } else {
            Ordering::Equal
        }
    });
    intermediate_vec.iter().map(|a| {a.0}).collect()
}

#[test]
fn sorted_delta_test() {
    let test_q = vec![1.0,2.0,3.0,4.0,5.0];
    let f_sig = vec![1.5,1.2,2.2];
    let h_sig = vec![1.0,1.0,2.0];
    let W = 10.0;
    compute_pi_j(&test_q, &f_sig, &h_sig, W);
}

fn score_set(perturbation_set: &[usize], square_zj_list: &[f64]) -> f64 {
    perturbation_set.iter().map(|ind| {square_zj_list[*ind]}).sum()
}

fn gen_perturbation_sets(zj_l: &[f64]) -> PerturbationIterator {
    let mut new_heap = BinaryHeap::new();
    let zero_vec = vec![0];
    let a0 = PerturbationSet {
        data: zero_vec,
        zj_list: zj_l,
        max_m: zj_l.len() / 2
    };
    new_heap.push(RevOrd(a0));
    PerturbationIterator {
        heap: new_heap,
    }
}

struct PerturbationIterator<'a> {
    heap: BinaryHeap<RevOrd<PerturbationSet<'a>>>,
}

#[test]
fn test_perturbation_iterator() {
    let zjl = vec![1.0,2.0,3.0,4.0];
    let ii = gen_perturbation_sets(&zjl);
    let z: Vec<_> = ii.take(3).collect();
}
impl<'a> Iterator for PerturbationIterator<'a> {
    type Item = PerturbationSet<'a>;
    fn next(&mut self) -> Option<PerturbationSet<'a>> {
        loop {
            match self.heap.pop() {
                Some(revord_val) => {
                    let a_i = revord_val.0;
                    let a_s = a_i.shift();
                    if a_s.valid() {
                        self.heap.push(RevOrd(a_s));
                    }
                    let a_e = a_i.expand();
                    if a_e.valid() {
                        self.heap.push(RevOrd(a_e));
                    }

                    if a_i.valid() {
                        return Some(a_i);
                    }
                },
                None => return None
            };
        }
    }
}

#[derive(PartialEq, Debug)]
struct PerturbationSet<'a> {
    data: Vec<usize>,
    zj_list: &'a [f64],
    max_m:  usize
}
impl<'a> Eq for PerturbationSet<'a> {}

impl<'a> Ord for PerturbationSet<'a> {
    fn cmp(&self, other: &PerturbationSet) -> Ordering {
        let self_score = score_set(&(self.data), self.zj_list);
        let other_score = score_set(&(other.data), other.zj_list);
        if (self_score - other_score) <= 1e-6 {
            Ordering::Equal
        } else if self_score > other_score {
            Ordering::Greater
        } else {
            Ordering::Less
        }
    }
}

impl<'a> PartialOrd for PerturbationSet<'a> {
    fn partial_cmp(&self, other: &PerturbationSet) -> Option<Ordering> {
        let self_score = score_set(&(self.data), self.zj_list);
        let other_score = score_set(&(other.data), other.zj_list);
        self_score.partial_cmp(&other_score)
    }
}
impl<'a> PerturbationSet<'a> {
    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn shift<'b>(&'b self) -> PerturbationSet<'a> {
        let mut new_data = Vec::new();
        let max_val = self.data.iter().max().unwrap();
        let newlist = self.zj_list;
        for &x in &self.data {
            let y = x;
            if y == *max_val {
                new_data.push(y+1);
            } else {
                new_data.push(y);
            }
        }
        PerturbationSet {
            data: new_data,
            zj_list: newlist,
            max_m: self.max_m
        }
    }
    pub fn valid(&self) -> bool {
        let &max_val = self.data.iter().max().unwrap();
        if max_val >= self.max_m {
            return false;
        }
        for &val in &self.data {
            let other_should_be_missing = 2 * self.max_m + 1 - val;
            if let Some(_) = self.data.iter().position(|&y| { y == other_should_be_missing}) {
                return false;
            }
        }
        return true;
    }
    pub fn expand<'b>(&'b self) -> PerturbationSet<'a> {
        let mut new_data = Vec::new();
        let max_val = self.data.iter().max().unwrap();
        for &x in &self.data {
            new_data.push(x);
        }
        new_data.push(*max_val + 1);
        PerturbationSet {
            data: new_data,
            zj_list: self.zj_list,
            max_m: self.max_m
        }
    }
}

fn expected_zj_squared(j: usize, M: usize, W: f64) -> f64 {
    if j <= M {
        (((j * (j + 1)) as f64)
            / ((4 * (M + 1) * (M + 2)) as f64)) * (W * W)
    } else {
        (W*W) * (1.0 - ((2 * M + 1 - j) as f64) / ((M + 1) as f64) +
                 (((2 * M + 1 - j) * (2 * M + 2 - j)) as f64) / ((4 * (M + 1) * (M + 2)) as f64))
    }
}

#[test]
fn test_expected_zj_squared() {
    expected_zj_squared(1,10,1.0);
}

#[test]
fn test_perturbation_iterator_zj() {
    let mut zj_vals = Vec::new();
    for j in 1..20 {
        zj_vals.push(expected_zj_squared(j, 10, 1.0));
    }
    let perturbation_iterator = gen_perturbation_sets(&zj_vals);
    for pert in perturbation_iterator {
        println!("{}", pert.len());
    }
}
<|endoftext|>


<commit_before>#![crate_name = "mv"]
#![feature(path_ext, slice_extras, slice_patterns, str_char)]
#![allow(deprecated)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Orvar Segerström <orvarsegerstrom@gmail.com>
 * (c) Sokovikov Evgeniy  <skv-headless@yandex.ru>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::fs::{self, PathExt};
use std::io::{BufRead, BufReader, Result, stdin, Write};
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "mv";
static VERSION: &'static str = "0.0.1";

pub struct Behaviour {
    overwrite: OverwriteMode,
    backup: BackupMode,
    suffix: String,
    update: bool,
    target_dir: Option<String>,
    no_target_dir: bool,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode {
    NoClobber,
    Interactive,
    Force,
}

#[derive(Clone, Eq, PartialEq)]
pub enum BackupMode {
    NoBackup,
    SimpleBackup,
    NumberedBackup,
    ExistingBackup,
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflagopt("",  "backup", "make a backup of each existing destination file", "CONTROL");
    opts.optflag("b", "", "like --backup but does not accept an argument");
    opts.optflag("f", "force", "do not prompt before overwriting");
    opts.optflag("i", "interactive", "prompt before override");
    opts.optflag("n", "no-clobber", "do not overwrite an existing file");
    // I have yet to find a use-case (and thereby write a test) where this option is useful.
    //opts.optflag("",  "strip-trailing-slashes", "remove any trailing slashes from each SOURCE\n \
    //                                        argument");
    opts.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX");
    opts.optopt("t", "target-directory", "move all SOURCE arguments into DIRECTORY", "DIRECTORY");
    opts.optflag("T", "no-target-directory", "treat DEST as a normal file");
    opts.optflag("u", "update", "move only when the SOURCE file is newer\n \
                                than the destination file or when the\n \
                                destination file is missing");
    opts.optflag("v", "verbose", "explain what is being done");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f);
            return 1;
        }
    };
    let usage = opts.usage("Move SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.");

    /* This does not exactly match the GNU implementation:
     * The GNU mv defaults to Force, but if more than one of the
     * overwrite options are supplied, only the last takes effect.
     * To default to no-clobber in that situation seems safer:
     */
    let overwrite_mode = if matches.opt_present("no-clobber") {
        OverwriteMode::NoClobber
    } else if matches.opt_present("interactive") {
        OverwriteMode::Interactive
    } else {
        OverwriteMode::Force
    };

    let backup_mode = if matches.opt_present("b") {
        BackupMode::SimpleBackup
    } else if matches.opt_present("backup") {
        match matches.opt_str("backup") {
            None => BackupMode::SimpleBackup,
            Some(mode) => match &mode[..] {
                "simple" | "never" => BackupMode::SimpleBackup,
                "numbered" | "t"   => BackupMode::NumberedBackup,
                "existing" | "nil" => BackupMode::ExistingBackup,
                "none" | "off"     => BackupMode::NoBackup,
                x => {
                    show_error!("invalid argument ‘{}’ for ‘backup type’\n\
                                Try '{} --help' for more information.", x, NAME);
                    return 1;
                }
            }
        }
    } else {
        BackupMode::NoBackup
    };

    if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
        show_error!("options --backup and --no-clobber are mutually exclusive\n\
                    Try '{} --help' for more information.", NAME);
        return 1;
    }

    let backup_suffix = if matches.opt_present("suffix") {
        match matches.opt_str("suffix") {
            Some(x) => x,
            None => {
                show_error!("option '--suffix' requires an argument\n\
                            Try '{} --help' for more information.", NAME);
                return 1;
            }
        }
    } else {
        "~".to_string()
    };

    if matches.opt_present("T") && matches.opt_present("t") {
        show_error!("cannot combine --target-directory (-t) and --no-target-directory (-T)");
        return 1;
    }

    let behaviour = Behaviour {
        overwrite: overwrite_mode,
        backup: backup_mode,
        suffix: backup_suffix,
        update: matches.opt_present("u"),
        target_dir: matches.opt_str("t"),
        no_target_dir: matches.opt_present("T"),
        verbose: matches.opt_present("v"),
    };

    let string_to_path = |s: &String| { PathBuf::from(s) };
    let paths: Vec<PathBuf> = matches.free.iter().map(string_to_path).collect();

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        0
    } else if matches.opt_present("help") {
        help(&usage);
        0
    } else {
        exec(&paths[..], behaviour)
    }
}

fn help(usage: &str) {
    let msg = format!("a0{} {1}\n\n \
                       Usage: {0} SOURCE DEST\n  \
                         or:  {0} SOURCE... DIRECTORY \
                       \n\
                       {2}", NAME, VERSION, usage);
    println!("{}", msg);
}

fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
    match b.target_dir {
        Some(ref name) => return move_files_into_dir(files, &PathBuf::from(name), &b),
        None => {}
    }
    match files {
        [] | [_] => {
            show_error!("missing file operand\n\
                        Try '{} --help' for more information.", NAME);
            return 1;
        },
        [ref source, ref target] => {
            if !source.exists() {
                show_error!("cannot stat ‘{}’: No such file or directory", source.display());
                return 1;
            }

            if target.is_dir() {
                if b.no_target_dir {
                    if !source.is_dir() {
                        show_error!("cannot overwrite directory ‘{}’ with non-directory",
                            target.display());
                        return 1;
                    }

                    return match rename(source, target, &b) {
                        Err(e) => {
                            show_error!("{}", e);
                            1
                        },
                        _ => 0
                    }
                }

                return move_files_into_dir(&[source.clone()], target, &b);
            }

            match rename(source, target, &b) {
                Err(e) => {
                    show_error!("{}", e);
                    return 1;
                },
                _ => {}
            }
        }
        fs => {
            if b.no_target_dir {
                show_error!("mv: extra operand ‘{}’\n\
                            Try '{} --help' for more information.", fs[2].display(), NAME);
                return 1;
            }
            let target_dir = fs.last().unwrap();
            move_files_into_dir(fs.init(), target_dir, &b);
        }
    }
    0
}

fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!("cannot stat ‘{}’: No such file or directory",
                            sourcepath.display());

                all_successful = false;
                continue;
            }
        };

        match rename(sourcepath, &targetpath, b) {
            Err(e) => {
                show_error!("mv: cannot move ‘{}’ to ‘{}’: {}",
                            sourcepath.display(), targetpath.display(), e);
                all_successful = false;
            },
            _ => {}
        }
    };
    if all_successful { 0 } else { 1 }
}

fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
    let mut backup_path = None;

    if to.exists() {
        match b.overwrite {
            OverwriteMode::NoClobber => return Ok(()),
            OverwriteMode::Interactive => {
                print!("{}: overwrite ‘{}’? ", NAME, to.display());
                if !read_yes() {
                    return Ok(());
                }
            },
            OverwriteMode::Force => {}
        };

        backup_path = match b.backup {
            BackupMode::NoBackup => None,
            BackupMode::SimpleBackup => Some(simple_backup_path(to, &b.suffix)),
            BackupMode::NumberedBackup => Some(numbered_backup_path(to)),
            BackupMode::ExistingBackup => Some(existing_backup_path(to, &b.suffix))
        };
        if let Some(ref p) = backup_path {
            try!(fs::rename(to, p));
        }

        if b.update {
            if try!(fs::metadata(from)).mtime() <= try!(fs::metadata(to)).mtime() {
                return Ok(());
            }
        }
    }

    try!(fs::rename(from, to));

    if b.verbose {
        print!("‘{}’ -> ‘{}’", from.display(), to.display());
        match backup_path {
            Some(path) => println!(" (backup: ‘{}’)", path.display()),
            None => println!("")
        }
    }
    Ok(())
}

fn read_yes() -> bool {
    let mut s = String::new();
    match BufReader::new(stdin()).read_line(&mut s) {
        Ok(_) => match s.slice_shift_char() {
            Some((x, _)) => x == 'y' || x == 'Y',
            _ => false
        },
        _ => false
    }
}

fn simple_backup_path(path: &PathBuf, suffix: &String) -> PathBuf {
    let mut p = path.as_os_str().to_str().unwrap().to_string();
    p.push_str(suffix);
    return PathBuf::from(p);
}

fn numbered_backup_path(path: &PathBuf) -> PathBuf {
    let mut i: u64 = 1;
    loop {
        let new_path = simple_backup_path(path, &format!(".~{}~", i));
        if !new_path.exists() {
            return new_path;
        }
        i = i + 1;
    }
}

fn existing_backup_path(path: &PathBuf, suffix: &String) -> PathBuf {
    let test_path = simple_backup_path(path, &".~1~".to_string());
    if test_path.exists() {
        return numbered_backup_path(path);
    }
    simple_backup_path(path, suffix)
}
<commit_msg>Implement mv's --strip-trailing-slashes<commit_after>#![crate_name = "mv"]
#![feature(path_ext, slice_extras, slice_patterns, str_char)]
#![allow(deprecated)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Orvar Segerström <orvarsegerstrom@gmail.com>
 * (c) Sokovikov Evgeniy  <skv-headless@yandex.ru>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::fs::{self, PathExt};
use std::io::{BufRead, BufReader, Result, stdin, Write};
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "mv";
static VERSION: &'static str = "0.0.1";

pub struct Behaviour {
    overwrite: OverwriteMode,
    backup: BackupMode,
    suffix: String,
    update: bool,
    target_dir: Option<String>,
    no_target_dir: bool,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode {
    NoClobber,
    Interactive,
    Force,
}

#[derive(Clone, Eq, PartialEq)]
pub enum BackupMode {
    NoBackup,
    SimpleBackup,
    NumberedBackup,
    ExistingBackup,
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflagopt("",  "backup", "make a backup of each existing destination file", "CONTROL");
    opts.optflag("b", "", "like --backup but does not accept an argument");
    opts.optflag("f", "force", "do not prompt before overwriting");
    opts.optflag("i", "interactive", "prompt before override");
    opts.optflag("n", "no-clobber", "do not overwrite an existing file");
    opts.optflag("",  "strip-trailing-slashes", "remove any trailing slashes from each SOURCE\n \
                                                 argument");
    opts.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX");
    opts.optopt("t", "target-directory", "move all SOURCE arguments into DIRECTORY", "DIRECTORY");
    opts.optflag("T", "no-target-directory", "treat DEST as a normal file");
    opts.optflag("u", "update", "move only when the SOURCE file is newer\n \
                                than the destination file or when the\n \
                                destination file is missing");
    opts.optflag("v", "verbose", "explain what is being done");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f);
            return 1;
        }
    };
    let usage = opts.usage("Move SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.");

    /* This does not exactly match the GNU implementation:
     * The GNU mv defaults to Force, but if more than one of the
     * overwrite options are supplied, only the last takes effect.
     * To default to no-clobber in that situation seems safer:
     */
    let overwrite_mode = if matches.opt_present("no-clobber") {
        OverwriteMode::NoClobber
    } else if matches.opt_present("interactive") {
        OverwriteMode::Interactive
    } else {
        OverwriteMode::Force
    };

    let backup_mode = if matches.opt_present("b") {
        BackupMode::SimpleBackup
    } else if matches.opt_present("backup") {
        match matches.opt_str("backup") {
            None => BackupMode::SimpleBackup,
            Some(mode) => match &mode[..] {
                "simple" | "never" => BackupMode::SimpleBackup,
                "numbered" | "t"   => BackupMode::NumberedBackup,
                "existing" | "nil" => BackupMode::ExistingBackup,
                "none" | "off"     => BackupMode::NoBackup,
                x => {
                    show_error!("invalid argument ‘{}’ for ‘backup type’\n\
                                Try '{} --help' for more information.", x, NAME);
                    return 1;
                }
            }
        }
    } else {
        BackupMode::NoBackup
    };

    if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
        show_error!("options --backup and --no-clobber are mutually exclusive\n\
                    Try '{} --help' for more information.", NAME);
        return 1;
    }

    let backup_suffix = if matches.opt_present("suffix") {
        match matches.opt_str("suffix") {
            Some(x) => x,
            None => {
                show_error!("option '--suffix' requires an argument\n\
                            Try '{} --help' for more information.", NAME);
                return 1;
            }
        }
    } else {
        "~".to_string()
    };

    if matches.opt_present("T") && matches.opt_present("t") {
        show_error!("cannot combine --target-directory (-t) and --no-target-directory (-T)");
        return 1;
    }

    let behaviour = Behaviour {
        overwrite: overwrite_mode,
        backup: backup_mode,
        suffix: backup_suffix,
        update: matches.opt_present("u"),
        target_dir: matches.opt_str("t"),
        no_target_dir: matches.opt_present("T"),
        verbose: matches.opt_present("v"),
    };

    let paths: Vec<PathBuf> = {
        fn string_to_path<'a>(s: &'a String) -> &'a Path {
            Path::new(s)
        };
        fn strip_slashes<'a>(p: &'a Path) -> &'a Path {
            p.components().as_path()
        }
        let to_owned = |p: &Path| p.to_owned();
        let arguments = matches.free.iter().map(string_to_path);
        if matches.opt_present("strip-trailing-slashes") {
            arguments.map(strip_slashes).map(to_owned).collect()
        } else {
            arguments.map(to_owned).collect()
        }
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        0
    } else if matches.opt_present("help") {
        help(&usage);
        0
    } else {
        exec(&paths[..], behaviour)
    }
}

fn help(usage: &str) {
    let msg = format!("a0{} {1}\n\n \
                       Usage: {0} SOURCE DEST\n  \
                         or:  {0} SOURCE... DIRECTORY \
                       \n\
                       {2}", NAME, VERSION, usage);
    println!("{}", msg);
}

fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
    match b.target_dir {
        Some(ref name) => return move_files_into_dir(files, &PathBuf::from(name), &b),
        None => {}
    }
    match files {
        [] | [_] => {
            show_error!("missing file operand\n\
                        Try '{} --help' for more information.", NAME);
            return 1;
        },
        [ref source, ref target] => {
            if !source.exists() {
                show_error!("cannot stat ‘{}’: No such file or directory", source.display());
                return 1;
            }

            if target.is_dir() {
                if b.no_target_dir {
                    if !source.is_dir() {
                        show_error!("cannot overwrite directory ‘{}’ with non-directory",
                            target.display());
                        return 1;
                    }

                    return match rename(source, target, &b) {
                        Err(e) => {
                            show_error!("{}", e);
                            1
                        },
                        _ => 0
                    }
                }

                return move_files_into_dir(&[source.clone()], target, &b);
            }

            match rename(source, target, &b) {
                Err(e) => {
                    show_error!("{}", e);
                    return 1;
                },
                _ => {}
            }
        }
        fs => {
            if b.no_target_dir {
                show_error!("mv: extra operand ‘{}’\n\
                            Try '{} --help' for more information.", fs[2].display(), NAME);
                return 1;
            }
            let target_dir = fs.last().unwrap();
            move_files_into_dir(fs.init(), target_dir, &b);
        }
    }
    0
}

fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target ‘{}’ is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.as_os_str().to_str() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!("cannot stat ‘{}’: No such file or directory",
                            sourcepath.display());

                all_successful = false;
                continue;
            }
        };

        match rename(sourcepath, &targetpath, b) {
            Err(e) => {
                show_error!("mv: cannot move ‘{}’ to ‘{}’: {}",
                            sourcepath.display(), targetpath.display(), e);
                all_successful = false;
            },
            _ => {}
        }
    };
    if all_successful { 0 } else { 1 }
}

fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
    let mut backup_path = None;

    if to.exists() {
        match b.overwrite {
            OverwriteMode::NoClobber => return Ok(()),
            OverwriteMode::Interactive => {
                print!("{}: overwrite ‘{}’? ", NAME, to.display());
                if !read_yes() {
                    return Ok(());
                }
            },
            OverwriteMode::Force => {}
        };

        backup_path = match b.backup {
            BackupMode::NoBackup => None,
            BackupMode::SimpleBackup => Some(simple_backup_path(to, &b.suffix)),
            BackupMode::NumberedBackup => Some(numbered_backup_path(to)),
            BackupMode::ExistingBackup => Some(existing_backup_path(to, &b.suffix))
        };
        if let Some(ref p) = backup_path {
            try!(fs::rename(to, p));
        }

        if b.update {
            if try!(fs::metadata(from)).mtime() <= try!(fs::metadata(to)).mtime() {
                return Ok(());
            }
        }
    }

    try!(fs::rename(from, to));

    if b.verbose {
        print!("‘{}’ -> ‘{}’", from.display(), to.display());
        match backup_path {
            Some(path) => println!(" (backup: ‘{}’)", path.display()),
            None => println!("")
        }
    }
    Ok(())
}

fn read_yes() -> bool {
    let mut s = String::new();
    match BufReader::new(stdin()).read_line(&mut s) {
        Ok(_) => match s.slice_shift_char() {
            Some((x, _)) => x == 'y' || x == 'Y',
            _ => false
        },
        _ => false
    }
}

fn simple_backup_path(path: &PathBuf, suffix: &String) -> PathBuf {
    let mut p = path.as_os_str().to_str().unwrap().to_string();
    p.push_str(suffix);
    return PathBuf::from(p);
}

fn numbered_backup_path(path: &PathBuf) -> PathBuf {
    let mut i: u64 = 1;
    loop {
        let new_path = simple_backup_path(path, &format!(".~{}~", i));
        if !new_path.exists() {
            return new_path;
        }
        i = i + 1;
    }
}

fn existing_backup_path(path: &PathBuf, suffix: &String) -> PathBuf {
    let test_path = simple_backup_path(path, &".~1~".to_string());
    if test_path.exists() {
        return numbered_backup_path(path);
    }
    simple_backup_path(path, suffix)
}
<|endoftext|>