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

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

extern crate getopts;
extern crate pretty_bytes;
use pretty_bytes::converter::convert;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::fs;
use std::fs::{ReadDir, DirEntry, FileType, Metadata};
use std::ffi::{OsString};
use std::path::Path;
use std::io::Write;

#[derive(Copy, Clone, PartialEq)]
enum Mode {
    Help,
    Version,
    List
}

static NAME: &'static str = "ls";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

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

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

    opts.optflag("a", "all", "Do not ignore hidden files (files with names that start with '.').");
    opts.optflag("A", "almost-all", "In a directory, do not ignore all file names that start with '.', only ignore '.' and '..'.");
    opts.optflag("B", "ignore-backups", "Ignore files that end with ~. Equivalent to using `--ignore='*~'` or `--ignore='.*~'.");
    opts.optflag("d", "directory", "Only list the names of directories, rather than listing directory contents. This will not follow symbolic links unless one of `--dereference-command-line (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is specified.");
    opts.optflag("H", "dereference-command-line", "If a command line argument specifies a symbolic link, show information about the linked file rather than the link itself.");
    opts.optflag("h", "human-readable", "Print human readable file sizes (e.g. 1K 234M 56G).");

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

    let mode = if matches.opt_present("version") {
        Mode::Version
    } else if matches.opt_present("help") {
        Mode::Help
    } else {
        Mode::List
    };

    match mode {
        Mode::Version => version(),
        Mode::Help    => help(),
        Mode::List    => list(matches)
    }

    0
}

fn version() {
     println!("{} {}", NAME, VERSION);
}

fn help() {
    let msg = format!("{0} {1}\n\n\
                       Usage:  {0} [OPTION]... DIRECTORY \n   \
                          or:  {0} [OPTION]... [FILE]... \n \
                       \n \
                       By default, ls will list the files and contents of any directories on \
                       the command line, expect that it will ignore files and directories \
                       whose names start with '.'. \n\
                       \n", NAME, VERSION);
    println!("{}", msg);
}

fn list(options: getopts::Matches) {
    let locs: Vec<String> = if options.free.is_empty() {
        vec![String::from(".")]
    } else {
        options.free.iter().cloned().collect()
    };

    for loc in locs {
        let p = Path::new(&loc);

        if !p.exists() {
            show_error!("Cannot find path '{}' because it does not exist.", loc);
            panic!();
        }

        if p.is_dir() {
            match fs::read_dir(p) {
                Err(e)      => {
                    show_error!("Cannot read directory '{}'. \n Reason: {}", loc, e);
                    panic!();
                },
                Ok(entries) => enter_directory(entries, &options),
            };
        }

        if p.is_file() {
            display_item(Path::new(p), &options)
        }
    }
}

fn enter_directory(contents: ReadDir, options: &getopts::Matches) {
    for entry in contents {
        let entry = match entry {
            Err(err) => {
                show_error!("{}", err);
                panic!();
            },
            Ok(en) => en
        };

        // Currently have a DirEntry that we can believe in.
        display_dir_entry(entry, options);
    }
}

fn display_dir_entry(entry: DirEntry, options: &getopts::Matches) {
    let md = match entry.metadata() {
        Err(e) => {
            show_error!("Unable to retrieve metadata for {}. \n Error: {}",
                        display_file_name(entry.file_name()), e);
            panic!();
        },
        Ok(md) => md
    };

    println!(" {}{} {} somebody somegroup {: >9} {}",
             display_file_type(entry.file_type()),
             display_permissions(&md),
             display_symlink_count(&md),
             display_file_size(&md, options),
             display_file_name(entry.file_name())
            );
}

fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String {
    if options.opt_present("human-readable") {
        convert(metadata.len() as f64)
    } else {
        metadata.len().to_string()
    }
}

fn display_file_type(file_type: Result<FileType, std::io::Error>) -> String {
    let file_type = match file_type {
        Err(e) => {
            show_error!("{}", e);
            panic!()
        },
        Ok(ft) => ft
    };

    if file_type.is_dir() {
        "d".to_string()
    } else if file_type.is_symlink() {
        "l".to_string()
    } else {
        "-".to_string()
    }
}

fn display_file_name(name: OsString) -> String {
    name.to_string_lossy().into_owned()
}

#[cfg(target_family = "windows")]
#[allow(unused_variables)]
fn display_symlink_count(metadata: &Metadata) -> String {
    // Currently not sure of how to get this on Windows, so I'm punting.
    // Git Bash looks like it may do the same thing.
    String::from("1")
}

#[cfg(target_family = "unix")]
fn display_symlink_count(metadata: &Metadata) -> String {
    use std::os::unix::fs::MetadataExt;

    metadata.nlink().to_string()
}

#[cfg(target_family = "windows")]
#[allow(unused_variables)]
fn display_permissions(metadata: &Metadata) -> String {
    String::from("---------")
}

#[cfg(target_family = "unix")]
fn display_permissions(metadata: &Metadata) -> String {
    use std::os::unix::fs::PermissionsExt;

    "xxxxxxxxx".to_string();
}

fn display_item(item: &Path, options: &getopts::Matches) {
    // let fileType = item.file
    // let mut fileMeta = String::new();

    // fileMeta = fileMeta + if item.is_dir() {
    //                             "d"
    // } else if item.sy
    //                         } else {
    //                             "-"
    //                         };

    // println!("{}{}", displayString, item.display());
}<commit_msg>Fixing trailing semicolon in display_permissions<commit_after>#![crate_name = "uu_ls"]

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

extern crate getopts;
extern crate pretty_bytes;
use pretty_bytes::converter::convert;

#[macro_use]
extern crate uucore;

use getopts::Options;
use std::fs;
use std::fs::{ReadDir, DirEntry, FileType, Metadata};
use std::ffi::{OsString};
use std::path::Path;
use std::io::Write;

#[derive(Copy, Clone, PartialEq)]
enum Mode {
    Help,
    Version,
    List
}

static NAME: &'static str = "ls";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

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

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

    opts.optflag("a", "all", "Do not ignore hidden files (files with names that start with '.').");
    opts.optflag("A", "almost-all", "In a directory, do not ignore all file names that start with '.', only ignore '.' and '..'.");
    opts.optflag("B", "ignore-backups", "Ignore files that end with ~. Equivalent to using `--ignore='*~'` or `--ignore='.*~'.");
    opts.optflag("d", "directory", "Only list the names of directories, rather than listing directory contents. This will not follow symbolic links unless one of `--dereference-command-line (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is specified.");
    opts.optflag("H", "dereference-command-line", "If a command line argument specifies a symbolic link, show information about the linked file rather than the link itself.");
    opts.optflag("h", "human-readable", "Print human readable file sizes (e.g. 1K 234M 56G).");

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

    let mode = if matches.opt_present("version") {
        Mode::Version
    } else if matches.opt_present("help") {
        Mode::Help
    } else {
        Mode::List
    };

    match mode {
        Mode::Version => version(),
        Mode::Help    => help(),
        Mode::List    => list(matches)
    }

    0
}

fn version() {
     println!("{} {}", NAME, VERSION);
}

fn help() {
    let msg = format!("{0} {1}\n\n\
                       Usage:  {0} [OPTION]... DIRECTORY \n   \
                          or:  {0} [OPTION]... [FILE]... \n \
                       \n \
                       By default, ls will list the files and contents of any directories on \
                       the command line, expect that it will ignore files and directories \
                       whose names start with '.'. \n\
                       \n", NAME, VERSION);
    println!("{}", msg);
}

fn list(options: getopts::Matches) {
    let locs: Vec<String> = if options.free.is_empty() {
        vec![String::from(".")]
    } else {
        options.free.iter().cloned().collect()
    };

    for loc in locs {
        let p = Path::new(&loc);

        if !p.exists() {
            show_error!("Cannot find path '{}' because it does not exist.", loc);
            panic!();
        }

        if p.is_dir() {
            match fs::read_dir(p) {
                Err(e)      => {
                    show_error!("Cannot read directory '{}'. \n Reason: {}", loc, e);
                    panic!();
                },
                Ok(entries) => enter_directory(entries, &options),
            };
        }

        if p.is_file() {
            display_item(Path::new(p), &options)
        }
    }
}

fn enter_directory(contents: ReadDir, options: &getopts::Matches) {
    for entry in contents {
        let entry = match entry {
            Err(err) => {
                show_error!("{}", err);
                panic!();
            },
            Ok(en) => en
        };

        // Currently have a DirEntry that we can believe in.
        display_dir_entry(entry, options);
    }
}

fn display_dir_entry(entry: DirEntry, options: &getopts::Matches) {
    let md = match entry.metadata() {
        Err(e) => {
            show_error!("Unable to retrieve metadata for {}. \n Error: {}",
                        display_file_name(entry.file_name()), e);
            panic!();
        },
        Ok(md) => md
    };

    println!(" {}{} {} somebody somegroup {: >9} {}",
             display_file_type(entry.file_type()),
             display_permissions(&md),
             display_symlink_count(&md),
             display_file_size(&md, options),
             display_file_name(entry.file_name())
            );
}

fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String {
    if options.opt_present("human-readable") {
        convert(metadata.len() as f64)
    } else {
        metadata.len().to_string()
    }
}

