<commit_before>#![crate_name = "uu_pr"]

// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//

#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate quick_error;
extern crate chrono;
extern crate getopts;
extern crate uucore;

use std::io::{BufRead, BufReader, stdin, stdout, stderr, Error, Read, Write, Stdout};
use std::vec::Vec;
use chrono::offset::Local;
use chrono::DateTime;
use getopts::{Matches, Options};
use std::fs::{metadata, File};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use quick_error::ResultExt;

static NAME: &str = "pr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static LINES_PER_PAGE: usize = 66;
static HEADER_LINES_PER_PAGE: usize = 5;
static TRAILER_LINES_PER_PAGE: usize = 5;
static CONTENT_LINES_PER_PAGE: usize = LINES_PER_PAGE - HEADER_LINES_PER_PAGE - TRAILER_LINES_PER_PAGE;
static NUMBERING_MODE_DEFAULT_SEPARATOR: &str = "\t";
static NUMBERING_MODE_DEFAULT_WIDTH: usize = 5;
static STRING_HEADER_OPTION: &str = "h";
static NUMBERING_MODE_OPTION: &str = "n";
static FILE_STDIN: &str = "-";

struct OutputOptions {
    /// Line numbering mode
    number: Option<NumberingMode>,
    header: String,
    double_spaced: bool,
    line_separator: String,
    last_modified_time: String,
}

impl AsRef<OutputOptions> for OutputOptions {
    fn as_ref(&self) -> &OutputOptions {
        self
    }
}

struct NumberingMode {
    /// Line numbering mode
    width: usize,
    separator: String,
}

impl Default for NumberingMode {
    fn default() -> NumberingMode {
        NumberingMode {
            width: NUMBERING_MODE_DEFAULT_WIDTH,
            separator: NUMBERING_MODE_DEFAULT_SEPARATOR.to_string(),
        }
    }
}

enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

quick_error! {
    #[derive(Debug)]
    enum PrError {
        Input(err: Error, path: String) {
            context(path: &'a str, err: Error) -> (err, path.to_owned())
            display("pr: Reading from input {0} gave error", path)
            cause(err)
        }

        UnknownFiletype(path: String) {
            display("pr: {0}: unknown filetype", path)
        }

        EncounteredErrors(msg: String) {
            display("pr: {0} encountered", msg)
        }
        IsDirectory(path: String) {
            display("pr: {0}: Is a directory", path)
        }
    }
}

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

    opts.optopt(
        STRING_HEADER_OPTION,
        "",
        "Use the string header to replace the file name \
     in the header line.",
        "STRING",
    );

    opts.optflag(
        "d",
        "",
        "Produce output that is double spaced. An extra <newline> character is output following every <newline>
           found in the input.",
    );

    opts.optflagopt(
        NUMBERING_MODE_OPTION,
        "",
        "Provide width digit line numbering.  The default for width, if not specified, is 5.  The number occupies
           the first width column positions of each text column or each line of -m output.  If char (any nondigit
           character) is given, it is appended to the line number to separate it from whatever follows.  The default
           for char is a <tab>.  Line numbers longer than width columns are truncated.",
        "[char][width]",
    );

    opts.optflag("", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

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

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

    let mut files: Vec<String> = matches.free.clone();
    if files.is_empty() {
        //For stdin
        files.push(FILE_STDIN.to_owned());
    }

    if matches.opt_present("help") {
        return print_usage(&mut opts, &matches);
    }

    for f in files {
        let header: &String = &matches.opt_str(STRING_HEADER_OPTION).unwrap_or(f.to_string());
        let options: &OutputOptions = &build_options(&matches, header, &f);
        let status: i32 = pr(&f, options);
        if status != 0 {
            return status;
        }
    }
    return 0;
}

fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
    println!("{} {} -- print files", NAME, VERSION);
    println!();
    println!("Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
        [-L locale] [-h header] [[-i] [char] [gap]]
        [-l lines] [-o offset] [[-s] [char]] [[-n] [char]
        [width]] [-w width] [-] [file ...].", NAME);
    println!();
    let usage: &str = "The pr utility is a printing and pagination filter
     for text files.  When multiple input files are spec-
     ified, each is read, formatted, and written to stan-
     dard output.  By default, the input is separated
     into 66-line pages, each with

     o   A 5-line header with the page number, date,
         time, and the pathname of the file.

     o   A 5-line trailer consisting of blank lines.

     If standard output is associated with a terminal,
     diagnostic messages are suppressed until the pr
     utility has completed processing.

     When multiple column output is specified, text col-
     umns are of equal width.  By default text columns
     are separated by at least one <blank>.  Input lines
     that do not fit into a text column are truncated.
     Lines are not truncated under single column output.";
    println!("{}", opts.usage(usage));
    if matches.free.is_empty() {
        return 1;
    }
    return 0;
}

fn build_options(matches: &Matches, header: &String, path: &String) -> OutputOptions {
    let numbering_options: Option<NumberingMode> = matches.opt_str(NUMBERING_MODE_OPTION).map(|i| {
        NumberingMode {
            width: i.parse::<usize>().unwrap_or(NumberingMode::default().width),
            separator: NumberingMode::default().separator,
        }
    }).or_else(|| {
        if matches.opt_present(NUMBERING_MODE_OPTION) {
            return Some(NumberingMode::default());
        }
        return None;
    });

    let line_separator: String = if matches.opt_present("d") {
        "\n\n".to_string()
    } else {
        "\n".to_string()
    };

    let last_modified_time = if path.eq(FILE_STDIN) {
        current_time()
    } else {
        file_last_modified_time(path)
    };

    OutputOptions {
        number: numbering_options,
        header: header.to_string(),
        double_spaced: matches.opt_present("d"),
        line_separator,
        last_modified_time,
    }
}

fn open(path: &str) -> Result<Box<Read>, PrError> {
    match get_input_type(path) {
        Some(InputType::Directory) => Err(PrError::IsDirectory(path.to_string())),
        #[cfg(unix)]
        Some(InputType::Socket) => {
            // TODO Add reading from socket
            Err(PrError::EncounteredErrors("Reading from socket not supported yet".to_string()))
        }
        Some(InputType::StdIn) => {
            let stdin = stdin();
            Ok(Box::new(stdin) as Box<Read>)
        }
        Some(_) => Ok(Box::new(File::open(path).context(path)?) as Box<Read>),
        None => Err(PrError::UnknownFiletype(path.to_string()))
    }
}

fn pr(path: &str, options: &OutputOptions) -> i32 {
    let mut i = 0;
    let mut page: usize = 0;
    let mut buffered_content: Vec<String> = Vec::new();
    match open(path) {
        Ok(reader) => {
            // TODO Replace the loop
            for line in BufReader::new(reader).lines() {
                if i == CONTENT_LINES_PER_PAGE {
                    page = page + 1;
                    i = 0;
                    prepare_page(&mut buffered_content, options, &page);
                }
                match line {
                    Ok(content) => buffered_content.push(content),
                    Err(error) => {
                        writeln!(&mut stderr(), "pr: Unable to read from input type {}\n{}", path, error.to_string());
                        return -1;
                    }
                }
                i = i + 1;
            }

            if i != 0 {
                page = page + 1;
                prepare_page(&mut buffered_content, options, &page);
            }
        }
        Err(error) => {
            writeln!(&mut stderr(), "{}", error);
            return -1;
        }
    }
    return 0;
}

fn print_page(header_content: &Vec<String>, lines: &Vec<String>, options: &OutputOptions, page: &usize) {
    let trailer_content: Vec<String> = trailer_content();
    assert_eq!(lines.len() <= CONTENT_LINES_PER_PAGE, true, "Only {} lines of content allowed in a pr output page", CONTENT_LINES_PER_PAGE);
    assert_eq!(header_content.len(), HEADER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr header", HEADER_LINES_PER_PAGE);
    assert_eq!(trailer_content.len(), TRAILER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr trailer", TRAILER_LINES_PER_PAGE);
    let out: &mut Stdout = &mut stdout();
    let line_separator = options.as_ref().line_separator.as_bytes();

    out.lock();
    for x in header_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }

    let width: usize = options.as_ref()
        .number.as_ref()
        .map(|i| i.width)
        .unwrap_or(0);
    let separator: String = options.as_ref()
        .number.as_ref()
        .map(|i| i.separator.to_string())
        .unwrap_or(NumberingMode::default().separator);

    let prev_lines = CONTENT_LINES_PER_PAGE * (page - 1);
    let mut i = 1;
    for x in lines {
        if options.number.is_none() {
            out.write(x.as_bytes());
        } else {
            let fmtd_line_number: String = get_fmtd_line_number(&width, prev_lines + i, &separator);
            out.write(format!("{}{}", fmtd_line_number, x).as_bytes());
        }
        out.write(line_separator);
        i = i + 1;
    }
    for x in trailer_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }
    out.flush();
}

fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
    let line_str = line_number.to_string();
    if line_str.len() >= *width {
        format!("{:>width$}{}", &line_str[line_str.len() - *width..], separator, width = width)
    } else {
        format!("{:>width$}{}", line_str, separator, width = width)
    }
}

fn prepare_page(buffered_content: &mut Vec<String>, options: &OutputOptions, page: &usize) {
    let header: Vec<String> = header_content(&options, &page);
    print_page(&header, buffered_content, &options, &page);
    buffered_content.clear();
}

fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
    let first_line: String = format!("{} {} Page {}", options.last_modified_time, options.header, page);
    vec!["".to_string(), "".to_string(), first_line, "".to_string(), "".to_string()]
}

