<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 Result {
    filename: 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());
    }

    match wc(&matches) {
        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
}

pub fn wc(matches: &Matches) -> 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 matches.free.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 {
            filename: 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(&result.filename[..], result.lines, result.words, result.chars, result.bytes, result.max_line_length, matches, max_str_len);
    }

    if matches.free.len() > 1 {
        print_stats("total", total_line_count, total_word_count, total_char_count, total_byte_count, total_longest_line_length, matches, max_str_len);
    }

    Ok(())
}

fn print_stats(filename: &str, line_count: usize, word_count: usize, char_count: usize,
    byte_count: usize, longest_line_length: usize, matches: &Matches, max_str_len: usize) {
    if matches.opt_present("lines") {
        print!("{:1$}", line_count, max_str_len);
    }
    if matches.opt_present("words") {
        print!("{:1$}", word_count, max_str_len);
    }
    if matches.opt_present("bytes") {
        print!("{:1$}", byte_count, max_str_len);
    }
    if matches.opt_present("chars") {
        print!("{:1$}", char_count, max_str_len);
    }
    if matches.opt_present("max-line-length") {
        print!("{:1$}", longest_line_length, max_str_len);
    }

    // defaults
    if !matches.opt_present("bytes")
        && !matches.opt_present("chars")
        && !matches.opt_present("lines")
        && !matches.opt_present("words")
        && !matches.opt_present("max-line-length") {
        print!("{:1$}", line_count, max_str_len);
        print!("{:1$}", word_count, max_str_len + 1);
        print!("{:1$}", byte_count, max_str_len + 1);
    }

    if filename != "-" {
        println!(" {}", filename);
    }
    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>Refactor wc settings into dedicated struct.<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_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)
        }
    }
}
<|endoftext|>


<commit_before>//! Provides a Vec-like container for large sizes that is split to blocks.

use std::fmt;
use std::ops;
use std::ops::{Range, RangeFrom, RangeTo, RangeFull};
use std::cmp;

use itertools;
use odds::vec::VecExt;

/// A generic trait over Rust's built types.
pub trait FromRange {
    #[inline(always)]
    #[doc(hidden)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize);
}

impl FromRange for RangeFull {
    #[inline(always)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize) {
        return (0, seg.len());
    }
}

impl FromRange for Range<usize> {
    #[inline(always)]
    fn from_range(&self, _: &SplitVec) -> (usize, usize) {
        return (self.start, self.end);
    }
}

impl FromRange for RangeFrom<usize> {
    #[inline(always)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize) {
        return (self.start, seg.len());
    }
}

impl FromRange for RangeTo<usize> {
    #[inline(always)]
    fn from_range(&self, _: &SplitVec) -> (usize, usize) {
        return (0, self.end);
    }
}

/// A Vec for large sizes split into smaller blocks.
///
/// Splits a large continuos memory into block sizes to allow insertion/deletion to have an
/// upper bound, based on the block size. It is implemented as a Vec of Vec's, where each Vec
/// has a m inimum and maximum block size. As data is inserted and deleted, from any location,
/// SplitVec will split up and merge the blocks to try and stay between those minimum and maximum
/// block sizes.
pub struct SplitVec {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone)]
struct Index {
    outer: usize,
    inner: usize,
}

/// An iterator over SplitVec contents.
pub struct Items<'a> {
    seg: &'a SplitVec,
    index: Index,
    num_elem: Option<usize>,
}

/// A mutable iterator over SplitVec contents.
pub struct MutItems<'a> {
    seg: &'a mut SplitVec,
    index: Index,
    num_elem: Option<usize>,
}

/// An iterator over the blocks in SplitVec.
pub struct Slices<'a> {
    seg: &'a SplitVec,
    outer: usize,
}

static MIN_BLOCK_SIZE: usize = 1024 * 1024;
static MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024;

impl SplitVec {
    /// Create a new, empty SplitVec
    pub fn new() -> SplitVec {
        SplitVec {
            vecs: Vec::new(),
            length: 0,
        }
    }

    /// Create a SplitVec by consuming a vec as the initial data vector
    pub fn from_vec(values: Vec<u8>) -> SplitVec {
        let len = values.len();
        SplitVec {
            vecs: vec![values],
            length: len,
        }
    }

    /// Create a SplitVec by copying in values from a slice
    pub fn from_slice(values: &[u8]) -> SplitVec {
        SplitVec {
            vecs: vec![values.into()],
            length: values.len(),
        }
    }