fn display_file_type(file_type: Result<FileType, std::io::Error>) -> String {
    let file_type = match file_type {
        Err(e) => {
            show_error!("{}", e);
            panic!()
        },
        Ok(ft) => ft
    };

    if file_type.is_dir() {
        "d".to_string()
    } else if file_type.is_symlink() {
        "l".to_string()
    } else {
        "-".to_string()
    }
}

fn display_file_name(name: OsString) -> String {
    name.to_string_lossy().into_owned()
}

#[cfg(target_family = "windows")]
#[allow(unused_variables)]
fn display_symlink_count(metadata: &Metadata) -> String {
    // Currently not sure of how to get this on Windows, so I'm punting.
    // Git Bash looks like it may do the same thing.
    String::from("1")
}

#[cfg(target_family = "unix")]
fn display_symlink_count(metadata: &Metadata) -> String {
    use std::os::unix::fs::MetadataExt;

    metadata.nlink().to_string()
}

#[cfg(target_family = "windows")]
#[allow(unused_variables)]
fn display_permissions(metadata: &Metadata) -> String {
    String::from("---------")
}

#[cfg(target_family = "unix")]
fn display_permissions(metadata: &Metadata) -> String {
    use std::os::unix::fs::PermissionsExt;

    "xxxxxxxxx".to_string()
}

fn display_item(item: &Path, options: &getopts::Matches) {
    // let fileType = item.file
    // let mut fileMeta = String::new();

    // fileMeta = fileMeta + if item.is_dir() {
    //                             "d"
    // } else if item.sy
    //                         } else {
    //                             "-"
    //                         };

    // println!("{}{}", displayString, item.display());
}<|endoftext|>


<commit_before>use pb::ProgressBar;
use std::str::from_utf8;
use std::io::{Stdout, Result, Write};
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};

pub struct MultiBar<T: Write> {
    nlines: usize,

    lines: Vec<String>,

    nbars: usize,

    chan: (Sender<WriteMsg>, Receiver<WriteMsg>),

    handle: T,
}

impl MultiBar<Stdout> {
    /// Create a new MultiBar with stdout as a writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    /// mb.println("Application header:");
    ///
    /// let mut p1 = mb.create_bar(count);
    /// let _ = thread::spawn(move || {
    ///     for _ in 0..count {
    ///         p1.inc();
    ///         thread::sleep(Duration::from_millis(100));
    ///     }
    ///     // notify the multibar that this bar finished.
    ///     p1.finish();
    /// });
    ///
    /// mb.println("add a separator between the two bars");
    ///
    /// let mut p2 = mb.create_bar(count * 2);
    /// let _ = thread::spawn(move || {
    ///     for _ in 0..count * 2 {
    ///         p2.inc();
    ///         thread::sleep(Duration::from_millis(100));
    ///     }
    ///     // notify the multibar that this bar finished.
    ///     p2.finish();
    /// });
    ///
    /// // start listen to all bars changes.
    /// // this is a blocking operation, until all bars will finish.
    /// // to ignore blocking, you can run it in a different thread.
    /// mb.listen();
    /// ```
    pub fn new() -> MultiBar<Stdout> {
        MultiBar::on(::std::io::stdout())
    }
}

impl<T: Write> MultiBar<T> {
    /// Create a new MultiBar with an arbitrary writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    /// use std::io::stderr;
    ///
    /// let mut mb = MultiBar::on(stderr());
    /// // ...
    /// // see full example in `MultiBar::new`
    /// // ...
    /// ```
    pub fn on(handle: T) -> MultiBar<T> {
        MultiBar {
            nlines: 0,
            nbars: 0,
            lines: Vec::new(),
            chan: mpsc::channel(),
            handle: handle,
        }
    }

    /// println used to add text lines between the bars.
    /// for example: you could add a header to your application,
    /// or text separators between bars.
    ///
    ///   /// Create a new MultiBar with stdout as a writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    /// mb.println("Application header:");
    ///
    /// let mut p1 = MultiBar::create_bar(count);
    /// // ...
    ///
    /// mb.println("Text line between bar1 and bar2");
    ///
    /// let mut p2 = MultiBar::create_bar(count);
    /// // ...
    ///
    /// mb.println("Text line between bar2 and bar3");
    ///
    /// // ...
    /// // ...
    /// mb.listen();
    /// ```
    pub fn println(&mut self, s: &str) {
        self.lines.push(s.to_owned());
        self.nlines += 1;
    }

    /// create_bar creates new `ProgressBar` with `Pipe` as the writer.
    ///
    /// The ordering of the method calls is important. it means that in
    /// the first call, you get a progress bar in level 1, in the 2nd call,
    /// you get a progress bar in level 2, and so on.
    ///
    /// ProgressBar that finish its work, must call `finish()` to
    /// notify the MultiBar about it.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    ///
    /// // progress bar in level 1
    /// let mut p1 = MultiBar::create_bar(count1);
    /// // ...
    ///
    /// // progress bar in level 2
    /// let mut p2 = MultiBar::create_bar(count2);
    /// // ...
    ///
    /// // progress bar in level 3
    /// let mut p3 = MultiBar::create_bar(count3);
    ///
    /// // ...
    /// mb.listen();
    /// ```
    pub fn create_bar(&mut self, total: u64) -> ProgressBar<Pipe> {
        self.println("");
        self.nbars += 1;
        let mut p = ProgressBar::on(Pipe {
                                        level: self.nlines - 1,
                                        chan: self.chan.0.clone(),
                                    },
                                    total);
        p.add(0);
        p
    }

    /// listen start listen to all bars changes.
    ///
    /// ProgressBar that finish its work, must call `finish()` to
    /// notify the MultiBar about it.
    ///
    /// This is a blocking operation and blocks until all bars will
    /// finish.
    /// To ignore blocking, you can run it in a different thread.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    ///
    /// // ...
    /// // create some bars here
    /// // ...
    ///
    /// thread::spawn(move || {
    ///     mb.listen();
    ///     println!("all bars done!");
    /// });
    ///
    /// // ...
    /// ```
    pub fn listen(&mut self) {
        let mut first = true;
        let mut nbars = self.nbars;
        while nbars > 0 {

            // receive message
            let msg = self.chan.1.recv().unwrap();
            if msg.done {
                nbars -= 1;
                continue;
            }
            self.lines[msg.level] = msg.string;

            // and draw
            let mut out = String::new();
            // TODO: implement tty::move_cursor_up(lines), and use it here
            if !first {
                out.push_str(&format!("\x1B[{}A", self.nlines));
            } else {
                first = false;
            }
            for l in self.lines.iter() {
                out.push_str(&format!("\r{}\n", l));
            }
            printfl!(self.handle, "{}", out);
        }
    }
}

pub struct Pipe {
    level: usize,
    chan: Sender<WriteMsg>,
}

impl Write for Pipe {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        let s = from_utf8(buf).unwrap().to_owned();
        self.chan
            .send(WriteMsg {
                // finish method emit empty string
                done: s == "",
                level: self.level,
                string: s,
            })
            .unwrap();
        Ok(1)
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}

// WriteMsg is the message format used to communicate
// between MultiBar and its bars
struct WriteMsg {
    done: bool,
    level: usize,
    string: String,
}
<commit_msg>refac: replace HC with move_cursor method<commit_after>use pb::ProgressBar;
use std::str::from_utf8;
use tty::move_cursor_up;
use std::io::{Stdout, Result, Write};
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};

pub struct MultiBar<T: Write> {
    nlines: usize,

    lines: Vec<String>,

    nbars: usize,

    chan: (Sender<WriteMsg>, Receiver<WriteMsg>),

    handle: T,
}

impl MultiBar<Stdout> {
    /// Create a new MultiBar with stdout as a writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    /// mb.println("Application header:");
    ///
    /// let mut p1 = mb.create_bar(count);
    /// let _ = thread::spawn(move || {
    ///     for _ in 0..count {
    ///         p1.inc();
    ///         thread::sleep(Duration::from_millis(100));
    ///     }
    ///     // notify the multibar that this bar finished.
    ///     p1.finish();
    /// });
    ///
    /// mb.println("add a separator between the two bars");
    ///
    /// let mut p2 = mb.create_bar(count * 2);
    /// let _ = thread::spawn(move || {
    ///     for _ in 0..count * 2 {
    ///         p2.inc();
    ///         thread::sleep(Duration::from_millis(100));
    ///     }
    ///     // notify the multibar that this bar finished.
    ///     p2.finish();
    /// });
    ///
    /// // start listen to all bars changes.
    /// // this is a blocking operation, until all bars will finish.
    /// // to ignore blocking, you can run it in a different thread.
    /// mb.listen();
    /// ```
    pub fn new() -> MultiBar<Stdout> {
        MultiBar::on(::std::io::stdout())
    }
}

impl<T: Write> MultiBar<T> {
    /// Create a new MultiBar with an arbitrary writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    /// use std::io::stderr;
    ///
    /// let mut mb = MultiBar::on(stderr());
    /// // ...
    /// // see full example in `MultiBar::new`
    /// // ...
    /// ```
    pub fn on(handle: T) -> MultiBar<T> {
        MultiBar {
            nlines: 0,
            nbars: 0,
            lines: Vec::new(),
            chan: mpsc::channel(),
            handle: handle,
        }
    }