fn file_last_modified_time(path: &str) -> String {
    let file_metadata = metadata(path);
    return file_metadata.map(|i| {
        return i.modified().map(|x| {
            let datetime: DateTime<Local> = x.into();
            datetime.format("%b %d %H:%M %Y").to_string()
        }).unwrap_or(String::new());
    }).unwrap_or(String::new());
}

fn current_time() -> String {
    let datetime: DateTime<Local> = Local::now();
    datetime.format("%b %d %H:%M %Y").to_string()
}

fn trailer_content() -> Vec<String> {
    vec!["".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string()]
}

fn get_input_type(path: &str) -> Option<InputType> {
    if path == FILE_STDIN {
        return Some(InputType::StdIn);
    }

    metadata(path).map(|i| {
        match i.file_type() {
            #[cfg(unix)]
            ft if ft.is_block_device() =>
                {
                    Some(InputType::BlockDevice)
                }
            #[cfg(unix)]
            ft if ft.is_char_device() =>
                {
                    Some(InputType::CharacterDevice)
                }
            #[cfg(unix)]
            ft if ft.is_fifo() =>
                {
                    Some(InputType::Fifo)
                }
            #[cfg(unix)]
            ft if ft.is_socket() =>
                {
                    Some(InputType::Socket)
                }
            ft if ft.is_dir() => Some(InputType::Directory),
            ft if ft.is_file() => Some(InputType::File),
            ft if ft.is_symlink() => Some(InputType::SymLink),
            _ => None
        }
    }).unwrap_or(None)
}
<commit_msg>pr: throw error on reading from socket<commit_after>#![crate_name = "uu_pr"]

// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//

#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate quick_error;
extern crate chrono;
extern crate getopts;
extern crate uucore;

use std::io::{BufRead, BufReader, stdin, stdout, stderr, Error, Read, Write, Stdout};
use std::vec::Vec;
use chrono::offset::Local;
use chrono::DateTime;
use getopts::{Matches, Options};
use std::fs::{metadata, File};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use quick_error::ResultExt;

static NAME: &str = "pr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static LINES_PER_PAGE: usize = 66;
static HEADER_LINES_PER_PAGE: usize = 5;
static TRAILER_LINES_PER_PAGE: usize = 5;
static CONTENT_LINES_PER_PAGE: usize = LINES_PER_PAGE - HEADER_LINES_PER_PAGE - TRAILER_LINES_PER_PAGE;
static NUMBERING_MODE_DEFAULT_SEPARATOR: &str = "\t";
static NUMBERING_MODE_DEFAULT_WIDTH: usize = 5;
static STRING_HEADER_OPTION: &str = "h";
static NUMBERING_MODE_OPTION: &str = "n";
static FILE_STDIN: &str = "-";

struct OutputOptions {
    /// Line numbering mode
    number: Option<NumberingMode>,
    header: String,
    double_spaced: bool,
    line_separator: String,
    last_modified_time: String,
}

impl AsRef<OutputOptions> for OutputOptions {
    fn as_ref(&self) -> &OutputOptions {
        self
    }
}

struct NumberingMode {
    /// Line numbering mode
    width: usize,
    separator: String,
}

impl Default for NumberingMode {
    fn default() -> NumberingMode {
        NumberingMode {
            width: NUMBERING_MODE_DEFAULT_WIDTH,
            separator: NUMBERING_MODE_DEFAULT_SEPARATOR.to_string(),
        }
    }
}

enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

quick_error! {
    #[derive(Debug)]
    enum PrError {
        Input(err: Error, path: String) {
            context(path: &'a str, err: Error) -> (err, path.to_owned())
            display("pr: Reading from input {0} gave error", path)
            cause(err)
        }

        UnknownFiletype(path: String) {
            display("pr: {0}: unknown filetype", path)
        }

        EncounteredErrors(msg: String) {
            display("pr: {0} encountered", msg)
        }

        IsDirectory(path: String) {
            display("pr: {0}: Is a directory", path)
        }

        IsSocket(path: String) {
            display("pr: cannot open {}, Operation not supported on socket", path)
        }
    }
}

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

    opts.optopt(
        STRING_HEADER_OPTION,
        "",
        "Use the string header to replace the file name \
     in the header line.",
        "STRING",
    );

    opts.optflag(
        "d",
        "",
        "Produce output that is double spaced. An extra <newline> character is output following every <newline>
           found in the input.",
    );

    opts.optflagopt(
        NUMBERING_MODE_OPTION,
        "",
        "Provide width digit line numbering.  The default for width, if not specified, is 5.  The number occupies
           the first width column positions of each text column or each line of -m output.  If char (any nondigit
           character) is given, it is appended to the line number to separate it from whatever follows.  The default
           for char is a <tab>.  Line numbers longer than width columns are truncated.",
        "[char][width]",
    );

    opts.optflag("", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

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

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

    let mut files: Vec<String> = matches.free.clone();
    if files.is_empty() {
        //For stdin
        files.push(FILE_STDIN.to_owned());
    }

    if matches.opt_present("help") {
        return print_usage(&mut opts, &matches);
    }

    for f in files {
        let header: &String = &matches.opt_str(STRING_HEADER_OPTION).unwrap_or(f.to_string());
        let options: &OutputOptions = &build_options(&matches, header, &f);
        let status: i32 = pr(&f, options);
        if status != 0 {
            return status;
        }
    }
    return 0;
}

fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
    println!("{} {} -- print files", NAME, VERSION);
    println!();
    println!("Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
        [-L locale] [-h header] [[-i] [char] [gap]]
        [-l lines] [-o offset] [[-s] [char]] [[-n] [char]
        [width]] [-w width] [-] [file ...].", NAME);
    println!();
    let usage: &str = "The pr utility is a printing and pagination filter
     for text files.  When multiple input files are spec-
     ified, each is read, formatted, and written to stan-
     dard output.  By default, the input is separated
     into 66-line pages, each with

     o   A 5-line header with the page number, date,
         time, and the pathname of the file.

     o   A 5-line trailer consisting of blank lines.

     If standard output is associated with a terminal,
     diagnostic messages are suppressed until the pr
     utility has completed processing.

     When multiple column output is specified, text col-
     umns are of equal width.  By default text columns
     are separated by at least one <blank>.  Input lines
     that do not fit into a text column are truncated.
     Lines are not truncated under single column output.";
    println!("{}", opts.usage(usage));
    if matches.free.is_empty() {
        return 1;
    }
    return 0;
}

fn build_options(matches: &Matches, header: &String, path: &String) -> OutputOptions {
    let numbering_options: Option<NumberingMode> = matches.opt_str(NUMBERING_MODE_OPTION).map(|i| {
        NumberingMode {
            width: i.parse::<usize>().unwrap_or(NumberingMode::default().width),
            separator: NumberingMode::default().separator,
        }
    }).or_else(|| {
        if matches.opt_present(NUMBERING_MODE_OPTION) {
            return Some(NumberingMode::default());
        }
        return None;
    });

    let line_separator: String = if matches.opt_present("d") {
        "\n\n".to_string()
    } else {
        "\n".to_string()
    };

    let last_modified_time = if path.eq(FILE_STDIN) {
        current_time()
    } else {
        file_last_modified_time(path)
    };

    OutputOptions {
        number: numbering_options,
        header: header.to_string(),
        double_spaced: matches.opt_present("d"),
        line_separator,
        last_modified_time,
    }
}

fn open(path: &str) -> Result<Box<Read>, PrError> {
    match get_input_type(path) {
        Some(InputType::Directory) => Err(PrError::IsDirectory(path.to_string())),
        #[cfg(unix)]
        Some(InputType::Socket) => {
            Err(PrError::IsSocket(path.to_string()))
        }
        Some(InputType::StdIn) => {
            let stdin = stdin();
            Ok(Box::new(stdin) as Box<Read>)
        }
        Some(_) => Ok(Box::new(File::open(path).context(path)?) as Box<Read>),
        None => Err(PrError::UnknownFiletype(path.to_string()))
    }
}

fn pr(path: &str, options: &OutputOptions) -> i32 {
    let mut i = 0;
    let mut page: usize = 0;
    let mut buffered_content: Vec<String> = Vec::new();
    match open(path) {
        Ok(reader) => {
            // TODO Replace the loop
            for line in BufReader::new(reader).lines() {
                if i == CONTENT_LINES_PER_PAGE {
                    page = page + 1;
                    i = 0;
                    prepare_page(&mut buffered_content, options, &page);
                }
                match line {
                    Ok(content) => buffered_content.push(content),
                    Err(error) => {
                        writeln!(&mut stderr(), "pr: Unable to read from input type {}\n{}", path, error.to_string());
                        return -1;
                    }
                }
                i = i + 1;
            }

            if i != 0 {
                page = page + 1;
                prepare_page(&mut buffered_content, options, &page);
            }
        }
        Err(error) => {
            writeln!(&mut stderr(), "{}", error);
            return -1;
        }
    }
    return 0;
}

fn print_page(header_content: &Vec<String>, lines: &Vec<String>, options: &OutputOptions, page: &usize) {
    let trailer_content: Vec<String> = trailer_content();
    assert_eq!(lines.len() <= CONTENT_LINES_PER_PAGE, true, "Only {} lines of content allowed in a pr output page", CONTENT_LINES_PER_PAGE);
    assert_eq!(header_content.len(), HEADER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr header", HEADER_LINES_PER_PAGE);
    assert_eq!(trailer_content.len(), TRAILER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr trailer", TRAILER_LINES_PER_PAGE);
    let out: &mut Stdout = &mut stdout();
    let line_separator = options.as_ref().line_separator.as_bytes();

    out.lock();
    for x in header_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }

    let width: usize = options.as_ref()
        .number.as_ref()
        .map(|i| i.width)
        .unwrap_or(0);
    let separator: String = options.as_ref()
        .number.as_ref()
        .map(|i| i.separator.to_string())
        .unwrap_or(NumberingMode::default().separator);

    let prev_lines = CONTENT_LINES_PER_PAGE * (page - 1);
    let mut i = 1;
    for x in lines {
        if options.number.is_none() {
            out.write(x.as_bytes());
        } else {
            let fmtd_line_number: String = get_fmtd_line_number(&width, prev_lines + i, &separator);
            out.write(format!("{}{}", fmtd_line_number, x).as_bytes());
        }
        out.write(line_separator);
        i = i + 1;
    }
    for x in trailer_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }
    out.flush();
}

fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
    let line_str = line_number.to_string();
    if line_str.len() >= *width {
        format!("{:>width$}{}", &line_str[line_str.len() - *width..], separator, width = width)
    } else {
        format!("{:>width$}{}", line_str, separator, width = width)
    }
}