    /// Return the length.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Update the saved length value so that the len func will be -O(1)
    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    /// Convert a global pos to a locall index
    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    /// Give an iterator over a given range
    pub fn iter_range<'a, R: FromRange>(&'a self, range: R) -> Items<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Give a mutable iterator over a given range
    pub fn mut_iter_range<'a, R: FromRange>(&'a mut self, range: R) -> MutItems<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Provide an iterator over continuous memory slices
    ///
    /// This is useful for doing an efficient save to disk
    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    /// Prepare an index for future text insertion, splitting/merging big/small sections respectively
    fn prepare_insert(&mut self, index: Index) -> Index {
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < MAX_BLOCK_SIZE {
            return index;
        }

        let page_start_idx = (index.inner / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > MAX_BLOCK_SIZE {
                let insert_vec: Vec < _ >= self.vecs[index.outer][MIN_BLOCK_SIZE..].into();
                self.vecs.insert(index.outer + 1, insert_vec);
                self.vecs[index.outer].truncate(MIN_BLOCK_SIZE);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    /// insert all values from a slice at an offset.
    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            self.vecs[index.outer].splice(index.inner..index.inner, values.into_iter().cloned());
        }

        self.calc_len();
    }

    /// Moves data out from the supplied range.
    pub fn move_out<R: FromRange>(&mut self, range: R) -> Vec<u8> {
        let (from, to) = range.from_range(self);
        // TODO: Convert to drain when that settles
        assert!(from <= to);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(from, false);
        let num_elem = to - from;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    /// Produce of copy of the supplied range
    pub fn copy_out<R: FromRange>(&mut self, range: R) -> Vec<u8> {
        let (from, to) = range.from_range(self);

        // TODO: Make this use direct cocpy rather than iterators
        self.iter_range(from..to).map(|x| *x).collect::<Vec<u8>>()
    }

    /// Copy data from a slice in.
    pub fn copy_in(&mut self, offset: usize, val: &[u8]) {
        for (s, d) in val.iter().zip(self.mut_iter_range(offset..(offset + val.len()))) {
            *d = s.clone();
        }
    }

    /// Replace values in range with the supplied values
    pub fn splice<R: FromRange>(&mut self, range: R, values: &[u8]) -> Vec<u8> {
        // TODO: This is terribly inefficient, will need a reimplementation
        let (from, to) = range.from_range(self);
        let res;

        // Make sure that when we pull data out for the splice, we don't go over the end
        if from < self.len() {
            let move_end = cmp::min(self.len(), to);
            res = self.move_out(from..move_end);
        } else {
            res = vec![];
        }

        self.insert(from, values);

        res
    }

    /// Find a slice.
    pub fn find_slice(&self, needle: &[u8]) -> Option<usize> {
        self.find_slice_from(0, needle)
    }

    /// Find a slice from a certain index and onward
    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        for i in from..self.len() {
            if itertools::equal(self.iter_range(i..i+needle.len()), needle.iter()) {
                return Some(i);
            }
        }
        None
    }

    #[cfg(test)]
    fn get_lengths(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
}

impl ops::Index<usize> for SplitVec {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for SplitVec {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for SplitVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_small_splitvec() {
    let size = 1024;
    let mut seg = SplitVec::from_vec(vec![1, 2, 3, 4, 5]);
    assert_eq!(Some(4), seg.find_slice(&[5]));

    let seg_len = seg.len();
    seg.insert(seg_len/2, &vec![1 as u8; size]);

    assert_eq!(Some(size + 4), seg.find_slice(&[5]));
}

#[test]
fn test_large_splitvec() {
    let big_size = 4*1024*1024;
    let small_size = 1024;
    let mut seg = SplitVec::from_vec(vec![0; big_size]);

    seg.insert(big_size/2, &vec![1 as u8; small_size]);

    assert_eq!(Some(big_size/2 -1), seg.find_slice(&[0, 1]));

    // Make sure we actually tested a "split" version
    let seg_lengths = seg.get_lengths();
    assert_eq!(2, seg_lengths.len());
    let index = seg_lengths[0];
    let sentinal = 100;
    seg[index] = sentinal;
    seg[index+1] = sentinal +1;
    assert_eq!(Some(index), seg.find_slice(&[sentinal, sentinal+1]));
}
<commit_msg>Make index debuggable for easier...debugging<commit_after>//! Provides a Vec-like container for large sizes that is split to blocks.

use std::fmt;
use std::ops;
use std::ops::{Range, RangeFrom, RangeTo, RangeFull};
use std::cmp;

use itertools;
use odds::vec::VecExt;

/// A generic trait over Rust's built types.
pub trait FromRange {
    #[inline(always)]
    #[doc(hidden)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize);
}

impl FromRange for RangeFull {
    #[inline(always)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize) {
        return (0, seg.len());
    }
}

impl FromRange for Range<usize> {
    #[inline(always)]
    fn from_range(&self, _: &SplitVec) -> (usize, usize) {
        return (self.start, self.end);
    }
}

impl FromRange for RangeFrom<usize> {
    #[inline(always)]
    fn from_range(&self, seg: &SplitVec) -> (usize, usize) {
        return (self.start, seg.len());
    }
}

impl FromRange for RangeTo<usize> {
    #[inline(always)]
    fn from_range(&self, _: &SplitVec) -> (usize, usize) {
        return (0, self.end);
    }
}

/// A Vec for large sizes split into smaller blocks.
///
/// Splits a large continuos memory into block sizes to allow insertion/deletion to have an
/// upper bound, based on the block size. It is implemented as a Vec of Vec's, where each Vec
/// has a m inimum and maximum block size. As data is inserted and deleted, from any location,
/// SplitVec will split up and merge the blocks to try and stay between those minimum and maximum
/// block sizes.
pub struct SplitVec {
    vecs: Vec<Vec<u8>>,
    length: usize,
}

#[derive(Copy, Clone, Debug)]
struct Index {
    outer: usize,
    inner: usize,
}

/// An iterator over SplitVec contents.
pub struct Items<'a> {
    seg: &'a SplitVec,
    index: Index,
    num_elem: Option<usize>,
}

/// A mutable iterator over SplitVec contents.
pub struct MutItems<'a> {
    seg: &'a mut SplitVec,
    index: Index,
    num_elem: Option<usize>,
}

/// An iterator over the blocks in SplitVec.
pub struct Slices<'a> {
    seg: &'a SplitVec,
    outer: usize,
}

static MIN_BLOCK_SIZE: usize = 1024 * 1024;
static MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024;

impl SplitVec {
    /// Create a new, empty SplitVec
    pub fn new() -> SplitVec {
        SplitVec {
            vecs: Vec::new(),
            length: 0,
        }
    }

    /// Create a SplitVec by consuming a vec as the initial data vector
    pub fn from_vec(values: Vec<u8>) -> SplitVec {
        let len = values.len();
        SplitVec {
            vecs: vec![values],
            length: len,
        }
    }

    /// Create a SplitVec by copying in values from a slice
    pub fn from_slice(values: &[u8]) -> SplitVec {
        SplitVec {
            vecs: vec![values.into()],
            length: values.len(),
        }
    }

    /// Return the length.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Update the saved length value so that the len func will be -O(1)
    fn calc_len(&mut self) {
        self.length = 0;
        for len in self.vecs.iter().map(|v| v.len()) {
            self.length += len
        }
    }

    /// Convert a global pos to a locall index
    fn pos_to_index(&self, pos: usize, for_insert: bool) -> Index {
        if pos == 0 {
            return Index { outer: 0, inner: 0 };
        }

        let mut cur_pos = pos;
        for (i, vec) in self.vecs.iter().enumerate() {
            if cur_pos < vec.len() || (for_insert && cur_pos == vec.len()) {
                return Index {
                    outer: i,
                    inner: cur_pos,
                }
            }
            cur_pos -= vec.len();
        }

        panic!("Position {} is out of bounds", pos);
    }