    /// println used to add text lines between the bars.
    /// for example: you could add a header to your application,
    /// or text separators between bars.
    ///
    ///   /// Create a new MultiBar with stdout as a writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    /// mb.println("Application header:");
    ///
    /// let mut p1 = MultiBar::create_bar(count);
    /// // ...
    ///
    /// mb.println("Text line between bar1 and bar2");
    ///
    /// let mut p2 = MultiBar::create_bar(count);
    /// // ...
    ///
    /// mb.println("Text line between bar2 and bar3");
    ///
    /// // ...
    /// // ...
    /// mb.listen();
    /// ```
    pub fn println(&mut self, s: &str) {
        self.lines.push(s.to_owned());
        self.nlines += 1;
    }

    /// create_bar creates new `ProgressBar` with `Pipe` as the writer.
    ///
    /// The ordering of the method calls is important. it means that in
    /// the first call, you get a progress bar in level 1, in the 2nd call,
    /// you get a progress bar in level 2, and so on.
    ///
    /// ProgressBar that finish its work, must call `finish()` to
    /// notify the MultiBar about it.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    ///
    /// // progress bar in level 1
    /// let mut p1 = MultiBar::create_bar(count1);
    /// // ...
    ///
    /// // progress bar in level 2
    /// let mut p2 = MultiBar::create_bar(count2);
    /// // ...
    ///
    /// // progress bar in level 3
    /// let mut p3 = MultiBar::create_bar(count3);
    ///
    /// // ...
    /// mb.listen();
    /// ```
    pub fn create_bar(&mut self, total: u64) -> ProgressBar<Pipe> {
        self.println("");
        self.nbars += 1;
        let mut p = ProgressBar::on(Pipe {
                                        level: self.nlines - 1,
                                        chan: self.chan.0.clone(),
                                    },
                                    total);
        p.add(0);
        p
    }

    /// listen start listen to all bars changes.
    ///
    /// ProgressBar that finish its work, must call `finish()` to
    /// notify the MultiBar about it.
    ///
    /// This is a blocking operation and blocks until all bars will
    /// finish.
    /// To ignore blocking, you can run it in a different thread.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::MultiBar;
    ///
    /// let mut mb = MultiBar::new();
    ///
    /// // ...
    /// // create some bars here
    /// // ...
    ///
    /// thread::spawn(move || {
    ///     mb.listen();
    ///     println!("all bars done!");
    /// });
    ///
    /// // ...
    /// ```
    pub fn listen(&mut self) {
        let mut first = true;
        let mut nbars = self.nbars;
        while nbars > 0 {

            // receive message
            let msg = self.chan.1.recv().unwrap();
            if msg.done {
                nbars -= 1;
                continue;
            }
            self.lines[msg.level] = msg.string;

            // and draw
            let mut out = String::new();
            if !first {
                out += &move_cursor_up(self.nlines);
            } else {
                first = false;
            }
            for l in self.lines.iter() {
                out.push_str(&format!("\r{}\n", l));
            }
            printfl!(self.handle, "{}", out);
        }
    }
}

pub struct Pipe {
    level: usize,
    chan: Sender<WriteMsg>,
}

impl Write for Pipe {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        let s = from_utf8(buf).unwrap().to_owned();
        self.chan
            .send(WriteMsg {
                // finish method emit empty string
                done: s == "",
                level: self.level,
                string: s,
            })
            .unwrap();
        Ok(1)
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}

// WriteMsg is the message format used to communicate
// between MultiBar and its bars
struct WriteMsg {
    done: bool,
    level: usize,
    string: String,
}
<|endoftext|>


<commit_before>use rand::random;
use std::collections::HashMap;

#[derive(Debug,PartialEq)]
pub struct SrcBlock {
    pub name: String,
    pub src: Vec<SrcKind>
}

#[derive(Debug,PartialEq)]
pub struct DefBlock {
    pub name: String,
    pub defs: Vec<(String,VarKind)>
}

#[derive(Debug,PartialEq)]
pub enum BlockKind {
    Src(SrcBlock),
    Def(DefBlock),
}

/// delimited by new line
#[derive(Debug,PartialEq)]
pub enum SrcKind {
    Logic(String, LogicKind), // ex: item_logic has_item

    // references logic in env and emits varkinds;
    // logic must resolve to true
    // ex: if item_logic give_quest
    // Can optionally end execution and begin next node
    If(ExpectKind, Vec<VarKind>, Option<String>),

    Composite(String,ExpectKind,Vec<String>),
    Next(String), // ends execution and begins next node
}

#[derive(Debug,PartialEq)]
pub enum ExpectKind {
    All,
    Any,
    None,

    Ref(String) // references env variable set from logic
}
impl ExpectKind {
    pub fn parse(s: String) -> ExpectKind {
        match &s[..] {
            "all" => ExpectKind::All,
            "any" => ExpectKind::Any,
            "none" => ExpectKind::None,
            _ => ExpectKind::Ref(s),
        }
    }
}