fn prepare_page(buffered_content: &mut Vec<String>, options: &OutputOptions, page: &usize) {
    let header: Vec<String> = header_content(&options, &page);
    print_page(&header, buffered_content, &options, &page);
    buffered_content.clear();
}

fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
    let first_line: String = format!("{} {} Page {}", options.last_modified_time, options.header, page);
    vec!["".to_string(), "".to_string(), first_line, "".to_string(), "".to_string()]
}

fn file_last_modified_time(path: &str) -> String {
    let file_metadata = metadata(path);
    return file_metadata.map(|i| {
        return i.modified().map(|x| {
            let datetime: DateTime<Local> = x.into();
            datetime.format("%b %d %H:%M %Y").to_string()
        }).unwrap_or(String::new());
    }).unwrap_or(String::new());
}

fn current_time() -> String {
    let datetime: DateTime<Local> = Local::now();
    datetime.format("%b %d %H:%M %Y").to_string()
}

fn trailer_content() -> Vec<String> {
    vec!["".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string()]
}

fn get_input_type(path: &str) -> Option<InputType> {
    if path == FILE_STDIN {
        return Some(InputType::StdIn);
    }

    metadata(path).map(|i| {
        match i.file_type() {
            #[cfg(unix)]
            ft if ft.is_block_device() =>
                {
                    Some(InputType::BlockDevice)
                }
            #[cfg(unix)]
            ft if ft.is_char_device() =>
                {
                    Some(InputType::CharacterDevice)
                }
            #[cfg(unix)]
            ft if ft.is_fifo() =>
                {
                    Some(InputType::Fifo)
                }
            #[cfg(unix)]
            ft if ft.is_socket() =>
                {
                    Some(InputType::Socket)
                }
            ft if ft.is_dir() => Some(InputType::Directory),
            ft if ft.is_file() => Some(InputType::File),
            ft if ft.is_symlink() => Some(InputType::SymLink),
            _ => None
        }
    }).unwrap_or(None)
}
<|endoftext|>


<commit_before>// Module for rules parsing.

extern crate enum_set;
extern crate pcre;
extern crate xdg_basedir;

use std::collections;
use std::io;
use std::io::BufRead;
use std::path;

use super::filenames;

pub fn get_rules_directory(options: Option<&str>) -> Result<path::PathBuf, String> {
    if let Some(directory) = options {
        let dir = path::PathBuf::from(directory);
        info!("Rules directory is defined: {}.", dir.display());
        Ok(dir)
    } else {
        info!("Rules directory is not set, try to discover.");
        let mut config_home_dir = try!(
            xdg_basedir::get_config_home().map_err(|e| e.to_string())
        );
        info!("Config home directory is {}", config_home_dir.display());

        config_home_dir.push("q");
        config_home_dir.push("rules");

        info!("Calculated home directory is {}.", config_home_dir.display());

        Ok(config_home_dir)
    }
}

pub fn get_rules(rules_directory: &path::PathBuf, rules_str: &str, case_insensitive: bool) -> Result<pcre::Pcre, String> {
    let filenames = parse_rules_filenames(rules_str, rules_directory);
    parse_rules(&filenames, case_insensitive)
}

#[inline]
fn parse_rules_filenames(rules: &str, config_dir: &path::PathBuf) -> collections::HashSet<path::PathBuf> {
    rules
        .split(",")
        .map(
            |item| {
                let mut root_path = config_dir.clone();
                root_path.push(item);
                debug!("Filepath for {} is {}.", item, root_path.display());
                root_path
            }
        )
        .collect::<collections::HashSet<path::PathBuf>>()
}

fn parse_rules(filenames: &collections::HashSet<path::PathBuf>, case_insensitive: bool) -> Result<pcre::Pcre, String> {
    let mut regex_buffer: Vec<String> = Vec::with_capacity(filenames.len() * 2);

    for filename in filenames.iter() {
        let path = filename.as_path();
        if let Some(file) = filenames::open_if_file(path) {
            let reader = io::BufReader::new(file);

            for line in reader.lines() {
                match line {
                    Ok(content) => {
                        let trimmed_content = content.trim();

                        if trimmed_content == "" {
                            continue
                        }

                        debug!("Add {} to regexp", &trimmed_content);
                        regex_buffer.push(trimmed_content.to_string());
                    },
                    Err(error) => return Err(
                        format!(
                            "Cannot fetch a line from file {}: {}!",
                            path.display(),
                            error.to_string()
                        )
                    )
                }
            }
        } else {
            return Err(
                format!(
                    "Cannot process file {} with fules", path.display()
                )
            )
        }
    }

    let concatenated_buffer = &regex_buffer.connect("|");
    info!("Regexp to compile: {}", concatenated_buffer);

    let mut regex_options: enum_set::EnumSet<pcre::CompileOption> = enum_set::EnumSet::new();
    if case_insensitive {
        regex_options.insert(pcre::CompileOption::Caseless);
    }

    Ok(
        try!(
            pcre::Pcre::compile_with_options(concatenated_buffer, &regex_options).map_err(
                |e| format!("Cannot compile regexps: {}.", e.to_string())
            )
        )
    )
}
<commit_msg>Put regexps into proper groups<commit_after>// Module for rules parsing.

extern crate enum_set;
extern crate pcre;
extern crate xdg_basedir;

use std::collections;
use std::io;
use std::io::BufRead;
use std::path;

use super::filenames;

pub fn get_rules_directory(options: Option<&str>) -> Result<path::PathBuf, String> {
    if let Some(directory) = options {
        let dir = path::PathBuf::from(directory);
        info!("Rules directory is defined: {}.", dir.display());
        Ok(dir)
    } else {
        info!("Rules directory is not set, try to discover.");
        let mut config_home_dir = try!(
            xdg_basedir::get_config_home().map_err(|e| e.to_string())
        );
        info!("Config home directory is {}", config_home_dir.display());

        config_home_dir.push("q");
        config_home_dir.push("rules");

        info!("Calculated home directory is {}.", config_home_dir.display());

        Ok(config_home_dir)
    }
}

pub fn get_rules(rules_directory: &path::PathBuf, rules_str: &str, case_insensitive: bool) -> Result<pcre::Pcre, String> {
    let filenames = parse_rules_filenames(rules_str, rules_directory);
    parse_rules(&filenames, case_insensitive)
}

#[inline]
fn parse_rules_filenames(rules: &str, config_dir: &path::PathBuf) -> collections::HashSet<path::PathBuf> {
    rules
        .split(",")
        .map(
            |item| {
                let mut root_path = config_dir.clone();
                root_path.push(item);
                debug!("Filepath for {} is {}.", item, root_path.display());
                root_path
            }
        )
        .collect::<collections::HashSet<path::PathBuf>>()
}

fn parse_rules(filenames: &collections::HashSet<path::PathBuf>, case_insensitive: bool) -> Result<pcre::Pcre, String> {
    let mut regex_buffer: Vec<String> = Vec::with_capacity(filenames.len() * 2);

    for filename in filenames.iter() {
        let path = filename.as_path();
        if let Some(file) = filenames::open_if_file(path) {
            let reader = io::BufReader::new(file);

            for line in reader.lines() {
                match line {
                    Ok(content) => {
                        let trimmed_content = content.trim();

                        if trimmed_content == "" {
                            continue
                        }

                        debug!("Add {} to regexp", &trimmed_content);
                        regex_buffer.push(format!("(?:{})", &trimmed_content));
                    },
                    Err(error) => return Err(
                        format!(
                            "Cannot fetch a line from file {}: {}!",
                            path.display(),
                            error.to_string()
                        )
                    )
                }
            }
        } else {
            return Err(
                format!(
                    "Cannot process file {} with fules", path.display()
                )
            )
        }
    }

    let concatenated_buffer = &regex_buffer.connect("|");
    info!("Regexp to compile: {}", concatenated_buffer);

    let mut regex_options: enum_set::EnumSet<pcre::CompileOption> = enum_set::EnumSet::new();
    if case_insensitive {
        regex_options.insert(pcre::CompileOption::Caseless);
    }

    Ok(
        try!(
            pcre::Pcre::compile_with_options(concatenated_buffer, &regex_options).map_err(
                |e| format!("Cannot compile regexps: {}.", e.to_string())
            )
        )
    )
}
<|endoftext|>


<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use json;
use std::{mem, slice};

use {Camera, Gltf, Mesh, Skin};