    /// Give an iterator over a given range
    pub fn iter_range<'a, R: FromRange>(&'a self, range: R) -> Items<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        Items {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Give a mutable iterator over a given range
    pub fn mut_iter_range<'a, R: FromRange>(&'a mut self, range: R) -> MutItems<'a> {
        let (from, to) = range.from_range(self);
        if to < from {
            panic!("to ({}) is smaller than from ({})!", to, from);
        }

        let idx = self.pos_to_index(from, false);
        MutItems {
            seg: self,
            index: idx,
            num_elem: Some(to - from),
        }
    }

    /// Provide an iterator over continuous memory slices
    ///
    /// This is useful for doing an efficient save to disk
    pub fn iter_slices<'a>(&'a self) -> Slices<'a> {
        Slices {
            seg: self,
            outer: 0,
        }
    }

    /// Prepare an index for future text insertion, splitting/merging big/small sections respectively
    fn prepare_insert(&mut self, index: Index) -> Index {
        if index.outer >= self.vecs.len() {
            self.vecs.push(Vec::new());
        }

        if self.vecs[index.outer].len() < MAX_BLOCK_SIZE {
            return index;
        }

        let page_start_idx = (index.inner / MIN_BLOCK_SIZE) * MIN_BLOCK_SIZE;
        if page_start_idx == 0 {
            if self.vecs[index.outer].len() > MAX_BLOCK_SIZE {
                let insert_vec: Vec < _ >= self.vecs[index.outer][MIN_BLOCK_SIZE..].into();
                self.vecs.insert(index.outer + 1, insert_vec);
                self.vecs[index.outer].truncate(MIN_BLOCK_SIZE);
            }

            return index;
        } else {
            let insert_vec: Vec<_> = self.vecs[index.outer][page_start_idx..].into();
            self.vecs.insert(index.outer + 1, insert_vec);
            self.vecs[index.outer].truncate(page_start_idx);
            return self.prepare_insert(Index {
                outer: index.outer + 1,
                inner: index.inner - page_start_idx
            })
        }
    }

    /// insert all values from a slice at an offset.
    pub fn insert(&mut self, offset: usize, values: &[u8]) {
        let mut index = self.pos_to_index(offset, true);
        index = self.prepare_insert(index);

        // This is needed for the mut borrow vec
        {
            self.vecs[index.outer].splice(index.inner..index.inner, values.into_iter().cloned());
        }

        self.calc_len();
    }

    /// Moves data out from the supplied range.
    pub fn move_out<R: FromRange>(&mut self, range: R) -> Vec<u8> {
        let (from, to) = range.from_range(self);
        // TODO: Convert to drain when that settles
        assert!(from <= to);
        let mut res = Vec::new();
        let mut index = self.pos_to_index(from, false);
        let num_elem = to - from;

        for _ in 0..num_elem {
            let c = self.vecs[index.outer].remove(index.inner);
            res.push(c);

            if index.inner >= self.vecs[index.outer].len() {
                if self.vecs[index.outer].len() == 0 {
                    self.vecs.remove(index.outer);
                } else {
                    index.inner = 0;
                    index.outer += 1;
                }
            }
        }

        self.calc_len();

        res
    }

    /// Produce of copy of the supplied range
    pub fn copy_out<R: FromRange>(&mut self, range: R) -> Vec<u8> {
        let (from, to) = range.from_range(self);

        // TODO: Make this use direct cocpy rather than iterators
        self.iter_range(from..to).map(|x| *x).collect::<Vec<u8>>()
    }

    /// Copy data from a slice in.
    pub fn copy_in(&mut self, offset: usize, val: &[u8]) {
        for (s, d) in val.iter().zip(self.mut_iter_range(offset..(offset + val.len()))) {
            *d = s.clone();
        }
    }

    /// Replace values in range with the supplied values
    pub fn splice<R: FromRange>(&mut self, range: R, values: &[u8]) -> Vec<u8> {
        // TODO: This is terribly inefficient, will need a reimplementation
        let (from, to) = range.from_range(self);
        let res;

        // Make sure that when we pull data out for the splice, we don't go over the end
        if from < self.len() {
            let move_end = cmp::min(self.len(), to);
            res = self.move_out(from..move_end);
        } else {
            res = vec![];
        }

        self.insert(from, values);

        res
    }

    /// Find a slice.
    pub fn find_slice(&self, needle: &[u8]) -> Option<usize> {
        self.find_slice_from(0, needle)
    }

    /// Find a slice from a certain index and onward
    pub fn find_slice_from(&self, from: usize, needle: &[u8]) -> Option<usize> {
        for i in from..self.len() {
            if itertools::equal(self.iter_range(i..i+needle.len()), needle.iter()) {
                return Some(i);
            }
        }
        None
    }

    #[cfg(test)]
    fn get_lengths(&self) -> Vec<usize> {
        self.vecs.iter().map(|v| v.len()).collect::<Vec<usize>>()
    }
}

impl ops::Index<usize> for SplitVec {
    type Output = u8;
    fn index<'a>(&'a self, _index: usize) -> &'a u8 {
        let idx = self.pos_to_index(_index, false);
        &self.vecs[idx.outer][idx.inner]
    }
}

impl ops::IndexMut<usize> for SplitVec {
    fn index_mut<'a>(&'a mut self, _index: usize) -> &'a mut u8 {
        let idx = self.pos_to_index(_index, false);
        &mut self.vecs[idx.outer][idx.inner]
    }
}

impl fmt::Debug for SplitVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.vecs.fmt(f)
    }
}

impl<'a> Iterator for Items<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<&'a u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem = {
            let vv = &self.seg.vecs[self.index.outer];
            &vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(elem)
    }
}

impl<'a> Iterator for MutItems<'a> {
    type Item = &'a mut u8;
    fn next(&mut self) -> Option<&'a mut u8> {
        if self.index.outer >= self.seg.vecs.len() {
            return None;
        }
        if let Some(ref mut num_elem) = self.num_elem {
            if *num_elem <= 0 {
                return None;
            }
            *num_elem -= 1;
        }

        let elem_raw: *mut u8 = {
            let vv = &mut self.seg.vecs[self.index.outer];
            &mut vv[self.index.inner]
        };

        self.index.inner += 1;
        if self.index.inner >= self.seg.vecs[self.index.outer].len() {
            self.index.inner = 0;
            self.index.outer += 1;
        }

        Some(unsafe { &mut *elem_raw })
    }
}

impl<'a> Iterator for Slices<'a> {
    type Item = &'a [u8];
    fn next(&mut self) -> Option<&'a [u8]> {
        if self.outer >= self.seg.vecs.len() {
            None
        } else {
            let i = self.outer;
            self.outer += 1;
            Some(&self.seg.vecs[i])
        }
    }
}

#[test]
fn test_small_splitvec() {
    let size = 1024;
    let mut seg = SplitVec::from_vec(vec![1, 2, 3, 4, 5]);
    assert_eq!(Some(4), seg.find_slice(&[5]));

    let seg_len = seg.len();
    seg.insert(seg_len/2, &vec![1 as u8; size]);

    assert_eq!(Some(size + 4), seg.find_slice(&[5]));
}