impl SrcKind {
    fn eval<D:Eval> (&self, state: &mut HashMap<String,bool>, data: &D)
                     -> (Vec<VarKind>,Option<String>)
    {
        match self {
            &SrcKind::Next(ref node) => {
                return (vec![],Some(node.clone()))
            },
            &SrcKind::Logic(ref name, ref logic) => { //logic updates state
                let name = name.clone();
                match logic {
                    &LogicKind::Is(ref lookup) => {
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Bool(v) => { state.insert(name,v); },
                                _ => { state.insert(name,true); }, //if exists?
                            }
                        }
                    },
                    &LogicKind::IsNot(ref lookup) => { //inverse state
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Bool(v) => {
                                    if !v { state.insert(name,true); }
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    },

                    &LogicKind::GT(ref lookup, ref var) => {
                        let num:f32 = VarKind::get_num::<D>(var,data).unwrap();

                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Num(num_) => {
                                    state.insert(name, num_ > num);
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    },
                    &LogicKind::LT(ref lookup, ref var) => {
                        let num = VarKind::get_num::<D>(var,data).unwrap();

                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Num(num_) => {
                                    state.insert(name, num_ < num);
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    }
                }

                return (vec![],None) // logic does not return anything
            },
            &SrcKind::Composite(ref name, ref x, ref lookups) => {
                let mut comp_value = false;
                match x {
                    &ExpectKind::All => { // all must pass as true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                            }
                            else { comp_value = false; break }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::Any => { // first truth passes for set
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::None => { // inverse of any, none must be true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = false;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::Ref(_) => panic!("ERROR: Unexpected parsing") // this should never hit
                }

                return (vec![],None) // composite does not return anything
            },
            &SrcKind::If(ref x, ref v, ref node) => {
                let mut if_value = false;
                match x {
                    &ExpectKind::All => {
                        for n in state.values() {
                            if !n { if_value = false; break }
                            else { if_value = true; }
                        }
                    },
                    &ExpectKind::Any => {
                        for n in state.values() {
                            if *n { if_value = true; break }
                        }
                    },
                    &ExpectKind::None => {
                        for n in state.values() {
                            if !n { if_value = true; }
                            else { if_value = true; break }
                        }
                    },
                    &ExpectKind::Ref(ref lookup) => {
                        let val = state.get(lookup);
                        if val.is_some() && *val.unwrap() { if_value = true; }
                    },
                }

                if if_value { return ((*v).clone(),node.clone()) }
                else { return (vec![],None) }
            }
        }
    }

    pub fn parse(mut exp: Vec<String>) -> SrcKind {
        if exp[0] == "if" {
            if exp.len() < 3 { panic!("ERROR: Invalid IF Logic {:?}",exp) }

            let x = exp.remove(1);

            let mut node = None;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                if next {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| VarKind::parse(n)).collect();
            SrcKind::If(ExpectKind::parse(x),
                        v, node)
        }
        else if exp[0] == "next" {
            if exp.len() == 2 {
                SrcKind::Next(exp.pop().unwrap())
            }
            else { panic!("ERROR: Uneven NEXT Logic {:?}",exp) }
        }
        else {
            let keys = exp.remove(0);
            let mut keys: Vec<&str> = keys.split_terminator(':').collect();

            if keys.len() < 2 { // regular logic
                SrcKind::Logic(keys.pop().unwrap().to_owned(),
                               LogicKind::parse(exp))
            }
            else { // composite type
                let kind = ExpectKind::parse(keys.pop().unwrap().to_owned());
                match kind { // only formal expected types allowed
                    ExpectKind::Ref(_) => { panic!("ERROR: Informal ExpectKind found {:?}", kind) },
                    _ => {}
                }
                SrcKind::Composite(keys.pop().unwrap().to_owned(),
                                   kind,
                                   exp)
            }
        }
    }
}

/// delimited by new line
/// should resolve to boolean
#[derive(Debug,PartialEq)]
pub enum LogicKind {
    GT(String,VarKind), // weight > 1
    LT(String,VarKind),

    //boolean checks
    Is(String),
    IsNot(String),
}

impl LogicKind {
    pub fn parse(mut exp: Vec<String>) -> LogicKind {
        let len = exp.len();

        if len == 1 {
            let mut exp = exp.pop().unwrap();
            let inv = exp.remove(0);
            if inv == '!' {
                LogicKind::IsNot(exp)
            }
            else {
                LogicKind::Is(exp)
            }
        }
        else if len == 3 {
            let var = exp.pop().unwrap();
            let var = VarKind::parse(var);

            let sym = exp.pop().unwrap();
            let key = exp.pop().unwrap();

            if sym == ">" {
                LogicKind::GT(key,var)
            }
            else if sym == "<" {
                LogicKind::LT(key,var)
            }
            else { panic!("ERROR: Invalid LogicKind Syntax") }
        }
        else { panic!("ERROR: Unbalanced LogicKind Syntax ({:?})",exp) }
    }
}

#[derive(Debug,PartialEq, Clone)]
pub enum VarKind {
    String(String),
    Num(f32),
    Bool(bool),
}

impl ToString for VarKind {
    fn to_string(&self) -> String {
        match self {
            &VarKind::String(ref s) => s.clone(),
            &VarKind::Num(ref n) => n.to_string(),
            &VarKind::Bool(ref b) => b.to_string(),
        }
    }
}

impl VarKind {
    pub fn parse(t: String) -> VarKind {
        let val;

        if let Ok(v) = t.parse::<f32>() {
            val = VarKind::Num(v);
        }
        else if let Ok(v) = t.parse::<bool>() {
            val = VarKind::Bool(v);
        }
        else { val = VarKind::String(t) }

        val
    }

    pub fn get_num<D:Eval> (&self, data: &D) -> Result<f32,&'static str> {
        let num;
        match self {
            &VarKind::Num(n) => { num = n; },
            &VarKind::String(ref s) => {
                if let Some(n) = data.eval(s) {
                    match n {
                        VarKind::Num(n) => { num = n; },
                        _ => return Err("ERROR: NaN Evaluation")
                    }
                }
                else {  return Err("ERROR: Empty Evaluation") }
            },
            _ =>  return Err("ERROR: NaN Evaluation")
        }

        return Ok(num)
    }
}

pub trait Eval {
    fn eval (&self, data: &str) -> Option<VarKind>;
}

pub struct Parser {
    //state: HashMap<String,usize>, //lookup table
}

impl Parser {
    pub fn eval_block<D:Eval>
        (block: &BlockKind, data: &D)
         -> (Vec<VarKind>,Option<String>)
    {
        let mut r = vec!();
        let mut node = None;

        match block {
            &BlockKind::Src(ref b) => {
                let mut state: HashMap<String,bool> = HashMap::new();
                for src in b.src.iter() {
                    let (mut vars, node_) = src.eval(&mut state, data);
                    for n in vars.drain(..) { r.push(n); }
                    if node.is_some() { node = node_; break; }
                }
            },
            _ => panic!("ERROR: Unimplemented block evaluation type")
        }

        for var in r.iter_mut() {
            match var {
                &mut VarKind::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;
                    for word in s.split_whitespace() {
                        if started { fs.push(' '); }

                        if word.chars().next().unwrap() == '`' {
                            if let Some(ref val) = data.eval(&word[1..]) {
                                fs.push_str(&val.to_string());
                            }
                        }
                        else {
                            fs.push_str(word);
                        }

                        started = true;
                    }

                    *s = fs;
                },
                _ => {}
            }
        }

        return (r,node)
    }

    pub fn parse_blocks (src: &str) -> Vec<BlockKind> {
        let mut v = vec!();
        let mut exp = String::new();
        let mut exps: Vec<String> = vec!();
        let mut block: Option<BlockKind> = None;

        let mut in_string = false;
        let mut in_comment = false;
        let mut in_vec = false;

        for c in src.chars() {
            if c == '[' { in_vec = true; continue }
            else if c == ']' { in_vec = false; }
            else if c == '#' && !in_string { in_comment = true; }
            else if  c == '\n' && in_comment && !in_string {
                in_comment = false;
                continue;
            }

            if c == '\n' && in_vec { continue }

            if (c == ']' ||
                c == '#' ||
                c == '\n')
                && !in_string
            {
                for n in exp.split_whitespace() {
                    exps.push(n.trim().to_owned());
                }
                exp = String::new();

                if exps.len() < 1 { continue }

                // determine block type
                if block.is_none() {
                    let name = exps.pop().unwrap();

                    if name == "def" {
                        let b = DefBlock {
                            name: exps.pop().unwrap(),
                            defs: vec!()
                        };

                        block = Some(BlockKind::Def(b));
                    }
                    else {
                        let b = SrcBlock {
                            name: name,
                            src: vec!()
                        };

                        block = Some(BlockKind::Src(b));
                    }
                }
                else { // build block type
                    let mut qsyms = vec!();
                    for n in exps.iter_mut() {
                        if n.chars().next().expect("ERROR: Empty QSYM") == '\'' {
                            let mut qsym = "__".to_owned();
                            let sym = n[1..].trim().to_owned();
                            qsym.push_str(&random::<u16>().to_string());

                            qsyms.push(qsym.clone());
                            qsyms.push(sym);
                            *n = qsym;
                        }
                    }

                    match block {
                        Some(BlockKind::Def(ref mut b)) => {
                            b.defs.push((exps[0].to_owned(),
                                         VarKind::parse(exps[1].to_owned())));
                        },
                        Some(BlockKind::Src(ref mut b)) => {
                            //println!("EXPS{:?}",exps); //DEBUG
                            if qsyms.len() > 1 {
                                b.src.push(SrcKind::parse(qsyms));
                            }

                            b.src.push(SrcKind::parse(exps));
                        },
                        _ => {}
                    }

                    exps = vec!();
                }
            }
            else if c == '"' && !in_comment {
                in_string = !in_string;
                if in_string {
                    for n in exp.split_whitespace() {
                        exps.push(n.trim().to_owned());
                    }
                    exp = String::new();
                }
                else if !in_string {
                    exps.push(exp);
                    exp = String::new();
                }
            }
            else if c == ';' && !in_string && !in_comment {
                //fail otherwise, block should be built!
                v.push(block.unwrap());
                block = None;
            }
            else {
                if !in_comment {
                    exp.push(c);
                }
            }
        }

        v
    }
}
<commit_msg>clear up and fix bug<commit_after>use rand::random;
use std::collections::HashMap;

#[derive(Debug,PartialEq)]
pub struct SrcBlock {
    pub name: String,
    pub src: Vec<SrcKind>
}

#[derive(Debug,PartialEq)]
pub struct DefBlock {
    pub name: String,
    pub defs: Vec<(String,VarKind)>
}

#[derive(Debug,PartialEq)]
pub enum BlockKind {
    Src(SrcBlock),
    Def(DefBlock),
}

/// delimited by new line
#[derive(Debug,PartialEq)]
pub enum SrcKind {
    Logic(String, LogicKind), // ex: item_logic has_item

    // references logic in env and emits varkinds;
    // logic must resolve to true
    // ex: if item_logic give_quest
    // Can optionally end execution and begin next node
    If(ExpectKind, Vec<VarKind>, Option<String>),

    Composite(String,ExpectKind,Vec<String>),
    Next(String), // ends execution and begins next node
}

#[derive(Debug,PartialEq)]
pub enum ExpectKind {
    All,
    Any,
    None,

    Ref(String) // references env variable set from logic
}
impl ExpectKind {
    pub fn parse(s: String) -> ExpectKind {
        match &s[..] {
            "all" => ExpectKind::All,
            "any" => ExpectKind::Any,
            "none" => ExpectKind::None,
            _ => ExpectKind::Ref(s),
        }
    }
}

impl SrcKind {
    fn eval<D:Eval> (&self, state: &mut HashMap<String,bool>, data: &D)
                     -> (Vec<VarKind>,Option<String>)
    {
        match self {
            &SrcKind::Next(ref node) => {
                return (vec![],Some(node.clone()))
            },
            &SrcKind::Logic(ref name, ref logic) => { //logic updates state
                let name = name.clone();
                match logic {
                    &LogicKind::Is(ref lookup) => {
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Bool(v) => { state.insert(name,v); },
                                _ => { state.insert(name,true); }, //if exists?
                            }
                        }
                    },
                    &LogicKind::IsNot(ref lookup) => { //inverse state
                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Bool(v) => {
                                    if !v { state.insert(name,true); }
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    },

                    &LogicKind::GT(ref lookup, ref var) => {
                        let num:f32 = VarKind::get_num::<D>(var,data).unwrap();

                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Num(num_) => {
                                    state.insert(name, num_ > num);
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    },
                    &LogicKind::LT(ref lookup, ref var) => {
                        let num = VarKind::get_num::<D>(var,data).unwrap();

                        let r = data.eval(&lookup);
                        if r.is_some() {
                            match r.unwrap() {
                                VarKind::Num(num_) => {
                                    state.insert(name, num_ < num);
                                },
                                _ => { state.insert(name,false); },
                            }
                        }
                    }
                }

                return (vec![],None) // logic does not return anything
            },
            &SrcKind::Composite(ref name, ref x, ref lookups) => {
                let mut comp_value = false;
                match x {
                    &ExpectKind::All => { // all must pass as true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                            }
                            else { comp_value = false; break }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::Any => { // first truth passes for set
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = true;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::None => { // inverse of any, none must be true
                        for lookup in lookups.iter() {
                            let val = state.get(lookup);
                            if val.is_some() && *val.unwrap() {
                                comp_value = false;
                                break;
                            }
                        }

                        state.insert(name.clone(),comp_value);
                    },
                    &ExpectKind::Ref(_) => panic!("ERROR: Unexpected parsing") // this should never hit
                }

                return (vec![],None) // composite does not return anything
            },
            &SrcKind::If(ref x, ref v, ref node) => {
                let mut if_value = false;
                match x {
                    &ExpectKind::All => {
                        for n in state.values() {
                            if !n { if_value = false; break }
                            else { if_value = true; }
                        }
                    },
                    &ExpectKind::Any => {
                        for n in state.values() {
                            if *n { if_value = true; break }
                        }
                    },
                    &ExpectKind::None => {
                        for n in state.values() {
                            if !n { if_value = true; }
                            else { if_value = true; break }
                        }
                    },
                    &ExpectKind::Ref(ref lookup) => {
                        let val = state.get(lookup);
                        if let Some(val) = val {
                            if_value = *val;
                        }
                    },
                }

                if if_value { return ((*v).clone(),node.clone()) }
                else { return (vec![],None) }
            }
        }
    }

    pub fn parse(mut exp: Vec<String>) -> SrcKind {
        if exp[0] == "if" {
            if exp.len() < 3 { panic!("ERROR: Invalid IF Logic {:?}",exp) }

            let x = exp.remove(1);

            let mut node = None;
            if exp.len() > 2 {
                let next = &exp[exp.len() - 2] == "next";
                if next {
                    node = exp.pop();
                    let _ = exp.pop(); // remove next tag
                }
            }

            let v = exp.drain(1..).map(|n| VarKind::parse(n)).collect();
            SrcKind::If(ExpectKind::parse(x),
                        v, node)
        }
        else if exp[0] == "next" {
            if exp.len() == 2 {
                SrcKind::Next(exp.pop().unwrap())
            }
            else { panic!("ERROR: Uneven NEXT Logic {:?}",exp) }
        }
        else {
            let keys = exp.remove(0);
            let mut keys: Vec<&str> = keys.split_terminator(':').collect();

            if keys.len() < 2 { // regular logic
                SrcKind::Logic(keys.pop().unwrap().to_owned(),
                               LogicKind::parse(exp))
            }
            else { // composite type
                let kind = ExpectKind::parse(keys.pop().unwrap().to_owned());
                match kind { // only formal expected types allowed
                    ExpectKind::Ref(_) => { panic!("ERROR: Informal ExpectKind found {:?}", kind) },
                    _ => {}
                }
                SrcKind::Composite(keys.pop().unwrap().to_owned(),
                                   kind,
                                   exp)
            }
        }
    }
}

/// delimited by new line
/// should resolve to boolean
#[derive(Debug,PartialEq)]
pub enum LogicKind {
    GT(String,VarKind), // weight > 1
    LT(String,VarKind),

    //boolean checks
    Is(String),
    IsNot(String),
}

impl LogicKind {
    pub fn parse(mut exp: Vec<String>) -> LogicKind {
        let len = exp.len();

        if len == 1 {
            let mut exp = exp.pop().unwrap();
            let inv = exp.remove(0);
            if inv == '!' {
                LogicKind::IsNot(exp)
            }
            else {
                LogicKind::Is(exp)
            }
        }
        else if len == 3 {
            let var = exp.pop().unwrap();
            let var = VarKind::parse(var);

            let sym = exp.pop().unwrap();
            let key = exp.pop().unwrap();

            if sym == ">" {
                LogicKind::GT(key,var)
            }
            else if sym == "<" {
                LogicKind::LT(key,var)
            }
            else { panic!("ERROR: Invalid LogicKind Syntax") }
        }
        else { panic!("ERROR: Unbalanced LogicKind Syntax ({:?})",exp) }
    }
}

#[derive(Debug,PartialEq, Clone)]
pub enum VarKind {
    String(String),
    Num(f32),
    Bool(bool),
}

impl ToString for VarKind {
    fn to_string(&self) -> String {
        match self {
            &VarKind::String(ref s) => s.clone(),
            &VarKind::Num(ref n) => n.to_string(),
            &VarKind::Bool(ref b) => b.to_string(),
        }
    }
}

impl VarKind {
    pub fn parse(t: String) -> VarKind {
        let val;

        if let Ok(v) = t.parse::<f32>() {
            val = VarKind::Num(v);
        }
        else if let Ok(v) = t.parse::<bool>() {
            val = VarKind::Bool(v);
        }
        else { val = VarKind::String(t) }

        val
    }

    pub fn get_num<D:Eval> (&self, data: &D) -> Result<f32,&'static str> {
        let num;
        match self {
            &VarKind::Num(n) => { num = n; },
            &VarKind::String(ref s) => {
                if let Some(n) = data.eval(s) {
                    match n {
                        VarKind::Num(n) => { num = n; },
                        _ => return Err("ERROR: NaN Evaluation")
                    }
                }
                else {  return Err("ERROR: Empty Evaluation") }
            },
            _ =>  return Err("ERROR: NaN Evaluation")
        }

        return Ok(num)
    }
}

pub trait Eval {
    fn eval (&self, data: &str) -> Option<VarKind>;
}

pub struct Parser {
    //state: HashMap<String,usize>, //lookup table
}

impl Parser {
    pub fn eval_block<D:Eval>
        (block: &BlockKind, data: &D)
         -> (Vec<VarKind>,Option<String>)
    {
        let mut r = vec!();
        let mut node = None;

        match block {
            &BlockKind::Src(ref b) => {
                let mut state: HashMap<String,bool> = HashMap::new();
                for src in b.src.iter() {
                    let (mut vars, node_) = src.eval(&mut state, data);
                    for n in vars.drain(..) { r.push(n); }
                    if node_.is_some() { node = node_; break; }
                }
            },
            _ => panic!("ERROR: Unimplemented block evaluation type")
        }

        for var in r.iter_mut() {
            match var {
                &mut VarKind::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;
                    for word in s.split_whitespace() {
                        if started { fs.push(' '); }

                        if word.chars().next().unwrap() == '`' {
                            if let Some(ref val) = data.eval(&word[1..]) {
                                fs.push_str(&val.to_string());
                            }
                        }
                        else {
                            fs.push_str(word);
                        }

                        started = true;
                    }

                    *s = fs;
                },
                _ => {}
            }
        }

        return (r,node)
    }

    pub fn parse_blocks (src: &str) -> Vec<BlockKind> {
        let mut v = vec!();
        let mut exp = String::new();
        let mut exps: Vec<String> = vec!();
        let mut block: Option<BlockKind> = None;

        let mut in_string = false;
        let mut in_comment = false;
        let mut in_vec = false;

        for c in src.chars() {
            if c == '[' { in_vec = true; continue }
            else if c == ']' { in_vec = false; }
            else if c == '#' && !in_string { in_comment = true; }
            else if  c == '\n' && in_comment && !in_string {
                in_comment = false;
                continue;
            }

            if c == '\n' && in_vec { continue }

            if (c == ']' ||
                c == '#' ||
                c == '\n')
                && !in_string
            {
                for n in exp.split_whitespace() {
                    exps.push(n.trim().to_owned());
                }
                exp = String::new();

                if exps.len() < 1 { continue }

                // determine block type
                if block.is_none() {
                    let name = exps.pop().unwrap();

                    if name == "def" {
                        let b = DefBlock {
                            name: exps.pop().unwrap(),
                            defs: vec!()
                        };

                        block = Some(BlockKind::Def(b));
                    }
                    else {
                        let b = SrcBlock {
                            name: name,
                            src: vec!()
                        };

                        block = Some(BlockKind::Src(b));
                    }
                }
                else { // build block type
                    let mut qsyms = vec!();
                    for n in exps.iter_mut() {
                        if n.chars().next().expect("ERROR: Empty QSYM") == '\'' {
                            let mut qsym = "__".to_owned();
                            let sym = n[1..].trim().to_owned();
                            qsym.push_str(&random::<u16>().to_string());

                            qsyms.push(qsym.clone());
                            qsyms.push(sym);
                            *n = qsym;
                        }
                    }

                    match block {
                        Some(BlockKind::Def(ref mut b)) => {
                            b.defs.push((exps[0].to_owned(),
                                         VarKind::parse(exps[1].to_owned())));
                        },
                        Some(BlockKind::Src(ref mut b)) => {
                            //println!("EXPS{:?}",exps); //DEBUG
                            if qsyms.len() > 1 {
                                b.src.push(SrcKind::parse(qsyms));
                            }

                            b.src.push(SrcKind::parse(exps));
                        },
                        _ => {}
                    }

                    exps = vec!();
                }
            }
            else if c == '"' && !in_comment {
                in_string = !in_string;
                if in_string {
                    for n in exp.split_whitespace() {
                        exps.push(n.trim().to_owned());
                    }
                    exp = String::new();
                }
                else if !in_string {
                    exps.push(exp);
                    exp = String::new();
                }
            }
            else if c == ';' && !in_string && !in_comment {
                //fail otherwise, block should be built!
                v.push(block.unwrap());
                block = None;
            }
            else {
                if !in_comment {
                    exp.push(c);
                }
            }
        }

        v
    }
}
<|endoftext|>


<commit_before>use mozprofile::preferences::Pref;

lazy_static! {
    pub static ref DEFAULT: [(&'static str, Pref); 74] = [
        // Disable automatic downloading of new releases
        ("app.update.auto", Pref::new(false)),

        // Disable automatically upgrading Firefox
        ("app.update.enabled", Pref::new(false)),

        // Increase the APZ content response timeout in tests to 1
        // minute.  This is to accommodate the fact that test environments
        // tends to be slower than production environments (with the
        // b2g emulator being the slowest of them all), resulting in the
        // production timeout value sometimes being exceeded and causing
        // false-positive test failures.
        //
        // (bug 1176798, bug 1177018, bug 1210465)
        ("apz.content_response_timeout", Pref::new(60000)),

        // Enable the dump function, which sends messages to the system
        // console
        ("browser.dom.window.dump.enabled", Pref::new(true)),

        // Indicate that the download panel has been shown once so
        // that whichever download test runs first does not show the popup
        // inconsistently
        ("browser.download.panel.shown", Pref::new(true)),

        // Implicitly accept license
        ("browser.EULA.override", Pref::new(true)),

        // use about:blank as new tab page
        ("browser.newtabpage.enabled", Pref::new(false)),

        // Assume the about:newtab pages intro panels have been shown
        // to not depend on which test runs first and happens to open
        // about:newtab
        ("browser.newtabpage.introShown", Pref::new(true)),

        // Never start the browser in offline mode
        ("browser.offline", Pref::new(false)),

        // Background thumbnails in particular cause grief, and disabling
        // thumbnails in general cannot hurt
        ("browser.pagethumbnails.capturing_disabled", Pref::new(true)),

        // Avoid performing Reader Mode intros during tests
        ("browser.reader.detectedFirstArticle", Pref::new(true)),

        // Disable safebrowsing components
        ("browser.safebrowsing.blockedURIs.enabled", Pref::new(false)),
        ("browser.safebrowsing.downloads.enabled", Pref::new(false)),
        ("browser.safebrowsing.enabled", Pref::new(false)),
        ("browser.safebrowsing.forbiddenURIs.enabled", Pref::new(false)),
        ("browser.safebrowsing.malware.enabled", Pref::new(false)),
        ("browser.safebrowsing.phishing.enabled", Pref::new(false)),

        // Disable updates to search engines
        ("browser.search.update", Pref::new(false)),

        // Do not restore the last open set of tabs if the browser crashed
        ("browser.sessionstore.resume_from_crash", Pref::new(false)),

        // Skip check for default browser on startup
        ("browser.shell.checkDefaultBrowser", Pref::new(false)),

        // Disable Android snippets
        ("browser.snippets.enabled", Pref::new(false)),
        ("browser.snippets.syncPromo.enabled", Pref::new(false)),
        ("browser.snippets.firstrunHomepage.enabled", Pref::new(false)),

        // Do not redirect user when a milestone upgrade of Firefox
        // is detected
        ("browser.startup.homepage_override.mstone", Pref::new("ignore")),

        // Start with a blank page (about:blank)
        ("browser.startup.page", Pref::new(0)),

        // Disable tab animation
        ("browser.tabs.animate", Pref::new(false)),

        // Do not warn when quitting a window with multiple tabs
        ("browser.tabs.closeWindowWithLastTab", Pref::new(false)),

        // Do not allow background tabs to be zombified, otherwise for
        // tests that open additional tabs, the test harness tab itself
        // might get unloaded
        ("browser.tabs.disableBackgroundZombification", Pref::new(false)),

        // Do not warn when closing all other open tabs
        ("browser.tabs.warnOnCloseOtherTabs", Pref::new(false)),

        // Do not warn when multiple tabs will be opened
        ("browser.tabs.warnOnOpen", Pref::new(false)),

        // Disable first run splash page on Windows 10
        ("browser.usedOnWindows10.introURL", Pref::new("")),

        // Disable the UI tour
        ("browser.uitour.enabled", Pref::new(false)),

        // Do not show datareporting policy notifications which can
        // interfere with tests
        ("datareporting.healthreport.about.reportUrl", Pref::new("http://%(server)s/dummy/abouthealthreport/")),
        ("datareporting.healthreport.documentServerURI", Pref::new("http://%(server)s/dummy/healthreport/")),
        ("datareporting.healthreport.logging.consoleEnabled", Pref::new(false)),
        ("datareporting.healthreport.service.enabled", Pref::new(false)),
        ("datareporting.healthreport.service.firstRun", Pref::new(false)),
        ("datareporting.healthreport.uploadEnabled", Pref::new(false)),
        ("datareporting.policy.dataSubmissionEnabled", Pref::new(false)),
        ("datareporting.policy.dataSubmissionPolicyAccepted", Pref::new(false)),
        ("datareporting.policy.dataSubmissionPolicyBypassNotification", Pref::new(true)),

        // Disable popup-blocker
        ("dom.disable_open_during_load", Pref::new(false)),

        // Disable the ProcessHangMonitor
        ("dom.ipc.reportProcessHangs", Pref::new(false)),

        // Disable slow script dialogues
        ("dom.max_chrome_script_run_time", Pref::new(0)),
        ("dom.max_script_run_time", Pref::new(0)),

        // Only load extensions from the application and user profile
        // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
        ("extensions.autoDisableScopes", Pref::new(0)),
        ("extensions.enabledScopes", Pref::new(5)),

        // don't block add-ons for e10s
        ("extensions.e10sBlocksEnabling", Pref::new(false)),

        // Disable metadata caching for installed add-ons by default
        ("extensions.getAddons.cache.enabled", Pref::new(false)),

        // Disable intalling any distribution extensions or add-ons
        ("extensions.installDistroAddons", Pref::new(false)),
        ("extensions.showMismatchUI", Pref::new(false)),

        // Turn off extension updates so they do not bother tests
        ("extensions.update.enabled", Pref::new(false)),
        ("extensions.update.notifyUser", Pref::new(false)),

        // Make sure opening about:addons will not hit the network
        ("extensions.webservice.discoverURL", Pref::new("http://%(server)s/dummy/discoveryURL")),

        // Allow the application to have focus even it runs in the
        // background
        ("focusmanager.testmode", Pref::new(true)),

        // Disable useragent updates
        ("general.useragent.updates.enabled", Pref::new(false)),

        // Always use network provider for geolocation tests so we bypass
        // the macOS dialog raised by the corelocation provider
        ("geo.provider.testing", Pref::new(true)),

        // Do not scan wi-fi
        ("geo.wifi.scan", Pref::new(false)),

        // No hang monitor
        ("hangmonitor.timeout", Pref::new(0)),

        // Show chrome errors and warnings in the error console
        ("javascript.options.showInConsole", Pref::new(true)),

        // Make sure the disk cache does not get auto disabled
        ("network.http.bypass-cachelock-threshold", Pref::new(200000)),

        // Do not prompt for temporary redirects
        ("network.http.prompt-temp-redirect", Pref::new(false)),

        // Disable speculative connections so they are not reported as
        // leaking when they are hanging around
        ("network.http.speculative-parallel-limit", Pref::new(0)),

        // Do not automatically switch between offline and online
        ("network.manage-offline-status", Pref::new(false)),

        // Make sure SNTP requests do not hit the network
        ("network.sntp.pools", Pref::new("%(server)s")),

        // Local documents have access to all other local docments,
        // including directory listings.

        ("security.fileuri.strict_origin_policy", Pref::new(false)),

        // Tests don't wait for the notification button security delay
        ("security.notification_enable_delay", Pref::new(0)),

        // Ensure blocklist updates don't hit the network
        ("services.settings.server", Pref::new("http://%(server)s/dummy/blocklist/")),

        // Do not automatically fill sign-in forms with known usernames
        // and passwords
        ("signon.autofillForms", Pref::new(false)),

        // Disable password capture, so that tests that include forms
        // are not influenced by the presence of the persistent doorhanger
        // notification
        ("signon.rememberSignons", Pref::new(false)),

        // Disable first run pages
        ("startup.homepage_welcome_url", Pref::new("about:blank")),
        ("startup.homepage_welcome_url.additional", Pref::new("")),

        // Prevent starting into safe mode after application crashes
        ("toolkit.startup.max_resumed_crashes", Pref::new(-1)),

        // We want to collect telemetry, but we don't want to send in the results
        ("toolkit.telemetry.server", Pref::new("https://%(server)s/dummy/telemetry/")),
    ];

    pub static ref REQUIRED: [(&'static str, Pref); 5] = [
        // Do not warn on quitting Firefox
        ("browser.warnOnQuit", Pref::new(false)),

        // Do not warn on exit when multiple tabs are open
        ("browser.tabs.warnOnClose", Pref::new(false)),

        // Do not warn when quitting with multiple tabs
        ("browser.showQuitWarning", Pref::new(false)),

        // Until bug 1238095 is fixed, we have to disable safe CPOW checks
        ("dom.ipc.cpows.forbid-unsafe-from-browser", Pref::new(false)),

        // TODO(ato): Should not be needed, as Marionette is enabled by
        // passing the --marionette flag to the binary
        ("marionette.defaultPrefs.enabled", Pref::new(true)),
    ];
}
<commit_msg>prefs: disable flash and plugin container (#590)<commit_after>use mozprofile::preferences::Pref;

lazy_static! {
    pub static ref DEFAULT: [(&'static str, Pref); 74] = [
        // Disable automatic downloading of new releases
        ("app.update.auto", Pref::new(false)),

        // Disable automatically upgrading Firefox
        ("app.update.enabled", Pref::new(false)),

        // Increase the APZ content response timeout in tests to 1
        // minute.  This is to accommodate the fact that test environments
        // tends to be slower than production environments (with the
        // b2g emulator being the slowest of them all), resulting in the
        // production timeout value sometimes being exceeded and causing
        // false-positive test failures.
        //
        // (bug 1176798, bug 1177018, bug 1210465)
        ("apz.content_response_timeout", Pref::new(60000)),

        // Enable the dump function, which sends messages to the system
        // console
        ("browser.dom.window.dump.enabled", Pref::new(true)),

        // Indicate that the download panel has been shown once so
        // that whichever download test runs first does not show the popup
        // inconsistently
        ("browser.download.panel.shown", Pref::new(true)),

        // Implicitly accept license
        ("browser.EULA.override", Pref::new(true)),

        // use about:blank as new tab page
        ("browser.newtabpage.enabled", Pref::new(false)),

        // Assume the about:newtab pages intro panels have been shown
        // to not depend on which test runs first and happens to open
        // about:newtab
        ("browser.newtabpage.introShown", Pref::new(true)),

        // Never start the browser in offline mode
        ("browser.offline", Pref::new(false)),

        // Background thumbnails in particular cause grief, and disabling
        // thumbnails in general cannot hurt
        ("browser.pagethumbnails.capturing_disabled", Pref::new(true)),

        // Avoid performing Reader Mode intros during tests
        ("browser.reader.detectedFirstArticle", Pref::new(true)),

        // Disable safebrowsing components
        ("browser.safebrowsing.blockedURIs.enabled", Pref::new(false)),
        ("browser.safebrowsing.downloads.enabled", Pref::new(false)),
        ("browser.safebrowsing.enabled", Pref::new(false)),
        ("browser.safebrowsing.forbiddenURIs.enabled", Pref::new(false)),
        ("browser.safebrowsing.malware.enabled", Pref::new(false)),
        ("browser.safebrowsing.phishing.enabled", Pref::new(false)),

        // Disable updates to search engines
        ("browser.search.update", Pref::new(false)),

        // Do not restore the last open set of tabs if the browser crashed
        ("browser.sessionstore.resume_from_crash", Pref::new(false)),

        // Skip check for default browser on startup
        ("browser.shell.checkDefaultBrowser", Pref::new(false)),

        // Disable Android snippets
        ("browser.snippets.enabled", Pref::new(false)),
        ("browser.snippets.syncPromo.enabled", Pref::new(false)),
        ("browser.snippets.firstrunHomepage.enabled", Pref::new(false)),

        // Do not redirect user when a milestone upgrade of Firefox
        // is detected
        ("browser.startup.homepage_override.mstone", Pref::new("ignore")),

        // Start with a blank page (about:blank)
        ("browser.startup.page", Pref::new(0)),

        // Disable tab animation
        ("browser.tabs.animate", Pref::new(false)),

        // Do not warn when quitting a window with multiple tabs
        ("browser.tabs.closeWindowWithLastTab", Pref::new(false)),

        // Do not allow background tabs to be zombified, otherwise for
        // tests that open additional tabs, the test harness tab itself
        // might get unloaded
        ("browser.tabs.disableBackgroundZombification", Pref::new(false)),

        // Do not warn when closing all other open tabs
        ("browser.tabs.warnOnCloseOtherTabs", Pref::new(false)),

        // Do not warn when multiple tabs will be opened
        ("browser.tabs.warnOnOpen", Pref::new(false)),

        // Disable first run splash page on Windows 10
        ("browser.usedOnWindows10.introURL", Pref::new("")),

        // Disable the UI tour
        ("browser.uitour.enabled", Pref::new(false)),

        // Do not show datareporting policy notifications which can
        // interfere with tests
        ("datareporting.healthreport.about.reportUrl", Pref::new("http://%(server)s/dummy/abouthealthreport/")),
        ("datareporting.healthreport.documentServerURI", Pref::new("http://%(server)s/dummy/healthreport/")),
        ("datareporting.healthreport.logging.consoleEnabled", Pref::new(false)),
        ("datareporting.healthreport.service.enabled", Pref::new(false)),
        ("datareporting.healthreport.service.firstRun", Pref::new(false)),
        ("datareporting.healthreport.uploadEnabled", Pref::new(false)),
        ("datareporting.policy.dataSubmissionEnabled", Pref::new(false)),
        ("datareporting.policy.dataSubmissionPolicyAccepted", Pref::new(false)),
        ("datareporting.policy.dataSubmissionPolicyBypassNotification", Pref::new(true)),

        // Disable popup-blocker
        ("dom.disable_open_during_load", Pref::new(false)),

        // Disable the ProcessHangMonitor
        ("dom.ipc.reportProcessHangs", Pref::new(false)),

        // Disable slow script dialogues
        ("dom.max_chrome_script_run_time", Pref::new(0)),
        ("dom.max_script_run_time", Pref::new(0)),

        // Only load extensions from the application and user profile
        // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
        ("extensions.autoDisableScopes", Pref::new(0)),
        ("extensions.enabledScopes", Pref::new(5)),

        // don't block add-ons for e10s
        ("extensions.e10sBlocksEnabling", Pref::new(false)),

        // Disable metadata caching for installed add-ons by default
        ("extensions.getAddons.cache.enabled", Pref::new(false)),

        // Disable intalling any distribution extensions or add-ons
        ("extensions.installDistroAddons", Pref::new(false)),
        ("extensions.showMismatchUI", Pref::new(false)),

        // Turn off extension updates so they do not bother tests
        ("extensions.update.enabled", Pref::new(false)),
        ("extensions.update.notifyUser", Pref::new(false)),

        // Make sure opening about:addons will not hit the network
        ("extensions.webservice.discoverURL", Pref::new("http://%(server)s/dummy/discoveryURL")),

        // Allow the application to have focus even it runs in the
        // background
        ("focusmanager.testmode", Pref::new(true)),

        // Disable useragent updates
        ("general.useragent.updates.enabled", Pref::new(false)),

        // Always use network provider for geolocation tests so we bypass
        // the macOS dialog raised by the corelocation provider
        ("geo.provider.testing", Pref::new(true)),

        // Do not scan wi-fi
        ("geo.wifi.scan", Pref::new(false)),

        // No hang monitor
        ("hangmonitor.timeout", Pref::new(0)),

        // Show chrome errors and warnings in the error console
        ("javascript.options.showInConsole", Pref::new(true)),

        // Make sure the disk cache does not get auto disabled
        ("network.http.bypass-cachelock-threshold", Pref::new(200000)),

        // Do not prompt for temporary redirects
        ("network.http.prompt-temp-redirect", Pref::new(false)),

        // Disable speculative connections so they are not reported as
        // leaking when they are hanging around
        ("network.http.speculative-parallel-limit", Pref::new(0)),

        // Do not automatically switch between offline and online
        ("network.manage-offline-status", Pref::new(false)),

        // Make sure SNTP requests do not hit the network
        ("network.sntp.pools", Pref::new("%(server)s")),

        // Disable Flash.  The plugin container it is run in is
        // causing problems when quitting Firefox from geckodriver,
        // c.f. https://github.com/mozilla/geckodriver/issues/225.
        ("plugin.state.flash", Pref::new(0)),

        // Local documents have access to all other local docments,
        // including directory listings.
        ("security.fileuri.strict_origin_policy", Pref::new(false)),

        // Tests don't wait for the notification button security delay
        ("security.notification_enable_delay", Pref::new(0)),

        // Ensure blocklist updates don't hit the network
        ("services.settings.server", Pref::new("http://%(server)s/dummy/blocklist/")),

        // Do not automatically fill sign-in forms with known usernames
        // and passwords
        ("signon.autofillForms", Pref::new(false)),

        // Disable password capture, so that tests that include forms
        // are not influenced by the presence of the persistent doorhanger
        // notification
        ("signon.rememberSignons", Pref::new(false)),

        // Disable first run pages
        ("startup.homepage_welcome_url", Pref::new("about:blank")),
        ("startup.homepage_welcome_url.additional", Pref::new("")),

        // Prevent starting into safe mode after application crashes
        ("toolkit.startup.max_resumed_crashes", Pref::new(-1)),

        // We want to collect telemetry, but we don't want to send in the results
        ("toolkit.telemetry.server", Pref::new("https://%(server)s/dummy/telemetry/")),
    ];

    pub static ref REQUIRED: [(&'static str, Pref); 5] = [
        // Do not warn on quitting Firefox
        ("browser.warnOnQuit", Pref::new(false)),

        // Do not warn on exit when multiple tabs are open
        ("browser.tabs.warnOnClose", Pref::new(false)),

        // Do not warn when quitting with multiple tabs
        ("browser.showQuitWarning", Pref::new(false)),

        // Until bug 1238095 is fixed, we have to disable safe CPOW checks
        ("dom.ipc.cpows.forbid-unsafe-from-browser", Pref::new(false)),

        // TODO(ato): Should not be needed, as Marionette is enabled by
        // passing the --marionette flag to the binary
        ("marionette.defaultPrefs.enabled", Pref::new(true)),
    ];
}
<|endoftext|>


<commit_before>use rustc_serialize::json::Json;

use super::Document;

#[derive(Debug)]
pub enum Filter {
    Term(String, Json),
    Prefix(String, String),
    And(Vec<Filter>),
    Or(Vec<Filter>),
    Not(Box<Filter>),
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term(ref field, ref value) => {
                let obj = doc.data.as_object().unwrap();

                if let Some(field_value) = obj.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Prefix(ref field, ref value) => {
                let obj = doc.data.as_object().unwrap();

                if let Some(field_value) = obj.get(field) {
                    if let Json::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::And(ref filters) => {
                for filter in filters.iter() {
                    if !filter.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or(ref filters) => {
                for filter in filters.iter() {
                    if filter.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not(ref filter) => !filter.matches(doc),
        }
    }
}

pub fn parse_filter(json: &Json) -> Filter {
    let filter_json = json.as_object().unwrap();
    let first_key = filter_json.keys().nth(0).unwrap();

    if first_key == "term" {
        let filter_json = filter_json.get("term").unwrap().as_object().unwrap();
        let first_key = filter_json.keys().nth(0).unwrap();

        Filter::Term(first_key.clone(), filter_json.get(first_key).unwrap().clone())
    } else if first_key == "prefix" {
        let filter_json = filter_json.get("prefix").unwrap().as_object().unwrap();
        let first_key = filter_json.keys().nth(0).unwrap();
        let value = filter_json.get(first_key).unwrap().as_string().unwrap();

        Filter::Prefix(first_key.clone(), value.to_owned())
    } else if first_key == "and" {
        Filter::And(filter_json.get("and").unwrap()
                               .as_array().unwrap()
                               .iter().map(|f| parse_filter(f))
                               .collect::<Vec<_>>(),)
    } else if first_key == "or" {
        Filter::Or(filter_json.get("or").unwrap()
                               .as_array().unwrap()
                               .iter().map(|f| parse_filter(f))
                               .collect::<Vec<_>>(),)
    } else if first_key == "not" {
        Filter::Not(Box::new(parse_filter(filter_json.get("not").unwrap())))
    } else {
        Filter::Term("not".to_owned(), Json::String("implemented".to_owned()))
    }
}

#[derive(Debug)]
pub enum Query {
    Match{field: String, query: String},
    MultiMatch{fields: Vec<String>, query: String},
    Filtered{query: Box<Query>, filter: Box<Filter>},
}

pub fn parse_match_query(json: &Json) -> Query {
    let query_json = json.as_object().unwrap();

    let field = match query_json.get("field") {
        Some(val) => val.as_string().unwrap().to_owned(),
        None => "_all".to_owned(),
    };

    Query::Match {
        field: field,
        query: query_json.get("query").unwrap().as_string().unwrap().to_owned(),
    }
}

pub fn parse_multi_match_query(json: &Json) -> Query {
    let query_json = json.as_object().unwrap();

    Query::MultiMatch {
        // Convert "fields" into a Vec<String>
        fields: query_json.get("fields").unwrap()
                          .as_array().unwrap()
                          .iter().map(|s| s.as_string().unwrap().to_owned())
                          .collect::<Vec<_>>(),
        query: query_json.get("query").unwrap().as_string().unwrap().to_owned(),
    }
}

pub fn parse_filtered_query(json: &Json) -> Query {
    let query_json = json.as_object().unwrap();

    Query::Filtered {
        filter: Box::new(parse_filter(query_json.get("filter").unwrap())),
        query: Box::new(parse_query(query_json.get("query").unwrap())),
    }
}

pub fn parse_query(json: &Json) -> Query {
    let query_json = json.as_object().unwrap();
    let first_key = query_json.keys().nth(0).unwrap();

    if first_key == "match" {
        let inner_query = query_json.get("match").unwrap();
        parse_match_query(inner_query)
    } else if first_key == "multi_match" {
        let inner_query = query_json.get("multi_match").unwrap();
        parse_multi_match_query(inner_query)
    } else if first_key == "filtered" {
        let inner_query = query_json.get("filtered").unwrap();
        parse_filtered_query(inner_query)
    } else {
        println!("not implmented query type! {:?}", first_key);

        // TODO
        Query::Match {
            field: "not".to_owned(),
            query: "implemented".to_owned(),
        }
    }
}
<commit_msg>Error management for query parser<commit_after>use rustc_serialize::json::Json;

use super::Document;

#[derive(Debug)]
pub enum QuerySyntaxError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownQueryType(String),
    NoQuery,
    FilteredNoFilter,
    FilteredNoQuery,
    MissingQueryString,
    MultiMatchMissingFields,
}

#[derive(Debug)]
pub enum Filter {
    Term(String, Json),
    Prefix(String, String),
    And(Vec<Filter>),
    Or(Vec<Filter>),
    Not(Box<Filter>),
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term(ref field, ref value) => {
                let obj = doc.data.as_object().unwrap();

                if let Some(field_value) = obj.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Prefix(ref field, ref value) => {
                let obj = doc.data.as_object().unwrap();

                if let Some(field_value) = obj.get(field) {
                    if let Json::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::And(ref filters) => {
                for filter in filters.iter() {
                    if !filter.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or(ref filters) => {
                for filter in filters.iter() {
                    if filter.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not(ref filter) => !filter.matches(doc),
        }
    }
}

pub fn parse_filter(json: &Json) -> Filter {
    let filter_json = json.as_object().unwrap();
    let first_key = filter_json.keys().nth(0).unwrap();

    if first_key == "term" {
        let filter_json = filter_json.get("term").unwrap().as_object().unwrap();
        let first_key = filter_json.keys().nth(0).unwrap();

        Filter::Term(first_key.clone(), filter_json.get(first_key).unwrap().clone())
    } else if first_key == "prefix" {
        let filter_json = filter_json.get("prefix").unwrap().as_object().unwrap();
        let first_key = filter_json.keys().nth(0).unwrap();
        let value = filter_json.get(first_key).unwrap().as_string().unwrap();

        Filter::Prefix(first_key.clone(), value.to_owned())
    } else if first_key == "and" {
        Filter::And(filter_json.get("and").unwrap()
                               .as_array().unwrap()
                               .iter().map(|f| parse_filter(f))
                               .collect::<Vec<_>>(),)
    } else if first_key == "or" {
        Filter::Or(filter_json.get("or").unwrap()
                               .as_array().unwrap()
                               .iter().map(|f| parse_filter(f))
                               .collect::<Vec<_>>(),)
    } else if first_key == "not" {
        Filter::Not(Box::new(parse_filter(filter_json.get("not").unwrap())))
    } else {
        Filter::Term("not".to_owned(), Json::String("implemented".to_owned()))
    }
}

#[derive(Debug)]
pub enum Query {
    Match{field: String, query: String},
    MultiMatch{fields: Vec<String>, query: String},
    Filtered{query: Box<Query>, filter: Box<Filter>},
}

pub fn parse_match_query(json: &Json) -> Result<Query, QuerySyntaxError> {
    let json_object = try!(json.as_object().ok_or(QuerySyntaxError::ExpectedObject));

    let field = match json_object.get("field") {
        Some(val) => try!(val.as_string().ok_or(QuerySyntaxError::ExpectedString)).to_owned(),
        None => "_all".to_owned(),
    };

    let query_json = try!(json_object.get("query").ok_or(QuerySyntaxError::MissingQueryString));
    let query = try!(query_json.as_string().ok_or(QuerySyntaxError::ExpectedString)).to_owned();

    Ok(Query::Match {
        field: field,
        query: query,
    })
}

pub fn parse_multi_match_query(json: &Json) -> Result<Query, QuerySyntaxError> {
    let json_object = try!(json.as_object().ok_or(QuerySyntaxError::ExpectedObject));

    // Convert "fields" into a Vec<String>
    let fields_json = try!(json_object.get("fields").ok_or(QuerySyntaxError::MultiMatchMissingFields));
    let fields = try!(fields_json.as_array().ok_or(QuerySyntaxError::ExpectedArray))
                      .iter().map(|s| s.as_string().unwrap().to_owned())
                      .collect::<Vec<_>>();

    let query_json = try!(json_object.get("query").ok_or(QuerySyntaxError::MissingQueryString));
    let query = try!(query_json.as_string().ok_or(QuerySyntaxError::ExpectedString)).to_owned();

    Ok(Query::MultiMatch {
        fields: fields,
        query: query,
    })
}

pub fn parse_filtered_query(json: &Json) -> Result<Query, QuerySyntaxError> {
    let json_object = try!(json.as_object().ok_or(QuerySyntaxError::ExpectedObject));

    let filter_json = try!(json_object.get("filter").ok_or(QuerySyntaxError::FilteredNoFilter));
    let filter = parse_filter(filter_json);

    let query_json = try!(json_object.get("query").ok_or(QuerySyntaxError::FilteredNoQuery));
    let query = try!(parse_query(query_json));

    Ok(Query::Filtered {
        filter: Box::new(filter),
        query: Box::new(query),
    })
}

pub fn parse_query(json: &Json) -> Result<Query, QuerySyntaxError> {
    let json_object = try!(json.as_object().ok_or(QuerySyntaxError::ExpectedObject));
    let first_key = try!(json_object.keys().nth(0).ok_or(QuerySyntaxError::NoQuery));

    if first_key == "match" {
        let inner_query = json_object.get("match").unwrap();
        Ok(try!(parse_match_query(inner_query)))
    } else if first_key == "multi_match" {
        let inner_query = json_object.get("multi_match").unwrap();
        Ok(try!(parse_multi_match_query(inner_query)))
    } else if first_key == "filtered" {
        let inner_query = json_object.get("filtered").unwrap();
        Ok(try!(parse_filtered_query(inner_query)))
    } else {
        Err(QuerySyntaxError::UnknownQueryType(first_key.clone()))
    }
}
<|endoftext|>