/// 4x4 identity matrix.
const IDENTITY: [f32; 16] = {
    [1.0, 0.0, 0.0, 0.0,
     0.0, 1.0, 0.0, 0.0,
     0.0, 0.0, 1.0, 0.0,
     0.0, 0.0, 0.0, 1.0]
};

/// The transform for a `Node`.
#[derive(Clone, Debug)]
pub enum Transform {
    /// 4x4 transformation matrix in column-major order.
    Matrix([[f32; 4]; 4]),

    /// Decomposed TRS properties.
    Decomposed {
        /// `[x, y, z]` vector.
        translation: [f32; 3],

        /// `[x, y, z, w]` quaternion, where `w` is the scalar.
        rotation: [f32; 4],

        /// `[x, y, z]` vector.
        scale: [f32; 3],
    },
}

/// A node in the node hierarchy. When the node contains `skin`, all
/// `mesh.primitives` must contain `JOINTS_0` and `WEIGHTS_0` attributes. A node can
/// have either a `matrix` or any combination of `translation`/`rotation`/`scale`
/// (TRS) properties. TRS properties are converted to matrices and postmultiplied in
/// the `T * R * S` order to compose the transformation matrix; first the scale is
/// applied to the vertices, then the rotation, and then the translation. If none are
/// provided, the transform is the identity. When a node is targeted for animation
/// (referenced by an animation.channel.target), only TRS properties may be present;
/// `matrix` will not be present.
#[derive(Clone, Debug)]
pub struct Node<'a> {
    /// The parent `Gltf` struct.
    gltf: &'a Gltf,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::scene::Node,
}

/// The root `Node`s of a scene.
#[derive(Clone, Debug)]
pub struct Scene<'a> {
    /// The parent `Gltf` struct.
    #[allow(dead_code)]
    gltf: &'a Gltf,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::scene::Scene,
}

/// An `Iterator` that visits the nodes in a scene.
#[derive(Clone, Debug)]
pub struct Nodes<'a> {
    /// The parent `Gltf` struct.
    gltf: &'a Gltf,

    /// The internal node index iterator.
    iter: slice::Iter<'a, json::Index<json::scene::Node>>,
}

/// An `Iterator` that visits the children of a node.
#[derive(Clone, Debug)]
pub struct Children<'a> {
    /// The parent `Node` struct.
    parent: &'a Node<'a>,

    /// The internal node index iterator.
    iter: slice::Iter<'a, json::Index<json::scene::Node>>,
}