#[test]
fn test_large_splitvec() {
    let big_size = 4*1024*1024;
    let small_size = 1024;
    let mut seg = SplitVec::from_vec(vec![0; big_size]);

    seg.insert(big_size/2, &vec![1 as u8; small_size]);

    assert_eq!(Some(big_size/2 -1), seg.find_slice(&[0, 1]));

    // Make sure we actually tested a "split" version
    let seg_lengths = seg.get_lengths();
    assert_eq!(2, seg_lengths.len());
    let index = seg_lengths[0];
    let sentinal = 100;
    seg[index] = sentinal;
    seg[index+1] = sentinal +1;
    assert_eq!(Some(index), seg.find_slice(&[sentinal, sentinal+1]));
}
<|endoftext|>


<commit_before>extern crate time;

use std::fs::File;
use std::io::Result;
use std::io::Write;
use std::path::Path;
use std::process;

use time::Timespec;

use wbs::backup::config::*;
use wbs::backup::state::*;
use wbs::backup::time::*;

pub fn run_script (
	name: &str,
	script: &str,
	log: &str,
	time: &str,
) -> process::ExitStatus {

	let process_output =
		process::Command::new (script)
		.arg (time)
		.output ()
		.unwrap_or_else (
			|err|

			panic! (
				"error running script {}: {}",
				script,
				err)

		);

	let output_path_str =
		format! (
			"{}-{}.log",
			log,
			time);

	let output_path =
		Path::new (& output_path_str);

	let mut output_file =
		File::create (
			& output_path
		).unwrap_or_else (
			|err|

			panic! (
				"error creating {} log {}: {}",
				name,
				output_path.display (),
				err)

		);

	write_process_output (
		&mut output_file,
		& process_output,
	).unwrap_or_else (
		|err|

		panic! (
			"error writing script output {}: {}",
			script,
			err)

	);

	process_output.status

}

pub fn write_process_output (
	output_file: &mut File,
	process_output: & process::Output,
) -> Result<()> {

	try! {
		write! (
			output_file,
			"STDOUT:\n{:?}\n",
			process_output.stdout)
	}

	try! {
		write! (
			output_file,
			"STDERR:\n{:?}\n",
			process_output.stderr)
	}

	Ok (())

}

pub fn do_sync (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	sync_time: Timespec
) {

	let job_config = & config.jobs [job_index];

	if job_config.sync_script.is_some () {

		log! (
			"sync started for {} {}",
			job_config.name,
			time_format_pretty (sync_time));

		state.jobs [job_index].state =
			JobState::Syncing;

		state.write_state (config);

		let sync_script =
			job_config.sync_script.clone ().unwrap ();

		let sync_log =
			job_config.sync_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"sync",
				& sync_script,
				& sync_log,
				& time_format_hour (sync_time));

		log! (
			"sync for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_sync =
			Some (sync_time);

		state.write_state (config);

	} else {

		log! (
			"sync skipped for {} {}",
			job_config.name,
			time_format_pretty (sync_time));

		state.jobs [job_index].last_sync =
			Some (sync_time);

		state.write_state (config);

	}

}

pub fn do_snapshot (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	snapshot_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.snapshot_script.is_some () {

		log! (
			"snapshot started for {} {}",
			job_config.name,
			time_format_pretty (snapshot_time));

		state.jobs [job_index].state =
			JobState::Snapshotting;

		let snapshot_index =
			state.jobs [job_index].snapshots.len ();

		state.jobs [job_index].snapshots.push (
			Snapshot {
				state: SnapshotState::Snapshotting,
				snapshot_time: snapshot_time,
				send_time: None,
			}
		);

		state.write_state (config);

		let snapshot_script =
			job_config.snapshot_script.clone ().unwrap ();

		let snapshot_log =
			job_config.snapshot_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"snapshot",
				& snapshot_script,
				& snapshot_log,
				& time_format_day (snapshot_time));

		log! (
			"snapshot for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_snapshot =
			Some (snapshot_time);

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Snapshotted;

		state.write_state (config);

	} else {

		log! (
			"snapshot skipped for {} {}",
			job_config.name,
			time_format_pretty (snapshot_time));

		state.jobs [job_index].last_snapshot =
			Some (snapshot_time);

		state.write_state (config);

	}

}