impl<'a> Node<'a> {
    /// Constructs a `Node`.
    pub(crate) fn new(gltf: &'a Gltf, index: usize, json: &'a json::scene::Node) -> Self {
        Self {
            gltf: gltf,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the internal JSON item.
    pub fn as_json(&self) ->  &json::scene::Node {
        self.json
    }

    /// Returns the camera referenced by this node.
    pub fn camera(&self) -> Option<Camera<'a>> {
        self.json.camera.as_ref().map(|index| {
            self.gltf.cameras().nth(index.value()).unwrap()
        })
    }

    /// Returns an `Iterator` that visits the node's children.
    pub fn children(&'a self) -> Children<'a> {
        Children {
            parent: self,
            iter: self.json.children.as_ref().map_or([].iter(), |x| x.iter()),
        }
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }

    /// Returns the 4x4 column-major transformation matrix.
    #[deprecated(since = "0.9.1", note = "use Node::transform instead")]
    pub fn matrix(&self) -> [f32; 16] {
        self.json.matrix.unwrap_or(IDENTITY)
    }

    /// Returns the mesh referenced by this node.
    pub fn mesh(&self) -> Option<Mesh<'a>> {
        self.json.mesh.as_ref().map(|index| {
            self.gltf.meshes().nth(index.value()).unwrap()
        })
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Returns the node's unit quaternion rotation in the order `[x, y, z, w]`,
    /// where `w` is the scalar.
    #[deprecated(since = "0.9.1", note = "use Node::transform instead")]
    pub fn rotation(&self) -> [f32; 4] {
        self.json.rotation.0
    }

    /// Returns the node's non-uniform scale.
    #[deprecated(since = "0.9.1", note = "use Node::transform instead")]
    pub fn scale(&self) -> [f32; 3] {
        self.json.scale
    }

    /// Returns the node's translation.
    #[deprecated(since = "0.9.1", note = "use Node::transform instead")]
    pub fn translation(&self) -> [f32; 3] {
        self.json.translation
    }

    /// Returns the node's transform.
    pub fn transform(&self) -> Transform {
        if let Some(matrix) = self.json.matrix.clone() {
            unsafe {
                Transform::Matrix(mem::transmute(matrix))
            }
        } else {
            Transform::Decomposed {
                translation: self.json.translation,
                rotation: self.json.rotation.0,
                scale: self.json.scale,
            }
        }
    }

    /// Returns the skin referenced by this node.
    pub fn skin(&self) -> Option<Skin<'a>> {
        self.json.skin.as_ref().map(|index| {
            self.gltf.skins().nth(index.value()).unwrap()
        })
    }

    /// Returns the weights of the instantiated morph target.
    pub fn weights(&self) -> Option<&[f32]> {
        self.json.weights.as_ref().map(Vec::as_slice)
    }
}

impl<'a> Scene<'a> {
    /// Constructs a `Scene`.
    pub(crate) fn new(gltf: &'a Gltf, index: usize, json: &'a json::scene::Scene) -> Self {
        Self {
            gltf: gltf,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the internal JSON item.
    pub fn as_json(&self) ->  &json::scene::Scene {
        self.json
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras{
        &self.json.extras
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Returns an `Iterator` that visits each root node of the scene.
    pub fn nodes(&self) -> Nodes<'a> {
        Nodes {
            gltf: self.gltf,
            iter: self.json.nodes.iter(),
        }
    }
}

impl<'a> ExactSizeIterator for Nodes<'a> {}
impl<'a> Iterator for Nodes<'a> {
    type Item = Node<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next()
            .map(|index| self.gltf.nodes().nth(index.value()).unwrap())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<'a> ExactSizeIterator for Children<'a> {}
impl<'a> Iterator for Children<'a> {
    type Item = Node<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next()
            .map(|index| self.parent.gltf.nodes().nth(index.value()).unwrap())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

<commit_msg>Improve documentation<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use json;
use std::{mem, slice};

use {Camera, Gltf, Mesh, Skin};

/// 4x4 identity matrix.
const IDENTITY: [f32; 16] = {
    [1.0, 0.0, 0.0, 0.0,
     0.0, 1.0, 0.0, 0.0,
     0.0, 0.0, 1.0, 0.0,
     0.0, 0.0, 0.0, 1.0]
};

/// The transform for a `Node`.
#[derive(Clone, Debug)]
pub enum Transform {
    /// 4x4 transformation matrix in column-major order.
    Matrix([[f32; 4]; 4]),

    /// Decomposed TRS properties.
    Decomposed {
        /// `[x, y, z]` vector.
        translation: [f32; 3],

        /// `[x, y, z, w]` quaternion, where `w` is the scalar.
        rotation: [f32; 4],

        /// `[x, y, z]` vector.
        scale: [f32; 3],
    },
}

/// A node in the node hierarchy. When the node contains `skin`, all
/// `mesh.primitives` must contain `JOINTS_0` and `WEIGHTS_0` attributes. A node can
/// have either a `matrix` or any combination of `translation`/`rotation`/`scale`
/// (TRS) properties. TRS properties are converted to matrices and postmultiplied in
/// the `T * R * S` order to compose the transformation matrix; first the scale is
/// applied to the vertices, then the rotation, and then the translation. If none are
/// provided, the transform is the identity. When a node is targeted for animation
/// (referenced by an animation.channel.target), only TRS properties may be present;
/// `matrix` will not be present.
#[derive(Clone, Debug)]
pub struct Node<'a> {
    /// The parent `Gltf` struct.
    gltf: &'a Gltf,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::scene::Node,
}

/// The root `Node`s of a scene.
#[derive(Clone, Debug)]
pub struct Scene<'a> {
    /// The parent `Gltf` struct.
    #[allow(dead_code)]
    gltf: &'a Gltf,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::scene::Scene,
}

/// An `Iterator` that visits the nodes in a scene.
#[derive(Clone, Debug)]
pub struct Nodes<'a> {
    /// The parent `Gltf` struct.
    gltf: &'a Gltf,

    /// The internal node index iterator.
    iter: slice::Iter<'a, json::Index<json::scene::Node>>,
}

/// An `Iterator` that visits the children of a node.
#[derive(Clone, Debug)]
pub struct Children<'a> {
    /// The parent `Node` struct.
    parent: &'a Node<'a>,

    /// The internal node index iterator.
    iter: slice::Iter<'a, json::Index<json::scene::Node>>,
}

impl<'a> Node<'a> {
    /// Constructs a `Node`.
    pub(crate) fn new(gltf: &'a Gltf, index: usize, json: &'a json::scene::Node) -> Self {
        Self {
            gltf: gltf,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the internal JSON item.
    pub fn as_json(&self) ->  &json::scene::Node {
        self.json
    }

    /// Returns the camera referenced by this node.
    pub fn camera(&self) -> Option<Camera<'a>> {
        self.json.camera.as_ref().map(|index| {
            self.gltf.cameras().nth(index.value()).unwrap()
        })
    }

    /// Returns an `Iterator` that visits the node's children.
    pub fn children(&'a self) -> Children<'a> {
        Children {
            parent: self,
            iter: self.json.children.as_ref().map_or([].iter(), |x| x.iter()),
        }
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }

    /// Returns the 4x4 column-major transformation matrix.
    #[deprecated(since = "0.9.1", note = "use Node::transform instead")]
    pub fn matrix(&self) -> [f32; 16] {
        self.json.matrix.unwrap_or(IDENTITY)
    }

    /// Returns the mesh referenced by this node.
    pub fn mesh(&self) -> Option<Mesh<'a>> {
        self.json.mesh.as_ref().map(|index| {
            self.gltf.meshes().nth(index.value()).unwrap()
        })
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Returns the node's unit quaternion rotation in the order `[x, y, z, w]`,
    /// where `w` is the scalar.
    #[deprecated(since = "0.9.1", note = "Use Node::transform instead.")]
    pub fn rotation(&self) -> [f32; 4] {
        self.json.rotation.0
    }

    /// Returns the node's non-uniform scale.
    #[deprecated(since = "0.9.1", note = "Use Node::transform instead.")]
    pub fn scale(&self) -> [f32; 3] {
        self.json.scale
    }

    /// Returns the node's translation.
    #[deprecated(since = "0.9.1", note = "Use Node::transform instead.")]
    pub fn translation(&self) -> [f32; 3] {
        self.json.translation
    }

    /// Returns the node's transform.
    pub fn transform(&self) -> Transform {
        if let Some(matrix) = self.json.matrix.clone() {
            unsafe {
                Transform::Matrix(mem::transmute(matrix))
            }
        } else {
            Transform::Decomposed {
                translation: self.json.translation,
                rotation: self.json.rotation.0,
                scale: self.json.scale,
            }
        }
    }

    /// Returns the skin referenced by this node.
    pub fn skin(&self) -> Option<Skin<'a>> {
        self.json.skin.as_ref().map(|index| {
            self.gltf.skins().nth(index.value()).unwrap()
        })
    }

    /// Returns the weights of the instantiated morph target.
    pub fn weights(&self) -> Option<&[f32]> {
        self.json.weights.as_ref().map(Vec::as_slice)
    }
}

impl<'a> Scene<'a> {
    /// Constructs a `Scene`.
    pub(crate) fn new(gltf: &'a Gltf, index: usize, json: &'a json::scene::Scene) -> Self {
        Self {
            gltf: gltf,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the internal JSON item.
    pub fn as_json(&self) ->  &json::scene::Scene {
        self.json
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras{
        &self.json.extras
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Returns an `Iterator` that visits each root node of the scene.
    pub fn nodes(&self) -> Nodes<'a> {
        Nodes {
            gltf: self.gltf,
            iter: self.json.nodes.iter(),
        }
    }
}

impl<'a> ExactSizeIterator for Nodes<'a> {}
impl<'a> Iterator for Nodes<'a> {
    type Item = Node<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next()
            .map(|index| self.gltf.nodes().nth(index.value()).unwrap())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<'a> ExactSizeIterator for Children<'a> {}
impl<'a> Iterator for Children<'a> {
    type Item = Node<'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next()
            .map(|index| self.parent.gltf.nodes().nth(index.value()).unwrap())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

<|endoftext|>


<commit_before>//! Defines the scene struct which contains the various objects defining the scene.
//! This includes the geometry, instances of the geometry, the camera and so on.

use std::sync::Arc;
use std::path::Path;

use linalg::{Transform, Point, Vector, Ray};
use film::{Camera, Colorf};
use geometry::{Sphere, Plane, Instance, BoundableGeom, Intersection, Mesh};
use material::{Matte, SpecularMetal, Glass, Material, Merl};
use integrator::{self, Integrator};
use light::{self, Light};
use geometry::BVH;

/// The scene containing the objects and camera configuration we'd like to render,
/// shared immutably among the ray tracing threads
pub struct Scene {
    pub camera: Camera,
    pub bvh: BVH<Instance>,
    pub integrator: Arc<Box<Integrator + Send + Sync>>,
    /// TODO: Only one light for now
    pub light: Arc<Box<Light + Send + Sync>>,
}

impl Scene {
    /// Create our (currently) hard-coded scene, passing in the render target
    /// dimensions so we can set the projection matrix for the camera
    pub fn new(w: usize, h: usize) -> Scene {
        let sphere = Arc::new(Box::new(Sphere::new(1.0)) as Box<BoundableGeom + Send + Sync>);
        let plane = Arc::new(Box::new(Plane) as Box<BoundableGeom + Send + Sync>);
        let mut models = Mesh::load_obj(Path::new("./rust-logo.obj"));
        let mesh = Arc::new(Box::new(models.remove("rust_logo").unwrap()) as Box<BoundableGeom + Send + Sync>);

        let white_wall = Arc::new(Box::new(Matte::new(&Colorf::new(1.0, 1.0, 1.0), 1.0)) as Box<Material + Send + Sync>);
        let red_wall = Arc::new(Box::new(Matte::new(&Colorf::new(1.0, 0.2, 0.2), 1.0)) as Box<Material + Send + Sync>);
        let blue_wall = Arc::new(Box::new(Matte::new(&Colorf::new(0.2, 0.2, 1.0), 1.0)) as Box<Material + Send + Sync>);
        let merl_mat = Arc::new(Box::new(Merl::load_file(Path::new("two-layer-silver.binary"))) as Box<Material + Send + Sync>);

        let instances = vec![
            Instance::new(mesh.clone(), merl_mat.clone(), Transform::translate(&Vector::new(0.0, 5.0, 12.0))
                          * Transform::rotate_z(180.0) * Transform::rotate_y(90.0)
                          * Transform::scale(&Vector::broadcast(12.0)), "model"),
            // The back wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 20.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_x(90.0), "back_wall"),
            // The left wall
            Instance::new(plane.clone(), red_wall.clone(), Transform::translate(&Vector::new(-15.0, 0.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_y(90.0), "left_wall"),
            // The right wall
            Instance::new(plane.clone(), blue_wall.clone(), Transform::translate(&Vector::new(15.0, 0.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_y(-90.0), "right_wall"),
            // The top wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 0.0, 24.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_x(180.0), "top_wall"),
            // The bottom wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 0.0, 0.0))
                          * Transform::scale(&Vector::broadcast(32.0)), "bottom_wall"),
            /*
            // The reflective sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(SpecularMetal::new(&Colorf::new(0.155, 0.116, 0.138), &Colorf::new(4.828, 3.122, 2.146)))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(-6.0, 8.0, 5.0))
                    * Transform::scale(&Vector::broadcast(5.0)), "metal_sphere"),
            // The glass sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(Glass::new(&Colorf::broadcast(1.0), &Colorf::broadcast(1.0), 1.52))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(6.0, -2.0, 5.0))
                     * Transform::scale(&Vector::broadcast(5.0)), "glass_sphere")
            */
        ];
        let light_color = Colorf::broadcast(200.0) * Colorf::new(0.780131, 0.780409, 0.775833);
        Scene {
            camera: Camera::new(Transform::look_at(&Point::new(0.0, -60.0, 12.0),
                &Point::new(0.0, 0.0, 12.0), &Vector::new(0.0, 0.0, 1.0)), 30.0, (w, h)),
            bvh: BVH::new(4, instances),
            integrator: Arc::new(Box::new(integrator::Path::new(4, 8)) as Box<Integrator + Send + Sync>),
            light: Arc::new(Box::new(light::Point::new(&Point::new(0.0, -5.0, 14.0), &light_color))
                            as Box<Light + Send + Sync>),
        }
    }
    /// Test the ray for intersections against the objects in the scene.
    /// Returns Some(Intersection) if an intersection was found and None if not.
    pub fn intersect(&self, ray: &mut Ray) -> Option<Intersection> {
        self.bvh.intersect(ray, |r, i| i.intersect(r))
    }
}

<commit_msg>Comment out the rust logo, Stanford Buddha, Dragon scene<commit_after>//! Defines the scene struct which contains the various objects defining the scene.
//! This includes the geometry, instances of the geometry, the camera and so on.

use std::sync::Arc;
use std::path::Path;

use linalg::{Transform, Point, Vector, Ray};
use film::{Camera, Colorf};
use geometry::{Sphere, Plane, Instance, BoundableGeom, Intersection, Mesh};
use material::{Matte, SpecularMetal, Glass, Material, Merl};
use integrator::{self, Integrator};
use light::{self, Light};
use geometry::BVH;

/// The scene containing the objects and camera configuration we'd like to render,
/// shared immutably among the ray tracing threads
pub struct Scene {
    pub camera: Camera,
    pub bvh: BVH<Instance>,
    pub integrator: Arc<Box<Integrator + Send + Sync>>,
    /// TODO: Only one light for now
    pub light: Arc<Box<Light + Send + Sync>>,
}

impl Scene {
    /// Create our (currently) hard-coded scene, passing in the render target
    /// dimensions so we can set the projection matrix for the camera
    pub fn new(w: usize, h: usize) -> Scene {
        let sphere = Arc::new(Box::new(Sphere::new(1.0)) as Box<BoundableGeom + Send + Sync>);
        let plane = Arc::new(Box::new(Plane) as Box<BoundableGeom + Send + Sync>);

        let white_wall = Arc::new(Box::new(Matte::new(&Colorf::new(0.75, 0.75, 0.75), 1.0))
                                  as Box<Material + Send + Sync>);
        let red_wall = Arc::new(Box::new(Matte::new(&Colorf::new(0.75, 0.25, 0.25), 1.0))
                                as Box<Material + Send + Sync>);
        let blue_wall = Arc::new(Box::new(Matte::new(&Colorf::new(0.25, 0.25, 0.75), 1.0))
                                 as Box<Material + Send + Sync>);

        /*
        let mut models = Mesh::load_obj(Path::new("./rust-logo.obj"));
        let rust_logo = Arc::new(Box::new(models.remove("rust_logo").unwrap()) as Box<BoundableGeom + Send + Sync>);

        let mut models = Mesh::load_obj(Path::new("./buddha.obj"));
        let buddha = Arc::new(Box::new(models.remove("buddha").unwrap()) as Box<BoundableGeom + Send + Sync>);

        let mut models = Mesh::load_obj(Path::new("./dragon.obj"));
        let dragon = Arc::new(Box::new(models.remove("dragon").unwrap()) as Box<BoundableGeom + Send + Sync>);

        let oxidized_steel = Arc::new(Box::new(Merl::load_file(Path::new("black-oxidized-steel.binary")))
                                      as Box<Material + Send + Sync>);
        let gold_paint = Arc::new(Box::new(Merl::load_file(Path::new("gold-metallic-paint.binary")))
                                  as Box<Material + Send + Sync>);
        let blue_acrylic = Arc::new(Box::new(Merl::load_file(Path::new("blue-acrylic.binary")))
                                    as Box<Material + Send + Sync>);
        */

        let instances = vec![
            /*
            Instance::new(rust_logo, oxidized_steel, Transform::translate(&Vector::new(0.0, 6.0, 12.0))
                          * Transform::rotate_z(180.0) * Transform::rotate_y(90.0)
                          * Transform::scale(&Vector::broadcast(12.0)), "rust_logo"),
            Instance::new(buddha, gold_paint, Transform::translate(&Vector::new(-9.0, 0.5, 7.55))
                          * Transform::rotate_z(-140.0) * Transform::rotate_x(90.0)
                          * Transform::scale(&Vector::broadcast(17.0)), "buddha"),
            Instance::new(dragon, blue_acrylic, Transform::translate(&Vector::new(8.25, 1.0, 3.7))
                          * Transform::rotate_z(120.0) * Transform::rotate_x(90.0)
                          * Transform::scale(&Vector::broadcast(13.0)), "dragon"),
            */
            // The back wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 20.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_x(90.0), "back_wall"),
            // The left wall
            Instance::new(plane.clone(), red_wall.clone(), Transform::translate(&Vector::new(-15.0, 0.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_y(90.0), "left_wall"),
            // The right wall
            Instance::new(plane.clone(), blue_wall.clone(), Transform::translate(&Vector::new(15.0, 0.0, 12.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_y(-90.0), "right_wall"),
            // The top wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 0.0, 24.0))
                          * Transform::scale(&Vector::broadcast(32.0)) * Transform::rotate_x(180.0), "top_wall"),
            // The bottom wall
            Instance::new(plane.clone(), white_wall.clone(), Transform::translate(&Vector::new(0.0, 0.0, 0.0))
                          * Transform::scale(&Vector::broadcast(32.0)), "bottom_wall"),
            // The reflective sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(SpecularMetal::new(&Colorf::new(0.155, 0.116, 0.138), &Colorf::new(4.828, 3.122, 2.146)))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(-6.0, 8.0, 5.0))
                    * Transform::scale(&Vector::broadcast(5.0)), "metal_sphere"),
            // The glass sphere
            Instance::new(sphere.clone(),
            Arc::new(Box::new(Glass::new(&Colorf::broadcast(1.0), &Colorf::broadcast(1.0), 1.52))
                     as Box<Material + Send + Sync>), Transform::translate(&Vector::new(6.0, -2.0, 5.0))
                     * Transform::scale(&Vector::broadcast(5.0)), "glass_sphere")
        ];
        let light_color = Colorf::broadcast(200.0) * Colorf::new(0.780131, 0.780409, 0.775833);
        Scene {
            camera: Camera::new(Transform::look_at(&Point::new(0.0, -60.0, 12.0),
                &Point::new(0.0, 0.0, 12.0), &Vector::new(0.0, 0.0, 1.0)), 30.0, (w, h)),
            bvh: BVH::new(4, instances),
            integrator: Arc::new(Box::new(integrator::Path::new(4, 8)) as Box<Integrator + Send + Sync>),
            light: Arc::new(Box::new(light::Point::new(&Point::new(0.0, 0.0, 22.0), &light_color))
                            as Box<Light + Send + Sync>),
        }
    }
    /// Test the ray for intersections against the objects in the scene.
    /// Returns Some(Intersection) if an intersection was found and None if not.
    pub fn intersect(&self, ray: &mut Ray) -> Option<Intersection> {
        self.bvh.intersect(ray, |r, i| i.intersect(r))
    }
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::rc::Rc;
use data::{Value, List, Scope, BuiltinFn, RuntimeError};
use builtins;

#[derive(Debug)]
pub struct GlobalScope
{
    dict: HashMap<String, Value>,
}

impl GlobalScope
{
    pub fn new() -> GlobalScope
    {
        GlobalScope{ dict: HashMap::new() }
    }

    pub fn set_number(&mut self, key: &str, val: f64)
    {
        self.set(key, Value::Number(val))
    }

    pub fn set_string(&mut self, key: &str, val: &str)
    {
        self.set(key, Value::String(Rc::new(val.to_string())))
    }

    pub fn set_builtin<F>(&mut self, key: &'static str, do_eval: bool, val: F)
        where F: Fn(&List, &mut Scope) -> Result<Value, RuntimeError> + 'static
    {
        self.set(key, Value::Builtin(BuiltinFn::new(key, do_eval, val)))
    }

    pub fn load_stdlib(&mut self)
    {
        self.set("nil", Value::Nil);
        builtins::load(self);
    }
}

impl Scope for GlobalScope
{
    fn get(&self, key: &str) -> Option<Value>
    {
        self.dict.get(key).map(|v| v.clone())
    }

    fn set(&mut self, key: &str, val: Value)
    {
        self.dict.insert(key.to_string(), val);
    }
}
<commit_msg>it should be more correct this way<commit_after>use std::collections::HashMap;
use std::rc::Rc;
use data::{Value, List, Scope, BuiltinFn, RuntimeError};
use builtins;

#[derive(Debug)]
pub struct GlobalScope
{
    dict: HashMap<String, Value>,
}

impl GlobalScope
{
    pub fn new() -> GlobalScope
    {
        GlobalScope{ dict: HashMap::new() }
    }

    pub fn set_number(&mut self, key: &str, val: f64)
    {
        self.set(key, Value::Number(val))
    }

    pub fn set_string(&mut self, key: &str, val: String)
    {
        self.set(key, Value::String(Rc::new(val)))
    }

    pub fn set_builtin<F>(&mut self, key: &'static str, do_eval: bool, val: F)
        where F: Fn(&List, &mut Scope) -> Result<Value, RuntimeError> + 'static
    {
        self.set(key, Value::Builtin(BuiltinFn::new(key, do_eval, val)))
    }

    pub fn load_stdlib(&mut self)
    {
        self.set("nil", Value::Nil);
        builtins::load(self);
    }
}

impl Scope for GlobalScope
{
    fn get(&self, key: &str) -> Option<Value>
    {
        self.dict.get(key).map(|v| v.clone())
    }

    fn set(&mut self, key: &str, val: Value)
    {
        self.dict.insert(key.to_string(), val);
    }
}
<|endoftext|>


<commit_before>/// score is responsible for calculating the scores of the similarity between
/// the query and the choice.
///
/// It is modeled after https://github.com/felipesere/icepick.git
use std::cmp::max;

// return (start, matched_len)
pub fn compute_match_length(choice: &str, query: &str) -> Option<(usize, usize)> {
    if query.len() <= 0 {
        return Some((0, 0));
    }

    let impossible_match = choice.len() + 1;
    let mut matched_start = impossible_match;
    let mut matched_end = impossible_match;

    let mut choice_chars = choice.chars().enumerate().peekable();
    let mut query_chars = query.chars().enumerate().peekable();

    loop {
        if query_chars.peek() == None {
            return Some((matched_start, matched_end - matched_start+1));
        }

        if choice_chars.peek() == None {
            return None;
        }

        let &(idx_choice, c) = choice_chars.peek().unwrap();
        let &(_, q) = query_chars.peek().unwrap();

        if c == q {
            if matched_start == impossible_match { matched_start = idx_choice; }
            let _ = query_chars.next();
        }

        matched_end = idx_choice;
        let _ = choice_chars.next();
    }
}

// The fuzzy search algorith is inspired by https://github.com/forrestthewoods/lib_fts
//
// # Why is new Fuzzy Match algorithm needed?
// 1. fzf use the query to compose regex for non-greedy match. When use for file/path match, it did
//    not work out for the best(e.g. Camel case characters should weight more)
// 2. fzf will only show the `range` be matched, while I think it is not enought sometimes. with
//    this fuzzy_match algorithm we are able to get all matched indics.
//
// # The complexity is O(n^2)
// An intuitive way of fuzzy match is to find all occurences of pattern characters in the target
// string. Then find out all the choice composites and evaluate the score for each. which will take
// O(n!) for each input string. Then with dynamic programing, we can reduce the time to O(n^3) with
// addition O(m*n) space;
//
// I think that O(n^3) may not be acceptable in the use case of fzf-rs. so I made some assumptions
// and reduce the complexity to O(n^2). Note that lib_fts' complexity is O(n).
//
// # The algorithm
// The scoring core is the same to lib_fts. The difference part is that I search more composites
// than lib_fts for a better result.
//
// \  A   B   a   b   C   c
//  \------------------------
// a| 20   x  -6
// b|  x  20+5 x .Y.
// c|
//
// For each matched key(b for example), we need to find all the matched A before and find the best
// matched A that will get us the max score for current b. For example, the one marked as '.Y.' in
// the previous example will search the row before(i.e. 'a') and calculate the max score for '.Y.'.
//
// This process will take O(n^3), because each row will cause O(n^2);
//
// So as shown below:
//       1         2         3        4
//    ...a.........a.........a....b...a....b....
// a
// b                              b1       b2
//
// if we already know that b1 gets max score when we choose a2, we assume that b2 will get max
// score only if 'a' is chosen within [a2..a4]. That means a1 is not searched any more.
//
// The worst case of course is also O(n^3) but normally that won't happan. Of course I wonder
// whether O(n^3) algorithm is acceptable or not.

const BONUS_ADJACENCY: i32 = 5;
const BONUS_SEPARATOR: i32 = 10;
const BONUS_CAMEL: i32 = 10;
const PENALTY_LEADING: i32 = -3; // penalty applied for every letter before the first match
const PENALTY_MAX_LEADING: i32 = -9; // maxing penalty for leading letters
const PENALTY_UNMATCHED: i32 = -1;

// judge how many scores the current index should get
fn fuzzy_score(string: &Vec<char>, index: usize) -> i32 {
    let mut score = 0;
    if index == 0 {
        return BONUS_SEPARATOR + BONUS_CAMEL;
    }

    let prev = string[index-1];
    let cur = string[index];

    // apply bonus for matches after a separator
    if prev == ' ' || prev == '_' || prev == '-' || prev == '/' || prev == '\\' {
        score += BONUS_SEPARATOR;
    }

    // apply bonus for camelCases
    if prev.is_lowercase() && cur.is_uppercase() {
        score += BONUS_CAMEL;
    }

    score
}

pub fn fuzzy_match(choice: &str, pattern: &str) -> Option<(i32, Vec<usize>)>{
    if pattern.len() == 0 {
        return Some((0, Vec::new()));
    }

    let choice_chars: Vec<char> = choice.chars().collect();
    let pattern_chars: Vec<char> = pattern.to_lowercase().chars().collect();

    let mut scores = vec![vec![]];
    let mut picked = vec![];

    // initialize the first row of scores
    for choice_idx in 0..choice.len() {
        if pattern_chars[0] == choice_chars[choice_idx].to_lowercase().next().unwrap() {
            let score = fuzzy_score(&choice_chars, choice_idx) + max(choice_idx as i32 * PENALTY_LEADING, PENALTY_MAX_LEADING);
            scores[0].push((choice_idx, score, 0)); //(char_idx, score, vec_idx back_ref)
        }
    }

    for pattern_idx in 1..pattern.len() {
        scores.push(vec![]);

        if scores[pattern_idx-1].len() <= 0 {
            return None;
        }

        let mut best_idx = 0; // points to previous line
        for choice_idx in 1..choice.len() {
            let mut best_score = -10000;

            if pattern_chars[pattern_idx] != choice_chars[choice_idx].to_lowercase().next().unwrap() {
                continue;
            }

            for (vec_idx, &(idx, prev_score, _)) in scores[pattern_idx-1][best_idx..].iter().enumerate() {
                if idx >= choice_idx {
                    break;
                }

                let mut char_score = fuzzy_score(&choice_chars, choice_idx);
                let adjacent_num = choice_idx - idx - 1;
                char_score += if adjacent_num == 0 {BONUS_ADJACENCY} else {PENALTY_UNMATCHED * adjacent_num as i32};
                char_score += prev_score;

                if char_score > best_score {
                    best_score = char_score;
                    best_idx = vec_idx;
                }
            }

            scores[pattern_idx].push((choice_idx, best_score, best_idx));
        }
    }

    let (mut next_col, &(_, score, _)) = scores[pattern.len()-1].iter().enumerate().max_by_key(|&(_, &x)| x.1).unwrap();
    let mut pattern_idx = pattern.len() as i32 - 1;
    while pattern_idx >= 0 {
        let (idx, _, next) = scores[pattern_idx as usize][next_col];
        next_col = next;
        picked.push(idx);
        pattern_idx -= 1;
    }
    picked.reverse();
    Some((score, picked))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_compute_match_length() {
        let choice_1 = "I am a 中国人.";
        let query_1 = "a人";
        assert_eq!(super::compute_match_length(&choice_1, &query_1), Some((2, 8)));

        let choice_2 = "Choice did not matter";
        let query_2 = "";
        assert_eq!(super::compute_match_length(&choice_2, &query_2), Some((0, 0)));

        let choice_3 = "abcdefg";
        let query_3 = "hi";
        assert_eq!(super::compute_match_length(&choice_3, &query_3), None);

        let choice_4 = "Partial match did not count";
        let query_4 = "PP";
        assert_eq!(compute_match_length(&choice_4, &query_4), None);
    }

    #[test]
    fn teset_fuzzy_match() {
        let choice_4 = "AaBbcc";
        let query_4 = "abc";
        assert_eq!(fuzzy_match(&choice_4, &query_4), Some((28, vec![0,2,4])));
    }
}
<commit_msg>[score] refine the code for better understanding<commit_after>/// score is responsible for calculating the scores of the similarity between
/// the query and the choice.
///
/// It is modeled after https://github.com/felipesere/icepick.git

use std::cmp::max;
use std::cell::RefCell;
const BONUS_ADJACENCY: i32 = 5;
const BONUS_SEPARATOR: i32 = 10;
const BONUS_CAMEL: i32 = 10;
const PENALTY_LEADING: i32 = -3; // penalty applied for every letter before the first match
const PENALTY_MAX_LEADING: i32 = -9; // maxing penalty for leading letters
const PENALTY_UNMATCHED: i32 = -1;

// judge how many scores the current index should get
fn fuzzy_score(string: &Vec<char>, index: usize, is_first: bool) -> i32 {
    let mut score = 0;
    if index == 0 {
        return BONUS_SEPARATOR;
    }

    let prev = string[index-1];
    let cur = string[index];

    // apply bonus for matches after a separator
    if prev == ' ' || prev == '_' || prev == '-' || prev == '/' || prev == '\\' {
        score += BONUS_SEPARATOR;
    }

    // apply bonus for camelCases
    if prev.is_lowercase() && cur.is_uppercase() {
        score += BONUS_CAMEL;
    }

    if is_first {
        score += max((index as i32) * PENALTY_LEADING, PENALTY_MAX_LEADING);
    }

    score
}

pub fn fuzzy_match(choice: &str, pattern: &str) -> Option<(i32, Vec<usize>)>{
    if pattern.len() == 0 {
        return Some((0, Vec::new()));
    }

    let choice_chars: Vec<char> = choice.chars().collect();
    let choice_lower = choice.to_lowercase();
    let pattern_chars: Vec<char> = pattern.to_lowercase().chars().collect();

    let mut scores = vec![];
    let mut picked = vec![];

    let mut prev_matched_idx = -1; // to ensure that the pushed char are able to match the pattern
    for pattern_idx in 0..pattern_chars.len() {
        let pattern_char = pattern_chars[pattern_idx];
        let vec_cell = RefCell::new(vec![]);
        {
            let mut vec = vec_cell.borrow_mut();
            for (idx, ch) in choice_lower.chars().enumerate() {
                if ch == pattern_char && (idx as i32) > prev_matched_idx {
                    vec.push((idx, fuzzy_score(&choice_chars, idx, pattern_idx == 0), 0)); // (char_idx, score, vec_idx back_ref)
                }
            }

            if vec.len() <= 0 {
                // not matched
                return None;
            }
            prev_matched_idx = vec[0].0 as i32;
        }
        scores.push(vec_cell);
    }

    for pattern_idx in 0..pattern.len()-1 {
        let cur_row = scores[pattern_idx].borrow();
        let mut next_row = scores[pattern_idx+1].borrow_mut();

        for idx in 0..next_row.len() {
            let (next_char_idx, next_score, _) = next_row[idx];
//(back_ref, &score)
            let (back_ref, score) = cur_row.iter()
                .take_while(|&&(idx, _, _)| idx < next_char_idx)
                .map(|&(char_idx, score, _)| {
                    let adjacent_num = next_char_idx - char_idx - 1;
                    score + next_score + if adjacent_num == 0 {BONUS_ADJACENCY} else {PENALTY_UNMATCHED * adjacent_num as i32}
                })
                .enumerate()
                .max_by_key(|&(_, x)| x)
                .unwrap();

            next_row[idx] = (next_char_idx, score, back_ref);
        }
    }

    let (mut next_col, &(_, score, _)) = scores[pattern.len()-1].borrow().iter().enumerate().max_by_key(|&(_, &x)| x.1).unwrap();
    let mut pattern_idx = pattern.len() as i32 - 1;
    while pattern_idx >= 0 {
        let (idx, _, next) = scores[pattern_idx as usize].borrow()[next_col];
        next_col = next;
        picked.push(idx);
        pattern_idx -= 1;
    }
    picked.reverse();
    Some((score, picked))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_compute_match_length() {
        let choice_1 = "I am a 中国人.";
        let query_1 = "a人";
        assert_eq!(super::compute_match_length(&choice_1, &query_1), Some((2, 8)));

        let choice_2 = "Choice did not matter";
        let query_2 = "";
        assert_eq!(super::compute_match_length(&choice_2, &query_2), Some((0, 0)));

        let choice_3 = "abcdefg";
        let query_3 = "hi";
        assert_eq!(super::compute_match_length(&choice_3, &query_3), None);

        let choice_4 = "Partial match did not count";
        let query_4 = "PP";
        assert_eq!(compute_match_length(&choice_4, &query_4), None);
    }

    #[test]
    fn teset_fuzzy_match() {
        // the score in this test doesn't actually matter, but the index matters.
        let choice_1 = "1111121";
        let query_1 = "21";
        assert_eq!(fuzzy_match(&choice_1, &query_1), Some((-4, vec![5,6])));

        let choice_2 = "Ca";
        let query_2 = "ac";
        assert_eq!(fuzzy_match(&choice_2, &query_2), None);

        let choice_3 = ".";
        let query_3 = "s";
        assert_eq!(fuzzy_match(&choice_3, &query_3), None);

        let choice_4 = "AaBbCc";
        let query_4 = "abc";
        assert_eq!(fuzzy_match(&choice_4, &query_4), Some((28, vec![0,2,4])));
    }
}
<|endoftext|>


<commit_before>use dimensions::Dimensions;

/// A trait that indicates whether or not a widget
/// builder is positionable.
pub trait Shapeable {
    fn dimensions(self, width: f64, height: f64) -> Self;
    fn dim(self, dim: Dimensions) -> Self;
    fn width(self, width: f64) -> Self;
    fn height(self, height: f64) -> Self;
}

/// Size property.
#[derive(Copy, Clone)]
pub struct Size(pub Dimensions);

/*
impl<T> Shapeable for T
    where
        (Size, T): Pair<Data = Size, Object = T> + SetAt + GetFrom
{
    #[inline]
    fn dimensions(self, width: f64, height: f64) -> Self {
        self.set(Size([width, height]))
    }
    #[inline]
    fn dim(self, dim: ::dimensions::Dimensions) -> Self {
        self.set(Size(dim))
    }
    #[inline]
    fn width(self, width: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([width, size[1]]))
    }
    #[inline]
    fn height(self, height: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([size[0], height]))
    }
}
*/
<commit_msg>Added `Shapeable::get_dim`<commit_after>use dimensions::Dimensions;

/// A trait that indicates whether or not a widget
/// builder is positionable.
pub trait Shapeable {
    fn get_dim(&self) -> Dimensions;
    fn dim(self, dim: Dimensions) -> Self;
    fn dimensions(self, width: f64, height: f64) -> Self;
    fn width(self, width: f64) -> Self;
    fn height(self, height: f64) -> Self;
}

/// Size property.
#[derive(Copy, Clone)]
pub struct Size(pub Dimensions);

/*
impl<T> Shapeable for T
    where
        (Size, T): Pair<Data = Size, Object = T> + SetAt + GetFrom
{
    #[inline]
    fn dimensions(self, width: f64, height: f64) -> Self {
        self.set(Size([width, height]))
    }
    #[inline]
    fn dim(self, dim: ::dimensions::Dimensions) -> Self {
        self.set(Size(dim))
    }
    #[inline]
    fn width(self, width: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([width, size[1]]))
    }
    #[inline]
    fn height(self, height: f64) -> Self {
        let Size(size) = self.get();
        self.set(Size([size[0], height]))
    }
}
*/
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::repeat;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| unicode_width::UnicodeWidthStr::width(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        // todo: this code could really use some comments
        let width = width / state.style.char_width;
        let pct = state.fraction();
        let fill = pct * width as f32;
        let fill = fill as usize;
        let head = if pct > 0.0 && fill < width { 1 } else { 0 };

        let pb = repeat(&state.style.progress_chars[0]).take(fill).fold(
            String::new(),
            |mut acc, new| {
                acc.push_str(&new);
                acc
            },
        );
        let cur = if head == 1 {
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n == 0 {
                1
            } else {
                n.saturating_sub((fill * n) % n)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };
        let bg = width.saturating_sub(fill).saturating_sub(head);
        let rest = repeat(state.style.progress_chars.last().unwrap())
            .take(bg)
            .fold(String::new(), |mut acc, new| {
                acc.push_str(&new);
                acc
            });
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<commit_msg>collect instead of fold<commit_after>use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::repeat;

use console::{measure_text_width, Style};

use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration};
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
    tick_strings: Vec<Box<str>>,
    progress_chars: Vec<Box<str>>,
    template: Cow<'static, str>,
    // how unicode-big each char in progress_chars is
    char_width: usize,
}

fn segment(s: &str) -> Vec<Box<str>> {
    UnicodeSegmentation::graphemes(s, true)
        .map(|s| s.into())
        .collect()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
    c.iter()
        .map(|s| unicode_width::UnicodeWidthStr::width(s.as_ref()))
        .fold(None, |acc, new| {
            match acc {
                None => return Some(new),
                Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
            }
            acc
        })
        .unwrap()
}

impl ProgressStyle {
    /// Returns the default progress bar style for bars.
    pub fn default_bar() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
        }
    }

    /// Returns the default progress bar style for spinners.
    pub fn default_spinner() -> ProgressStyle {
        let progress_chars = segment("█░");
        let char_width = width(&progress_chars);
        ProgressStyle {
            tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
                .chars()
                .map(|c| c.to_string().into())
                .collect(),
            progress_chars,
            char_width,
            template: Cow::Borrowed("{spinner} {msg}"),
        }
    }

    /// Sets the tick character sequence for spinners.
    pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
        self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
        self
    }

    /// Sets the tick string sequence for spinners.
    pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
        self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
        self
    }

    /// Sets the progress characters `(filled, current, to do)`.
    /// You can pass more then three for a more detailed display.
    /// All passed grapheme clusters need to be of equal width.
    pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
        self.progress_chars = segment(s);
        self.char_width = width(&self.progress_chars);
        self
    }

    /// Sets the template string for the progress bar.
    pub fn template(mut self, s: &str) -> ProgressStyle {
        self.template = Cow::Owned(s.into());
        self
    }

    /// Returns the tick char for a given number.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")]
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.get_tick_str(idx).chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for a given number.
    pub fn get_tick_str(&self, idx: u64) -> &str {
        &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")]
    pub fn get_final_tick_char(&self) -> char {
        self.get_final_tick_str().chars().next().unwrap_or(' ')
    }

    /// Returns the tick string for the finished state.
    pub fn get_final_tick_str(&self) -> &str {
        &self.tick_strings[self.tick_strings.len() - 1]
    }

    pub(crate) fn format_bar(
        &self,
        state: &ProgressState,
        width: usize,
        alt_style: Option<&Style>,
    ) -> String {
        // todo: this code could really use some comments
        let width = width / state.style.char_width;
        let pct = state.fraction();
        let fill = pct * width as f32;
        let fill = fill as usize;
        let head = if pct > 0.0 && fill < width { 1 } else { 0 };

        let pb: String = repeat(&state.style.progress_chars[0])
            .take(fill)
            .map(|b| b.as_ref())
            .collect();

        let cur = if head == 1 {
            let n = state.style.progress_chars.len().saturating_sub(2);
            let cur_char = if n == 0 {
                1
            } else {
                n.saturating_sub((fill * n) % n)
            };
            state.style.progress_chars[cur_char].to_string()
        } else {
            "".into()
        };
        let bg = width.saturating_sub(fill).saturating_sub(head);
        let rest: String = repeat(state.style.progress_chars.last().unwrap())
            .take(bg)
            .map(|b| b.as_ref())
            .collect();
        format!(
            "{}{}{}",
            pb,
            cur,
            alt_style.unwrap_or(&Style::new()).apply_to(rest)
        )
    }

    pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.template.lines() {
            let wide_element = RefCell::new(None);

            let s = expand_template(line, |var| {
                let key = var.key;

                match key {
                    "wide_bar" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("bar"));
                        "\x00".into()
                    }
                    "bar" => {
                        self.format_bar(state, var.width.unwrap_or(20), var.alt_style.as_ref())
                    }
                    "spinner" => state.current_tick_str().to_string(),
                    "wide_msg" => {
                        *wide_element.borrow_mut() = Some(var.duplicate_for_key("msg"));
                        "\x00".into()
                    }
                    "msg" => state.message().to_string(),
                    "prefix" => state.prefix().to_string(),
                    "pos" => pos.to_string(),
                    "len" => len.to_string(),
                    "percent" => format!("{:.*}", 0, state.fraction() * 100f32),
                    "bytes" => format!("{}", HumanBytes(state.pos)),
                    "total_bytes" => format!("{}", HumanBytes(state.len)),
                    "decimal_bytes" => format!("{}", DecimalBytes(state.pos)),
                    "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)),
                    "binary_bytes" => format!("{}", BinaryBytes(state.pos)),
                    "binary_total_bytes" => format!("{}", BinaryBytes(state.len)),
                    "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())),
                    "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())),
                    "per_sec" => format!("{}/s", state.per_sec()),
                    "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())),
                    "eta_precise" => format!("{}", FormattedDuration(state.eta())),
                    "eta" => format!("{:#}", HumanDuration(state.eta())),
                    _ => "".into(),
                }
            });

            rv.push(if let Some(ref var) = *wide_element.borrow() {
                let total_width = state.width();
                if var.key == "bar" {
                    let bar_width = total_width.saturating_sub(measure_text_width(&s));
                    s.replace(
                        "\x00",
                        &self.format_bar(state, bar_width, var.alt_style.as_ref()),
                    )
                } else if var.key == "msg" {
                    let msg_width = total_width.saturating_sub(measure_text_width(&s));
                    let msg = pad_str(state.message(), msg_width, var.align, true);
                    s.replace(
                        "\x00",
                        if var.last_element {
                            msg.trim_end()
                        } else {
                            &msg
                        },
                    )
                } else {
                    unreachable!()
                }
            } else {
                s.to_string()
            });
        }

        rv
    }
}
<|endoftext|>