pub fn do_send (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	send_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.send_script.is_some () {

		log! (
			"send started for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		let mut snapshot_indexes: Vec <usize> = vec! [];

		for (snapshot_index, snapshot)
			in state.jobs [job_index].snapshots.iter ().enumerate () {

			match snapshot.state {

				SnapshotState::Snapshotted => {

					snapshot_indexes.push (snapshot_index)

				},

				_ => {},

			}

		}

		for snapshot_index in snapshot_indexes {

			do_send_snapshot (
				config,
				state,
				job_index,
				snapshot_index,
				send_time);

		}

	} else {

		log! (
			"send skipped for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].last_send =
			Some (send_time);

		state.write_state (config);

	}

}

pub fn do_send_snapshot (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	snapshot_index: usize,
	send_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.send_script.is_some () {

		log! (
			"send started for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].state =
			JobState::Sending;

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Sending;

		state.jobs [job_index].snapshots [snapshot_index].send_time =
			Some (send_time);

		state.write_state (config);

		let snapshot_script =
			job_config.snapshot_script.clone ().unwrap ();

		let snapshot_log =
			job_config.snapshot_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"send",
				& snapshot_script,
				& snapshot_log,
				& time_format_day (send_time));

		log! (
			"snapshot for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_send =
			Some (send_time);

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Sent;

		state.write_state (config);

	} else {

		log! (
			"send skipped for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].last_send =
			Some (send_time);

		state.write_state (config);

	}

}

fn exit_report (
	exit_status: process::ExitStatus,
) -> String {

	if exit_status.success () {

		format! (
			"ended successfully")

	} else {

		match exit_status.code () {

			Some (status) => {

				format! (
					"ended with status {}",
					status)

			},

			None => {

				format! (
					"terminated by signal")

			}

		}

	}

}
<commit_msg>fix for backup send script execution<commit_after>extern crate time;

use std::fs::File;
use std::io::Result;
use std::io::Write;
use std::path::Path;
use std::process;

use time::Timespec;

use wbs::backup::config::*;
use wbs::backup::state::*;
use wbs::backup::time::*;

pub fn run_script (
	name: &str,
	script: &str,
	log: &str,
	time: &str,
) -> process::ExitStatus {

	let process_output =
		process::Command::new (script)
		.arg (time)
		.output ()
		.unwrap_or_else (
			|err|

			panic! (
				"error running script {}: {}",
				script,
				err)

		);

	let output_path_str =
		format! (
			"{}-{}.log",
			log,
			time);

	let output_path =
		Path::new (& output_path_str);

	let mut output_file =
		File::create (
			& output_path
		).unwrap_or_else (
			|err|

			panic! (
				"error creating {} log {}: {}",
				name,
				output_path.display (),
				err)

		);

	write_process_output (
		&mut output_file,
		& process_output,
	).unwrap_or_else (
		|err|

		panic! (
			"error writing script output {}: {}",
			script,
			err)

	);

	process_output.status

}

pub fn write_process_output (
	output_file: &mut File,
	process_output: & process::Output,
) -> Result<()> {

	try! {
		write! (
			output_file,
			"STDOUT:\n{:?}\n",
			process_output.stdout)
	}

	try! {
		write! (
			output_file,
			"STDERR:\n{:?}\n",
			process_output.stderr)
	}

	Ok (())

}

pub fn do_sync (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	sync_time: Timespec
) {

	let job_config = & config.jobs [job_index];

	if job_config.sync_script.is_some () {

		log! (
			"sync started for {} {}",
			job_config.name,
			time_format_pretty (sync_time));

		state.jobs [job_index].state =
			JobState::Syncing;

		state.write_state (config);

		let sync_script =
			job_config.sync_script.clone ().unwrap ();

		let sync_log =
			job_config.sync_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"sync",
				& sync_script,
				& sync_log,
				& time_format_hour (sync_time));

		log! (
			"sync for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_sync =
			Some (sync_time);

		state.write_state (config);

	} else {

		log! (
			"sync skipped for {} {}",
			job_config.name,
			time_format_pretty (sync_time));

		state.jobs [job_index].last_sync =
			Some (sync_time);

		state.write_state (config);

	}

}

pub fn do_snapshot (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	snapshot_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.snapshot_script.is_some () {

		log! (
			"snapshot started for {} {}",
			job_config.name,
			time_format_pretty (snapshot_time));

		state.jobs [job_index].state =
			JobState::Snapshotting;

		let snapshot_index =
			state.jobs [job_index].snapshots.len ();

		state.jobs [job_index].snapshots.push (
			Snapshot {
				state: SnapshotState::Snapshotting,
				snapshot_time: snapshot_time,
				send_time: None,
			}
		);

		state.write_state (config);

		let snapshot_script =
			job_config.snapshot_script.clone ().unwrap ();

		let snapshot_log =
			job_config.snapshot_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"snapshot",
				& snapshot_script,
				& snapshot_log,
				& time_format_day (snapshot_time));

		log! (
			"snapshot for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_snapshot =
			Some (snapshot_time);

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Snapshotted;

		state.write_state (config);

	} else {

		log! (
			"snapshot skipped for {} {}",
			job_config.name,
			time_format_pretty (snapshot_time));

		state.jobs [job_index].last_snapshot =
			Some (snapshot_time);

		state.write_state (config);

	}

}

pub fn do_send (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	send_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.send_script.is_some () {

		log! (
			"send started for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		let mut snapshot_indexes: Vec <usize> = vec! [];

		for (snapshot_index, snapshot)
			in state.jobs [job_index].snapshots.iter ().enumerate () {

			match snapshot.state {

				SnapshotState::Snapshotted => {

					snapshot_indexes.push (snapshot_index)

				},

				_ => {},

			}

		}

		for snapshot_index in snapshot_indexes {

			do_send_snapshot (
				config,
				state,
				job_index,
				snapshot_index,
				send_time);

		}

	} else {

		log! (
			"send skipped for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].last_send =
			Some (send_time);

		state.write_state (config);

	}

}

pub fn do_send_snapshot (
	config: & Config,
	state: &mut Global,
	job_index: usize,
	snapshot_index: usize,
	send_time: Timespec,
) {

	let job_config = & config.jobs [job_index];

	if job_config.send_script.is_some () {

		log! (
			"send started for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].state =
			JobState::Sending;

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Sending;

		state.jobs [job_index].snapshots [snapshot_index].send_time =
			Some (send_time);

		state.write_state (config);

		let send_script =
			job_config.send_script.clone ().unwrap ();

		let send_log =
			job_config.send_log.clone ().unwrap ();

		let exit_status =
			run_script (
				"send",
				& send_script,
				& send_log,
				& time_format_day (send_time));

		log! (
			"send completed for {} {}",
			job_config.name,
			exit_report (exit_status));

		state.jobs [job_index].state =
			JobState::Idle;

		state.jobs [job_index].last_send =
			Some (send_time);

		state.jobs [job_index].snapshots [snapshot_index].state =
			SnapshotState::Sent;

		state.write_state (config);

	} else {

		log! (
			"send skipped for {} {}",
			job_config.name,
			time_format_pretty (send_time));

		state.jobs [job_index].last_send =
			Some (send_time);

		state.write_state (config);

	}

}

fn exit_report (
	exit_status: process::ExitStatus,
) -> String {

	if exit_status.success () {

		format! (
			"ended successfully")

	} else {

		match exit_status.code () {

			Some (status) => {

				format! (
					"ended with status {}",
					status)

			},

			None => {

				format! (
					"terminated by signal")

			}

		}

	}

}
<|endoftext|>


<commit_before>use std::path::MAIN_SEPARATOR as SEP;

use support::{project, execs, path2url};
use support::{COMPILING, RUNNING};
use hamcrest::{assert_that, existing_file};

fn setup() {
}

test!(simple {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}foo[..]`
hello
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
    assert_that(&p.bin("foo"), existing_file());
});

test!(simple_quiet {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run").arg("-q"),
                execs().with_status(0).with_stdout("\
hello
")
    );
});

test!(simple_quiet_and_verbose {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run").arg("-q").arg("-v"),
                execs().with_status(101).with_stderr("\
cannot set both --verbose and --quiet
")
    );
});

test!(simple_with_args {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() {
                assert_eq!(std::env::args().nth(1).unwrap(), "hello");
                assert_eq!(std::env::args().nth(2).unwrap(), "world");
            }
        "#);

    assert_that(p.cargo_process("run").arg("hello").arg("world"),
                execs().with_status(0));
});

test!(exit_code {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { std::process::exit(2); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(2));
});

test!(no_main_file {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "");

    assert_that(p.cargo_process("run"),
                execs().with_status(101)
                       .with_stderr("a bin target must be available \
                                     for `cargo run`\n"));
});

test!(too_many_bins {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/a.rs", "")
        .file("src/bin/b.rs", "");

    assert_that(p.cargo_process("run"),
                execs().with_status(101)
                       .with_stderr("`cargo run` requires that a project only \
                                     have one executable; use the `--bin` option \
                                     to specify which one to run\n"));
});

test!(specify_name {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/a.rs", r#"
            extern crate foo;
            fn main() { println!("hello a.rs"); }
        "#)
        .file("src/bin/b.rs", r#"
            extern crate foo;
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run").arg("--bin").arg("a").arg("-v"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `rustc src[..]lib.rs [..]`
{running} `rustc src[..]a.rs [..]`
{running} `target{sep}debug{sep}a[..]`
hello a.rs
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));

    assert_that(p.cargo("run").arg("--bin").arg("b").arg("-v"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ([..])
{running} `rustc src[..]b.rs [..]`
{running} `target{sep}debug{sep}b[..]`
hello b.rs
",
        running = RUNNING, compiling = COMPILING,
        sep = SEP)));
});

test!(run_example {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("examples/a.rs", r#"
            fn main() { println!("example"); }
        "#)
        .file("src/bin/a.rs", r#"
            fn main() { println!("bin"); }
        "#);

    assert_that(p.cargo_process("run").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}examples{sep}a[..]`
example
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
});

test!(either_name_or_example {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/bin/a.rs", r#"
            fn main() { println!("hello a.rs"); }
        "#)
        .file("examples/b.rs", r#"
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run").arg("--bin").arg("a").arg("--example").arg("b"),
                execs().with_status(101)
                       .with_stderr("`cargo run` can run at most one \
                                     executable, but multiple were \
                                     specified"));
});

test!(one_bin_multiple_examples {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/main.rs", r#"
            fn main() { println!("hello main.rs"); }
        "#)
        .file("examples/a.rs", r#"
            fn main() { println!("hello a.rs"); }
        "#)
        .file("examples/b.rs", r#"
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}main[..]`
hello main.rs
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
});

test!(example_with_release_flag {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            version = "*"
            path = "bar"
        "#)
        .file("examples/a.rs", r#"
            extern crate bar;

            fn main() {
                if cfg!(debug_assertions) {
                    println!("slow1")
                } else {
                    println!("fast1")
                }
                bar::baz();
            }
        "#)
        .file("bar/Cargo.toml", r#"
            [project]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
        "#)
        .file("bar/src/bar.rs", r#"
            pub fn baz() {
                if cfg!(debug_assertions) {
                    println!("slow2")
                } else {
                    println!("fast2")
                }
            }
        "#);

    assert_that(p.cargo_process("run").arg("-v").arg("--release").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}bar.rs --crate-name bar --crate-type lib \
        -C opt-level=3 \
        -C metadata=[..] \
        -C extra-filename=[..] \
        --out-dir {dir}{sep}target{sep}release{sep}deps \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}release{sep}deps \
        -L dependency={dir}{sep}target{sep}release{sep}deps`
{compiling} foo v0.0.1 ({url})
{running} `rustc examples{sep}a.rs --crate-name a --crate-type bin \
        -C opt-level=3 \
        --out-dir {dir}{sep}target{sep}release{sep}examples \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}release \
        -L dependency={dir}{sep}target{sep}release{sep}deps \
         --extern bar={dir}{sep}target{sep}release{sep}deps{sep}libbar-[..].rlib`
{running} `target{sep}release{sep}examples{sep}a[..]`
fast1
fast2
",
        compiling = COMPILING,
        running = RUNNING,
        dir = p.root().display(),
        url = path2url(p.root()),
        sep = SEP)));

    assert_that(p.cargo("run").arg("-v").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}bar.rs --crate-name bar --crate-type lib \
        -g \
        -C metadata=[..] \
        -C extra-filename=[..] \
        --out-dir {dir}{sep}target{sep}debug{sep}deps \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}debug{sep}deps \
        -L dependency={dir}{sep}target{sep}debug{sep}deps`
{compiling} foo v0.0.1 ({url})
{running} `rustc examples{sep}a.rs --crate-name a --crate-type bin \
        -g \
        --out-dir {dir}{sep}target{sep}debug{sep}examples \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}debug \
        -L dependency={dir}{sep}target{sep}debug{sep}deps \
         --extern bar={dir}{sep}target{sep}debug{sep}deps{sep}libbar-[..].rlib`
{running} `target{sep}debug{sep}examples{sep}a[..]`
slow1
slow2
",
        compiling = COMPILING,
        running = RUNNING,
        dir = p.root().display(),
        url = path2url(p.root()),
        sep = SEP)));
});

test!(run_dylib_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            path = "bar"
        "#)
        .file("src/main.rs", r#"
            extern crate bar;
            fn main() { bar::bar(); }
        "#)
        .file("bar/Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            crate-type = ["dylib"]
        "#)
        .file("bar/src/lib.rs", "pub fn bar() {}");

    assert_that(p.cargo_process("run").arg("hello").arg("world"),
                execs().with_status(0));
});

test!(release_works {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { if cfg!(debug_assertions) { panic!() } }
        "#);

    assert_that(p.cargo_process("run").arg("--release"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}release{sep}foo[..]`
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
    assert_that(&p.release_bin("foo"), existing_file());
});

test!(run_bin_different_name {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "bar"
        "#)
        .file("src/bar.rs", r#"
            fn main() { }
        "#);

    assert_that(p.cargo_process("run"), execs().with_status(0));
});

test!(dashes_are_forwarded {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "bar"
        "#)
        .file("src/main.rs", r#"
            fn main() {
                let s: Vec<String> = std::env::args().collect();
                assert_eq!(s[1], "a");
                assert_eq!(s[2], "--");
                assert_eq!(s[3], "b");
            }
        "#);

    assert_that(p.cargo_process("run").arg("--").arg("a").arg("--").arg("b"),
                execs().with_status(0));
});
<commit_msg>Added test for issue #1942<commit_after>use std::path::MAIN_SEPARATOR as SEP;

use support::{project, execs, path2url};
use support::{COMPILING, RUNNING};
use hamcrest::{assert_that, existing_file};

fn setup() {
}

test!(simple {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}foo[..]`
hello
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
    assert_that(&p.bin("foo"), existing_file());
});

test!(simple_quiet {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run").arg("-q"),
                execs().with_status(0).with_stdout("\
hello
")
    );
});

test!(simple_quiet_and_verbose {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    assert_that(p.cargo_process("run").arg("-q").arg("-v"),
                execs().with_status(101).with_stderr("\
cannot set both --verbose and --quiet
")
    );
});

test!(simple_with_args {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() {
                assert_eq!(std::env::args().nth(1).unwrap(), "hello");
                assert_eq!(std::env::args().nth(2).unwrap(), "world");
            }
        "#);

    assert_that(p.cargo_process("run").arg("hello").arg("world"),
                execs().with_status(0));
});

test!(exit_code {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { std::process::exit(2); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(2));
});

test!(no_main_file {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "");

    assert_that(p.cargo_process("run"),
                execs().with_status(101)
                       .with_stderr("a bin target must be available \
                                     for `cargo run`\n"));
});

test!(too_many_bins {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/a.rs", "")
        .file("src/bin/b.rs", "");

    assert_that(p.cargo_process("run"),
                execs().with_status(101)
                       .with_stderr("`cargo run` requires that a project only \
                                     have one executable; use the `--bin` option \
                                     to specify which one to run\n"));
});

test!(specify_name {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/a.rs", r#"
            extern crate foo;
            fn main() { println!("hello a.rs"); }
        "#)
        .file("src/bin/b.rs", r#"
            extern crate foo;
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run").arg("--bin").arg("a").arg("-v"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `rustc src[..]lib.rs [..]`
{running} `rustc src[..]a.rs [..]`
{running} `target{sep}debug{sep}a[..]`
hello a.rs
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));

    assert_that(p.cargo("run").arg("--bin").arg("b").arg("-v"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ([..])
{running} `rustc src[..]b.rs [..]`
{running} `target{sep}debug{sep}b[..]`
hello b.rs
",
        running = RUNNING, compiling = COMPILING,
        sep = SEP)));
});

test!(run_example {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("examples/a.rs", r#"
            fn main() { println!("example"); }
        "#)
        .file("src/bin/a.rs", r#"
            fn main() { println!("bin"); }
        "#);

    assert_that(p.cargo_process("run").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}examples{sep}a[..]`
example
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
});

test!(either_name_or_example {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/bin/a.rs", r#"
            fn main() { println!("hello a.rs"); }
        "#)
        .file("examples/b.rs", r#"
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run").arg("--bin").arg("a").arg("--example").arg("b"),
                execs().with_status(101)
                       .with_stderr("`cargo run` can run at most one \
                                     executable, but multiple were \
                                     specified"));
});

test!(one_bin_multiple_examples {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .file("src/bin/main.rs", r#"
            fn main() { println!("hello main.rs"); }
        "#)
        .file("examples/a.rs", r#"
            fn main() { println!("hello a.rs"); }
        "#)
        .file("examples/b.rs", r#"
            fn main() { println!("hello b.rs"); }
        "#);

    assert_that(p.cargo_process("run"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}debug{sep}main[..]`
hello main.rs
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
});

test!(example_with_release_flag {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            version = "*"
            path = "bar"
        "#)
        .file("examples/a.rs", r#"
            extern crate bar;

            fn main() {
                if cfg!(debug_assertions) {
                    println!("slow1")
                } else {
                    println!("fast1")
                }
                bar::baz();
            }
        "#)
        .file("bar/Cargo.toml", r#"
            [project]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
        "#)
        .file("bar/src/bar.rs", r#"
            pub fn baz() {
                if cfg!(debug_assertions) {
                    println!("slow2")
                } else {
                    println!("fast2")
                }
            }
        "#);

    assert_that(p.cargo_process("run").arg("-v").arg("--release").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}bar.rs --crate-name bar --crate-type lib \
        -C opt-level=3 \
        -C metadata=[..] \
        -C extra-filename=[..] \
        --out-dir {dir}{sep}target{sep}release{sep}deps \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}release{sep}deps \
        -L dependency={dir}{sep}target{sep}release{sep}deps`
{compiling} foo v0.0.1 ({url})
{running} `rustc examples{sep}a.rs --crate-name a --crate-type bin \
        -C opt-level=3 \
        --out-dir {dir}{sep}target{sep}release{sep}examples \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}release \
        -L dependency={dir}{sep}target{sep}release{sep}deps \
         --extern bar={dir}{sep}target{sep}release{sep}deps{sep}libbar-[..].rlib`
{running} `target{sep}release{sep}examples{sep}a[..]`
fast1
fast2
",
        compiling = COMPILING,
        running = RUNNING,
        dir = p.root().display(),
        url = path2url(p.root()),
        sep = SEP)));

    assert_that(p.cargo("run").arg("-v").arg("--example").arg("a"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} bar v0.0.1 ({url})
{running} `rustc bar{sep}src{sep}bar.rs --crate-name bar --crate-type lib \
        -g \
        -C metadata=[..] \
        -C extra-filename=[..] \
        --out-dir {dir}{sep}target{sep}debug{sep}deps \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}debug{sep}deps \
        -L dependency={dir}{sep}target{sep}debug{sep}deps`
{compiling} foo v0.0.1 ({url})
{running} `rustc examples{sep}a.rs --crate-name a --crate-type bin \
        -g \
        --out-dir {dir}{sep}target{sep}debug{sep}examples \
        --emit=dep-info,link \
        -L dependency={dir}{sep}target{sep}debug \
        -L dependency={dir}{sep}target{sep}debug{sep}deps \
         --extern bar={dir}{sep}target{sep}debug{sep}deps{sep}libbar-[..].rlib`
{running} `target{sep}debug{sep}examples{sep}a[..]`
slow1
slow2
",
        compiling = COMPILING,
        running = RUNNING,
        dir = p.root().display(),
        url = path2url(p.root()),
        sep = SEP)));
});

test!(run_dylib_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            path = "bar"
        "#)
        .file("src/main.rs", r#"
            extern crate bar;
            fn main() { bar::bar(); }
        "#)
        .file("bar/Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            crate-type = ["dylib"]
        "#)
        .file("bar/src/lib.rs", "pub fn bar() {}");

    assert_that(p.cargo_process("run").arg("hello").arg("world"),
                execs().with_status(0));
});

test!(release_works {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { if cfg!(debug_assertions) { panic!() } }
        "#);

    assert_that(p.cargo_process("run").arg("--release"),
                execs().with_status(0).with_stdout(&format!("\
{compiling} foo v0.0.1 ({dir})
{running} `target{sep}release{sep}foo[..]`
",
        compiling = COMPILING,
        running = RUNNING,
        dir = path2url(p.root()),
        sep = SEP)));
    assert_that(&p.release_bin("foo"), existing_file());
});

test!(run_bin_different_name {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "bar"
        "#)
        .file("src/bar.rs", r#"
            fn main() { }
        "#);

    assert_that(p.cargo_process("run"), execs().with_status(0));
});

test!(dashes_are_forwarded {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "bar"
        "#)
        .file("src/main.rs", r#"
            fn main() {
                let s: Vec<String> = std::env::args().collect();
                assert_eq!(s[1], "a");
                assert_eq!(s[2], "--");
                assert_eq!(s[3], "b");
            }
        "#);

    assert_that(p.cargo_process("run").arg("--").arg("a").arg("--").arg("b"),
                execs().with_status(0));
});

test!(run_from_executable_folder {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/main.rs", r#"
            fn main() { println!("hello"); }
        "#);

    let cwd = p.root().join("target").join("debug");
    p.cargo_process("build").exec_with_output().unwrap();

    assert_that(p.cargo("run").cwd(cwd),
                execs().with_status(0).with_stdout(&format!("\
{running} `./foo`
hello
",
        running = RUNNING
        )));
});
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.5")]
#![allow(clippy::needless_doctest_main)]
#![warn(
    missing_debug_implementations,
    missing_docs,
    rust_2018_idioms,
    unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]

//! Macros for use with Tokio

// This `extern` is required for older `rustc` versions but newer `rustc`
// versions warn about the unused `extern crate`.
#[allow(unused_extern_crates)]
extern crate proc_macro;

mod entry;
mod select;

use proc_macro::TokenStream;

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### Set number of core threads
///
/// ```rust
/// #[tokio::main(core_threads = 1)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::main(args, item, true)
}

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread.
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default (requires `rt-threaded` feature).
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### Select runtime
///
/// ```rust
/// #[tokio::main(basic_scheduler)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::old::main(args, item)
}

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::main(args, item, false)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(core_threads = 1)]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::test(args, item, true)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
/// - `threaded_scheduler` - Use multi-threaded scheduler (requires `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(threaded_scheduler)]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::old::test(args, item)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Usage
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
#[proc_macro_attribute]
pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::test(args, item, false)
}

/// Implementation detail of the `select!` macro. This macro is **not** intended
/// to be used as part of the public API and is permitted to change.
#[proc_macro]
#[doc(hidden)]
pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream {
    select::declare_output_enum(input)
}
<commit_msg>tokio-macros: warn about renaming the tokio dependency (#2521)<commit_after>#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.5")]
#![allow(clippy::needless_doctest_main)]
#![warn(
    missing_debug_implementations,
    missing_docs,
    rust_2018_idioms,
    unreachable_pub
)]
#![deny(intra_doc_link_resolution_failure)]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]

//! Macros for use with Tokio

// This `extern` is required for older `rustc` versions but newer `rustc`
// versions warn about the unused `extern crate`.
#[allow(unused_extern_crates)]
extern crate proc_macro;

mod entry;
mod select;

use proc_macro::TokenStream;

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### Set number of core threads
///
/// ```rust
/// #[tokio::main(core_threads = 1)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::main(args, item, true)
}

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread.
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default (requires `rt-threaded` feature).
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### Select runtime
///
/// ```rust
/// #[tokio::main(basic_scheduler)]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::old::main(args, item)
}

/// Marks async function to be executed by selected runtime.
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Function arguments:
///
/// Arguments are allowed for any functions aside from `main` which is special
///
/// ## Usage
///
/// ### Using default
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
///     println!("Hello world");
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::main(args, item, false)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `core_threads=n` - Sets core threads to `n` (requires `rt-threaded` feature).
/// - `max_threads=n` - Sets max threads to `n` (requires `rt-core` or `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(core_threads = 1)]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::test(args, item, true)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
/// - `threaded_scheduler` - Use multi-threaded scheduler (requires `rt-threaded` feature).
///
/// ## Usage
///
/// ### Select runtime
///
/// ```no_run
/// #[tokio::test(threaded_scheduler)]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### Using default
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::old::test(args, item)
}

/// Marks async function to be executed by runtime, suitable to test environment
///
/// ## Options:
///
/// - `max_threads=n` - Sets max threads to `n`.
///
/// ## Usage
///
/// ```no_run
/// #[tokio::test]
/// async fn my_test() {
///     assert!(true);
/// }
/// ```
///
/// ### NOTE:
///
/// If you rename the tokio crate in your dependencies this macro
/// will not work. If you must rename the 0.2 version of tokio because
/// you're also using the 0.1 version of tokio, you _must_ make the
/// tokio 0.2 crate available as `tokio` in the module where this
/// macro is expanded.
#[proc_macro_attribute]
pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream {
    entry::test(args, item, false)
}

/// Implementation detail of the `select!` macro. This macro is **not** intended
/// to be used as part of the public API and is permitted to change.
#[proc_macro]
#[doc(hidden)]
pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream {
    select::declare_output_enum(input)
}
<|endoftext|>


<commit_before>use std::net::url;
use std::net::url::Url;

pub fn build_request(url: Url) -> ~str {

    let host = copy url.host;
    let mut path = if url.path.is_not_empty() { copy url.path } else { ~"/" };

    if url.query.len() > 0 {
        let kvps = do url.query.map |pair| {
            match *pair {
                (key, value) => fmt!("%s=%s", key, value)
            }
        };
        path += ~"?" + str::connect(kvps, "&");
    }

    let request_header = fmt!("GET %s HTTP/1.0\u000D\u000AHost: %s\u000D\u000A\u000D\u000A",
                              path, host);

    return move request_header;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn should_request_slash_when_path_is_empty() {
    let url = url::from_str(~"http://host").get();
    assert url.path.is_empty();
    let headers = build_request(url);
    assert headers.contains(~"GET / ");
}
<commit_msg>Update for language changes<commit_after>use std::net::url;
use std::net::url::Url;

pub fn build_request(url: Url) -> ~str {

    let host = copy url.host;
    let mut path = if url.path.is_not_empty() { copy url.path } else { ~"/" };

    if url.query.len() > 0 {
        let kvps = do url.query.map |pair| {
            match *pair {
                (ref key, ref value) => fmt!("%s=%s", *key, *value)
            }
        };
        path += ~"?" + str::connect(kvps, "&");
    }

    let request_header = fmt!("GET %s HTTP/1.0\u000D\u000AHost: %s\u000D\u000A\u000D\u000A",
                              path, host);

    return move request_header;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn should_request_slash_when_path_is_empty() {
    let url = url::from_str(~"http://host").get();
    assert url.path.is_empty();
    let headers = build_request(url);
    assert headers.contains(~"GET / ");
}
<|endoftext|>