<commit_before>use std::fmt::{Debug, Formatter};
use std::fmt;
use std::ops::Deref;
use std::process::exit;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

use storage::parser::FileHeaderParser;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli;
use module::helpers::cli::CliFileFilter;

mod header;

use self::header::get_url_from_header;
use self::header::get_tags_from_header;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    /**
     * Subcommand: add
     */
    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));

        let url    = matches.value_of("url").map(String::from).unwrap(); // clap ensures this is present

        if !self.validate_url(&url, &parser) {
            error!("URL validation failed, exiting.");
            exit(1);
        } else {
            debug!("Verification succeeded");
        }

        let tags   = matches.value_of("tags").and_then(|s| {
            Some(s.split(",").map(String::from).collect())
        }).unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    url  = '{:?}'", url);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(url, tags);

        let fileid = self.rt.store().new_file_with_header(self, header);
        self.rt.store().load(self, &parser, &fileid).and_then(|file| {
            info!("Created file in memory: {}", fileid);
            Some(self.rt.store().persist(&parser, file))
        }).unwrap_or(false)
    }

    fn validate_url<HP>(&self, url: &String, parser: &Parser<HP>) -> bool
        where HP: FileHeaderParser
    {
        use util::is_url;

        if !is_url(url) {
            error!("Url '{}' is not a valid URL. Will not store.", url);
            return false;
        }

        let is_in_store = self.rt
            .store()
            .load_for_module(self, parser)
            .iter()
            .any(|file| {
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url_in_store| {
                    &url_in_store == url
                }).unwrap_or(false)
            });

        if is_in_store {
            error!("URL '{}' seems to be in the store already", url);
            return false;
        }

        return true;
    }

    /**
     * Subcommand: list
     */
    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use std::ops::Deref;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", Some("URL"));

        let files  = self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file));
        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(files,
            &|file| {
                let fl = file.deref().borrow();
                let hdr = fl.header();
                let url = get_url_from_header(hdr).unwrap_or(String::from("Parser error"));
                let tags = get_tags_from_header(hdr);

                debug!("Custom printer field: url  = '{:?}'", url);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![url, tags.join(", ")]
            }
        );
        true
    }

    /**
     * Subcommand: remove
     */
    fn command_remove(&self, matches: &ArgMatches) -> bool {
        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can remove now: {:?}", file);
                let f = file.deref().borrow();
                self.rt.store().remove(f.id().clone())
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (worked, failed) = result;

        info!("Removing succeeded for {} files", worked);
        info!("Removing failed for {} files", failed);

        return failed == 0;
    }

    /**
     * Subcommand: add_tags
     */
    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            let mut new_tags = old_tags.clone();
            new_tags.append(&mut cli_tags.clone());
            new_tags
        })
    }

    /**
     * Subcommand: rm_tags
     */
    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            old_tags.clone()
                .into_iter()
                .filter(|tag| !cli_tags.contains(tag))
                .collect()
        })
    }

    /**
     * Subcommand: set_tags
     */
    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            cli_tags.clone()
        })
    }

    /**
     * Helper function to alter the tags in a file
     */
    fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
        where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
    {
        use self::header::rebuild_header_with_tags;

        let cli_tags = matches.value_of("tags")
                          .map(|ts| {
                            ts.split(",")
                              .map(String::from)
                              .collect::<Vec<String>>()
                          })
                          .unwrap_or(vec![]);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
                                                                 matches,
                                                                 "with_id",
                                                                 "with_match",
                                                                 "with_tags",
                                                                 None);
        self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("Remove tags from file: {:?}", file);

                let hdr = {
                    let f = file.deref().borrow();
                    f.header().clone()
                };

                debug!("Tags:...");
                let old_tags = get_tags_from_header(&hdr);
                debug!("    old_tags = {:?}", &old_tags);
                debug!("    cli_tags = {:?}", &cli_tags);

                let new_tags = generate_new_tags(old_tags, &cli_tags);
                debug!("    new_tags = {:?}", &new_tags);

                let new_header = rebuild_header_with_tags(&hdr, new_tags)
                    .unwrap_or_else(|| {
                        error!("Could not rebuild header for file");
                        exit(1);
                    });
                {
                    let mut f_mut = file.deref().borrow_mut();
                    f_mut.set_header(new_header);
                }

                self.rt.store().persist(&parser, file);
                true
            })
            .all(|x| x)
    }

}

/**
 * Trait implementation for BM module
 */
impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bookmark"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "BM"));
        Ok(())
    }

}

<commit_msg>Add info output to BM-add command<commit_after>use std::fmt::{Debug, Formatter};
use std::fmt;
use std::ops::Deref;
use std::process::exit;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

use storage::parser::FileHeaderParser;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli;
use module::helpers::cli::CliFileFilter;

mod header;

use self::header::get_url_from_header;
use self::header::get_tags_from_header;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    /**
     * Subcommand: add
     */
    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));

        let url    = matches.value_of("url").map(String::from).unwrap(); // clap ensures this is present

        if !self.validate_url(&url, &parser) {
            error!("URL validation failed, exiting.");
            exit(1);
        } else {
            debug!("Verification succeeded");
        }

        let tags   = matches.value_of("tags").and_then(|s| {
            Some(s.split(",").map(String::from).collect())
        }).unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    url  = '{:?}'", url);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(url, tags);

        let fileid = self.rt
                         .store()
                         .new_file_with_header(self, header);

        let result = self.rt
            .store()
            .load(self, &parser, &fileid)
            .map(|file| {
                info!("Created file in memory: {}", fileid);
                self.rt
                    .store()
                    .persist(&parser, file)
            })
            .unwrap_or(false);

        if result {
            info!("Adding worked");
        } else {
            info!("Adding failed");
        }

        result
    }

    fn validate_url<HP>(&self, url: &String, parser: &Parser<HP>) -> bool
        where HP: FileHeaderParser
    {
        use util::is_url;

        if !is_url(url) {
            error!("Url '{}' is not a valid URL. Will not store.", url);
            return false;
        }

        let is_in_store = self.rt
            .store()
            .load_for_module(self, parser)
            .iter()
            .any(|file| {
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url_in_store| {
                    &url_in_store == url
                }).unwrap_or(false)
            });

        if is_in_store {
            error!("URL '{}' seems to be in the store already", url);
            return false;
        }

        return true;
    }

    /**
     * Subcommand: list
     */
    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use std::ops::Deref;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", Some("URL"));

        let files  = self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file));
        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(files,
            &|file| {
                let fl = file.deref().borrow();
                let hdr = fl.header();
                let url = get_url_from_header(hdr).unwrap_or(String::from("Parser error"));
                let tags = get_tags_from_header(hdr);

                debug!("Custom printer field: url  = '{:?}'", url);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![url, tags.join(", ")]
            }
        );
        true
    }

    /**
     * Subcommand: remove
     */
    fn command_remove(&self, matches: &ArgMatches) -> bool {
        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can remove now: {:?}", file);
                let f = file.deref().borrow();
                self.rt.store().remove(f.id().clone())
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (worked, failed) = result;

        info!("Removing succeeded for {} files", worked);
        info!("Removing failed for {} files", failed);

        return failed == 0;
    }

    /**
     * Subcommand: add_tags
     */
    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            let mut new_tags = old_tags.clone();
            new_tags.append(&mut cli_tags.clone());
            new_tags
        })
    }

    /**
     * Subcommand: rm_tags
     */
    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            old_tags.clone()
                .into_iter()
                .filter(|tag| !cli_tags.contains(tag))
                .collect()
        })
    }

    /**
     * Subcommand: set_tags
     */
    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            cli_tags.clone()
        })
    }

    /**
     * Helper function to alter the tags in a file
     */
    fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
        where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
    {
        use self::header::rebuild_header_with_tags;

        let cli_tags = matches.value_of("tags")
                          .map(|ts| {
                            ts.split(",")
                              .map(String::from)
                              .collect::<Vec<String>>()
                          })
                          .unwrap_or(vec![]);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
                                                                 matches,
                                                                 "with_id",
                                                                 "with_match",
                                                                 "with_tags",
                                                                 None);
        self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("Remove tags from file: {:?}", file);

                let hdr = {
                    let f = file.deref().borrow();
                    f.header().clone()
                };

                debug!("Tags:...");
                let old_tags = get_tags_from_header(&hdr);
                debug!("    old_tags = {:?}", &old_tags);
                debug!("    cli_tags = {:?}", &cli_tags);

                let new_tags = generate_new_tags(old_tags, &cli_tags);
                debug!("    new_tags = {:?}", &new_tags);

                let new_header = rebuild_header_with_tags(&hdr, new_tags)
                    .unwrap_or_else(|| {
                        error!("Could not rebuild header for file");
                        exit(1);
                    });
                {
                    let mut f_mut = file.deref().borrow_mut();
                    f_mut.set_header(new_header);
                }

                self.rt.store().persist(&parser, file);
                true
            })
            .all(|x| x)
    }

}

/**
 * Trait implementation for BM module
 */
impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bookmark"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "BM"));
        Ok(())
    }

}

<|endoftext|>


<commit_before>extern crate fuse;
extern crate libc;

use std::os;
use libc::{ENOENT, ENOSYS};
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};

struct JsonFilesystem;

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        reply.error(ENOSYS);
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(JsonFilesystem, &mountpoint, &[]);
}
<commit_msg>Next step - working getattr.<commit_after>extern crate fuse;
extern crate time;
extern crate libc;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};

struct JsonFilesystem;

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        attr.ino = 1;
        attr.kind = FileType::Directory;
        attr.perm = USER_DIR;
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            reply.attr(&ttl, &attr);
        } else {
            reply.error(ENOSYS);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(JsonFilesystem, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index {}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(): ino {}, fh {}, ofset {}", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Debug info in readdir loop.<commit_after>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index {}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(): ino {}, fh {}, ofset {}", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    println!("\tkey: {}, inode: {} offset: {}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>use std::fmt;

#[derive(Debug)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

pub struct Error(pub (ErrorLevel, &'static str));

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{:?}] {}", (self.0).0, (self.0).1)
    }
}

// Use a hashmap which contains an error level and a string
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}", error).unwrap();
        }

        write!(f, "")
    }
}
<commit_msg>Add a colour level to error levels<commit_after>use std::fmt;
use ansi_term::Colour;

#[derive(Debug)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

pub struct Error(pub (ErrorLevel, &'static str));

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let colour_level = match (self.0).0 {
            ErrorLevel::Info => Colour::Yellow.paint("Info"),

            // ansi_term does not have orange for some reason
            ErrorLevel::Warning => Colour::RGB(255, 210, 0).paint("Warning"),
            ErrorLevel::Error => Colour::Red.paint("Error"),
        };

        write!(f, "[{:?}] {}\n", colour_level, (self.0).1)
    }
}

// Use a hashmap which contains an error level and a string
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}\n", error).unwrap();
        }

        write!(f, "")
    }
}
<|endoftext|>


<commit_before>use walkdir::DirEntry;
use termion::{is_tty, color};
use std::path::Path;
use std::fmt;
use mime_guess;
use std::fs;
use pretty_bytes::converter::convert as pretty_bytes_convert;
use std::time;
use chrono::{Local, TimeZone};
use ColorConfig;
use std::ffi::OsStr;
use std::os::unix::fs::FileTypeExt;
use std::os::linux::fs::MetadataExt;

struct RgbColor {
    red: u8,
    green: u8,
    blue: u8,
}

impl RgbColor {
    fn new(r: u8, g: u8, b: u8) -> RgbColor {
        RgbColor{red: r, green: g, blue: b}
    }

    fn from_str(color_string: &str) -> RgbColor {
        let red = u8::from_str_radix(&color_string[0..2], 16).expect(&format!("Could not parse red value in color {}", color_string));
        let green = u8::from_str_radix(&color_string[2..4], 16).expect(&format!("Could not parse green value in color {}", color_string));
        let blue = u8::from_str_radix(&color_string[4..6], 16).expect(&format!("Could not parse blue value in color {}", color_string));
        RgbColor{red: red, green: green, blue: blue}
    }

    fn as_color(&self) -> color::Rgb {
        color::Rgb(self.red, self.green, self.blue)
    }
}

impl fmt::Display for RgbColor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "rgb({}, {}, {})", self.red, self.green, self.blue)
    }
}

pub fn format_mimetype(t: mime_guess::Mime, machine_readable: bool) -> String {
    format!("{}", t)
}

pub fn format_filetype(ft: fs::FileType, machine_readable: bool) -> String {
    if ft.is_file() {
        String::from("file")
    } else if ft.is_dir() {
        String::from("dir")
    } else if ft.is_symlink() {
        String::from("symlink")
    } else if ft.is_block_device() {
        String::from("block_device")
    } else if ft.is_fifo() {
        String::from("pipe")
    } else if ft.is_socket() {
        String::from("socket")
    } else {
        String::from("unkown")
    }
}

pub fn format_filesize(size: u64, machine_readable: bool) -> String {
    if machine_readable {
        return format!("{}", size)
    } else {
        pretty_bytes_convert(size as f64)
    }
}

pub fn format_systime(t: time::SystemTime, machine_readable: bool) -> String {
    let duration = t.duration_since(time::UNIX_EPOCH).unwrap();
    if machine_readable {
        format!("{}", duration.as_secs())
    } else {
       format!("{}", Local.timestamp(duration.as_secs() as i64, 0).format("%F %T"))
    }
}

pub fn format_path<P: color::Color, C: color::Color>(path: &Path, parent_color: color::Fg<P>, filename_color: color::Fg<C>) -> String {
    match path.parent() {
        Some(parent) => match path.file_name() {
            Some(filename) => format!("{reset}{path_color}{path}/{filename_color}{filename}{reset}",
                                          reset=color::Fg(color::Reset),
                                          path_color=parent_color,
                                          path=parent.to_str().unwrap(),
                                          filename_color=filename_color,
                                          filename=filename.to_str().unwrap()),
            None => format!("{}", parent.to_str().unwrap()),
        },
        None => panic!("This should not happen!")
    }
}

pub fn format_dir<P: color::Color>(path: &Path, dir_color: color::Fg<P>) -> String {
    format!("{reset}{dircolor}{dirname}{reset}",
                                reset=color::Fg(color::Reset),
                                dircolor=dir_color,
                                dirname=path.to_str().unwrap())
}

pub fn format_name(dir_entry: &DirEntry, color_config: &Option<ColorConfig>, color_mode: bool) -> String {
    let path = dir_entry.path();
    let default_format = format!("{}", path.to_str().unwrap());
    if !color_mode {
        return default_format;
    }

    // Default configuration taken from LS_COLORS aka the output of /usr/bin/dircolors
    let file_color = color::Fg(color::Reset);
    let dir_color = color::Fg(color::Blue);
    let symlink_color = color::Fg(color::Cyan);
    let block_device_color = color::Fg(color::LightRed);
    let fifo_color = color::Fg(color::LightRed);
    let socket_color = color::Fg(color::Magenta);

    let filetype = dir_entry.file_type();
    match *color_config {
        Some(ref config) => match config.prefix {
            Some(ref pre) => {
                if filetype.is_file(){
                    match config.file {
                        Some(ref fc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(fc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), file_color),
                    }
                } else if filetype.is_symlink() {
                    match config.symlink {
                        Some(ref sc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), symlink_color),
                    }
                } else if filetype.is_block_device() {
                    match config.device {
                        Some(ref bc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(bc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), block_device_color),
                    }
                } else if filetype.is_fifo() {
                    match config.fifo {
                            Some(ref fc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(fc).as_color())),
                            None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), fifo_color),
                    }
                } else if filetype.is_socket() {
                    match config.socket {
                        Some(ref sc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), socket_color),
                    }
                } else if filetype.is_dir() {
                    match config.dir {
                        Some(ref dc) => format_dir(path, color::Fg(RgbColor::from_str(dc).as_color())),
                        None => format_dir(path, dir_color),
                    }
                } else {
                    format!("{}", path.to_str().unwrap())
                }
            },
            None         => {
                if filetype.is_file(){
                    match config.file {
                        Some(ref fc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(fc).as_color())),
                        None         => format_path(path, dir_color, file_color),
                    }
                } else if filetype.is_symlink() {
                    match config.symlink {
                        Some(ref sc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, dir_color, symlink_color),
                    }
                } else if filetype.is_block_device() {
                    match config.device {
                        Some(ref bc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(bc).as_color())),
                        None         => format_path(path, dir_color, block_device_color),
                    }
                } else if filetype.is_fifo() {
                    match config.fifo {
                            Some(ref fc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(fc).as_color())),
                            None         => format_path(path, dir_color, fifo_color),
                    }
                } else if filetype.is_socket() {
                    match config.socket {
                        Some(ref sc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, dir_color, socket_color),
                    }
                } else if filetype.is_dir() {
                    match config.dir {
                        Some(ref dc) => format_dir(path, color::Fg(RgbColor::from_str(dc).as_color())),
                        None => format_dir(path, dir_color),
                    }
                } else {
                    format!("{}", path.to_str().unwrap())
                }
            },
    },
    None => {
            if filetype.is_file(){
                format_path(path, dir_color, file_color)
            } else if filetype.is_symlink() {
                format_path(path, dir_color, symlink_color)
            } else if filetype.is_block_device() {
                format_path(path, dir_color, block_device_color)
            } else if filetype.is_fifo() {
                format_path(path, dir_color, fifo_color)
            } else if filetype.is_socket() {
                format_path(path, dir_color, socket_color)
            } else if filetype.is_dir() {
                format_dir(path, dir_color)
            } else {
                format!("{}", path.to_str().unwrap())
            }
        },
    }
}
<commit_msg>Fix compiling issue on macOS<commit_after>use walkdir::DirEntry;
use termion::{is_tty, color};
use std::path::Path;
use std::fmt;
use mime_guess;
use std::fs;
use pretty_bytes::converter::convert as pretty_bytes_convert;
use std::time;
use chrono::{Local, TimeZone};
use ColorConfig;
use std::ffi::OsStr;
use std::os::unix::fs::{FileTypeExt, MetadataExt};

struct RgbColor {
    red: u8,
    green: u8,
    blue: u8,
}

impl RgbColor {
    fn new(r: u8, g: u8, b: u8) -> RgbColor {
        RgbColor{red: r, green: g, blue: b}
    }

    fn from_str(color_string: &str) -> RgbColor {
        let red = u8::from_str_radix(&color_string[0..2], 16).expect(&format!("Could not parse red value in color {}", color_string));
        let green = u8::from_str_radix(&color_string[2..4], 16).expect(&format!("Could not parse green value in color {}", color_string));
        let blue = u8::from_str_radix(&color_string[4..6], 16).expect(&format!("Could not parse blue value in color {}", color_string));
        RgbColor{red: red, green: green, blue: blue}
    }

    fn as_color(&self) -> color::Rgb {
        color::Rgb(self.red, self.green, self.blue)
    }
}

impl fmt::Display for RgbColor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "rgb({}, {}, {})", self.red, self.green, self.blue)
    }
}

pub fn format_mimetype(t: mime_guess::Mime, machine_readable: bool) -> String {
    format!("{}", t)
}

pub fn format_filetype(ft: fs::FileType, machine_readable: bool) -> String {
    if ft.is_file() {
        String::from("file")
    } else if ft.is_dir() {
        String::from("dir")
    } else if ft.is_symlink() {
        String::from("symlink")
    } else if ft.is_block_device() {
        String::from("block_device")
    } else if ft.is_fifo() {
        String::from("pipe")
    } else if ft.is_socket() {
        String::from("socket")
    } else {
        String::from("unkown")
    }
}

pub fn format_filesize(size: u64, machine_readable: bool) -> String {
    if machine_readable {
        return format!("{}", size)
    } else {
        pretty_bytes_convert(size as f64)
    }
}

pub fn format_systime(t: time::SystemTime, machine_readable: bool) -> String {
    let duration = t.duration_since(time::UNIX_EPOCH).unwrap();
    if machine_readable {
        format!("{}", duration.as_secs())
    } else {
       format!("{}", Local.timestamp(duration.as_secs() as i64, 0).format("%F %T"))
    }
}

pub fn format_path<P: color::Color, C: color::Color>(path: &Path, parent_color: color::Fg<P>, filename_color: color::Fg<C>) -> String {
    match path.parent() {
        Some(parent) => match path.file_name() {
            Some(filename) => format!("{reset}{path_color}{path}/{filename_color}{filename}{reset}",
                                          reset=color::Fg(color::Reset),
                                          path_color=parent_color,
                                          path=parent.to_str().unwrap(),
                                          filename_color=filename_color,
                                          filename=filename.to_str().unwrap()),
            None => format!("{}", parent.to_str().unwrap()),
        },
        None => panic!("This should not happen!")
    }
}

pub fn format_dir<P: color::Color>(path: &Path, dir_color: color::Fg<P>) -> String {
    format!("{reset}{dircolor}{dirname}{reset}",
                                reset=color::Fg(color::Reset),
                                dircolor=dir_color,
                                dirname=path.to_str().unwrap())
}

pub fn format_name(dir_entry: &DirEntry, color_config: &Option<ColorConfig>, color_mode: bool) -> String {
    let path = dir_entry.path();
    let default_format = format!("{}", path.to_str().unwrap());
    if !color_mode {
        return default_format;
    }

    // Default configuration taken from LS_COLORS aka the output of /usr/bin/dircolors
    let file_color = color::Fg(color::Reset);
    let dir_color = color::Fg(color::Blue);
    let symlink_color = color::Fg(color::Cyan);
    let block_device_color = color::Fg(color::LightRed);
    let fifo_color = color::Fg(color::LightRed);
    let socket_color = color::Fg(color::Magenta);

    let filetype = dir_entry.file_type();
    match *color_config {
        Some(ref config) => match config.prefix {
            Some(ref pre) => {
                if filetype.is_file(){
                    match config.file {
                        Some(ref fc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(fc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), file_color),
                    }
                } else if filetype.is_symlink() {
                    match config.symlink {
                        Some(ref sc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), symlink_color),
                    }
                } else if filetype.is_block_device() {
                    match config.device {
                        Some(ref bc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(bc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), block_device_color),
                    }
                } else if filetype.is_fifo() {
                    match config.fifo {
                            Some(ref fc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(fc).as_color())),
                            None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), fifo_color),
                    }
                } else if filetype.is_socket() {
                    match config.socket {
                        Some(ref sc) => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, color::Fg(RgbColor::from_str(pre).as_color()), socket_color),
                    }
                } else if filetype.is_dir() {
                    match config.dir {
                        Some(ref dc) => format_dir(path, color::Fg(RgbColor::from_str(dc).as_color())),
                        None => format_dir(path, dir_color),
                    }
                } else {
                    format!("{}", path.to_str().unwrap())
                }
            },
            None         => {
                if filetype.is_file(){
                    match config.file {
                        Some(ref fc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(fc).as_color())),
                        None         => format_path(path, dir_color, file_color),
                    }
                } else if filetype.is_symlink() {
                    match config.symlink {
                        Some(ref sc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, dir_color, symlink_color),
                    }
                } else if filetype.is_block_device() {
                    match config.device {
                        Some(ref bc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(bc).as_color())),
                        None         => format_path(path, dir_color, block_device_color),
                    }
                } else if filetype.is_fifo() {
                    match config.fifo {
                            Some(ref fc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(fc).as_color())),
                            None         => format_path(path, dir_color, fifo_color),
                    }
                } else if filetype.is_socket() {
                    match config.socket {
                        Some(ref sc) => format_path(path, dir_color, color::Fg(RgbColor::from_str(sc).as_color())),
                        None         => format_path(path, dir_color, socket_color),
                    }
                } else if filetype.is_dir() {
                    match config.dir {
                        Some(ref dc) => format_dir(path, color::Fg(RgbColor::from_str(dc).as_color())),
                        None => format_dir(path, dir_color),
                    }
                } else {
                    format!("{}", path.to_str().unwrap())
                }
            },
    },
    None => {
            if filetype.is_file(){
                format_path(path, dir_color, file_color)
            } else if filetype.is_symlink() {
                format_path(path, dir_color, symlink_color)
            } else if filetype.is_block_device() {
                format_path(path, dir_color, block_device_color)
            } else if filetype.is_fifo() {
                format_path(path, dir_color, fifo_color)
            } else if filetype.is_socket() {
                format_path(path, dir_color, socket_color)
            } else if filetype.is_dir() {
                format_dir(path, dir_color)
            } else {
                format!("{}", path.to_str().unwrap())
            }
        },
    }
}
<|endoftext|>


<commit_before>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point, &mut Vec<Point>),
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      intersect(y, p1, p2, points);

      p1 = p2;
   }
}

#[cfg(test)]
mod tests {
   use test::Bencher;

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);
   }
}
<commit_msg>Adding exact points to the clip test<commit_after>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point, &mut Vec<Point>),
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      intersect(y, p1, p2, points);

      p1 = p2;
   }
}

#[cfg(test)]
mod tests {
   use test::Bencher;

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);

      let (_, result) = points.split_at(end);
      assert_eq!(result, [
         Point::new(140, 95),
         Point::new(110, 80),
         Point::new(70, 80),
         Point::new(70, 110),
         Point::new(83, 150),
         Point::new(125, 150),
         Point::new(140, 120),
      ]);
   }
}
<|endoftext|>


<commit_before>pub struct Person {
    greeting: &'static str,
}

impl Person {
    pub fn new() -> Person {
        Person { greeting: "Hello!"}
    }

    pub fn greet(&self) -> &'static str {
        self.greeting
    }
}
<commit_msg>Fix bracket spacing<commit_after>pub struct Person {
    greeting: &'static str,
}

impl Person {
    pub fn new() -> Person {
        Person { greeting: "Hello!" }
    }

    pub fn greet(&self) -> &'static str {
        self.greeting
    }
}
<|endoftext|>


<commit_before>use crate::context;
use crate::context::memory::Grant;
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::VirtualAddress;
use crate::paging::entry::EntryFlags;
use crate::syscall::data::{Map, Map2, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::scheme::Scheme;

pub struct MemoryScheme;

impl MemoryScheme {
    pub fn new() -> Self {
        MemoryScheme
    }
}
impl Scheme for MemoryScheme {
    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
        let used = used_frames() as u64;
        let free = free_frames() as u64;

        stat.f_bsize = PAGE_SIZE as u32;
        stat.f_blocks = used + free;
        stat.f_bfree = free;
        stat.f_bavail = stat.f_bfree;

        Ok(0)
    }

    fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
        //TODO: Abstract with other grant creation
        if map.size == 0 {
            Ok(0)
        } else {
            let contexts = context::contexts();
            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
            let context = context_lock.read();

            let fixed = map.flags.contains(MapFlags::MAP_FIXED);
            let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);

            let mut grants = context.grants.lock();

            let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

            let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
                if // map.address < crate::USER_GRANT_OFFSET || map.address + map.size > crate::USER_GRANT_OFFSET + crate::PML4_SIZE ||
                    map.address % PAGE_SIZE != 0
                {
                    return Err(Error::new(EINVAL));
                }
                map.address
            };

            let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
            if !map.flags.contains(PROT_EXEC) {
                entry_flags |= EntryFlags::NO_EXECUTE;
            }
            if map.flags.contains(PROT_READ) {
                //TODO: PROT_READ
            }
            if map.flags.contains(PROT_WRITE) {
                entry_flags |= EntryFlags::WRITABLE;
            }

            let mut i = 0;

            while i < grants.len()  {
                let grant = &mut grants[i];

                let mut grant_start = grant.start_address().get();
                let mut grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
                let mut grant_end = grant_start + grant_len;

                if grant_end <= to_address {
                    // grant has nothing to do with the memory to map, and thus we can safely just
                    // go on to the next one.

                    if !fixed {
                        to_address = grant_end;
                    }
                    i += 1;

                    continue;
                }

                // check whether this grant overlaps with the memory range to use, by checking that
                // the start and end of the grant is not within the memory range to map
                if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
                    // the range overlaps, thus we'll have to continue to the next grant, or to
                    // insert a new grant at the end (if not MapFlags::MAP_FIXED).

                    if fixed_noreplace {
                        return Err(Error::new(EEXIST));
                    } else if fixed {
                        /*
                        // shrink the grant, removing it if necessary. since the to_address isn't
                        // changed at all when mapping to a fixed address, we can just continue to
                        // the next grant and shrink or remove that one if it was also overlapping.
                        if to_address + full_size > grant_start {
                            let new_start = core::cmp::min(grant_end, to_address + full_size);

                            let new_size = grant.size() - (new_start - grant_start);
                            unsafe { grant.set_size(new_size) };
                            grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

                            let new_start = VirtualAddress::new(new_start);
                            unsafe { grant.set_start_address(new_start) };
                            grant_start = new_start;

                            grant_end = grant_start + grant_len;
                        }
                        */
                        // TODO
                        return Err(Error::new(EOPNOTSUPP));
                    } else {
                        to_address = grant_end;
                        i += 1;
                    }
                    continue;
                }

                i += 1;
            }

            grants.insert(i, Grant::map(
                VirtualAddress::new(to_address),
                full_size,
                entry_flags,
            ));

            Ok(to_address)
        }
    }
    fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
        if map.flags.contains(MapFlags::MAP_FIXED) {
            // not supported for fmap, which lacks the address argument.
            return Err(Error::new(EINVAL));
        }
        self.fmap2(id, &Map2 {
            offset: map.offset,
            size: map.size,
            flags: map.flags,
            address: 0,
        })
    }

    fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
        Ok(0)
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"memory:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn close(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }
}
<commit_msg>Fix mmap when using out-of-place address<commit_after>use crate::context;
use crate::context::memory::Grant;
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
use crate::paging::VirtualAddress;
use crate::paging::entry::EntryFlags;
use crate::syscall::data::{Map, Map2, StatVfs};
use crate::syscall::error::*;
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
use crate::syscall::scheme::Scheme;

pub struct MemoryScheme;

impl MemoryScheme {
    pub fn new() -> Self {
        MemoryScheme
    }
}
impl Scheme for MemoryScheme {
    fn open(&self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        Ok(0)
    }

    fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
        let used = used_frames() as u64;
        let free = free_frames() as u64;

        stat.f_bsize = PAGE_SIZE as u32;
        stat.f_blocks = used + free;
        stat.f_bfree = free;
        stat.f_bavail = stat.f_bfree;

        Ok(0)
    }

    fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
        //TODO: Abstract with other grant creation
        if map.size == 0 {
            Ok(0)
        } else {
            let contexts = context::contexts();
            let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
            let context = context_lock.read();

            let fixed = map.flags.contains(MapFlags::MAP_FIXED);
            let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);

            let mut grants = context.grants.lock();

            let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

            let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
                if // map.address < crate::USER_GRANT_OFFSET || map.address + map.size > crate::USER_GRANT_OFFSET + crate::PML4_SIZE ||
                    map.address % PAGE_SIZE != 0
                {
                    return Err(Error::new(EINVAL));
                }
                map.address
            };

            let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
            if !map.flags.contains(PROT_EXEC) {
                entry_flags |= EntryFlags::NO_EXECUTE;
            }
            if map.flags.contains(PROT_READ) {
                //TODO: PROT_READ
            }
            if map.flags.contains(PROT_WRITE) {
                entry_flags |= EntryFlags::WRITABLE;
            }

            let mut i = 0;

            while i < grants.len()  {
                let grant = &mut grants[i];

                let grant_start = grant.start_address().get();
                let grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
                let grant_end = grant_start + grant_len;

                if to_address < grant_start || grant_end <= to_address {
                    // grant has nothing to do with the memory to map, and thus we can safely just
                    // go on to the next one.

                    if grant_start >= crate::USER_GRANT_OFFSET && !fixed {
                        // don't ignore addresses outside of the automatic grant offset
                        to_address = grant_end;
                    }
                    i += 1;

                    continue;
                }

                // check whether this grant overlaps with the memory range to use, by checking that
                // the start and end of the grant is not within the memory range to map
                if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
                    // the range overlaps, thus we'll have to continue to the next grant, or to
                    // insert a new grant at the end (if not MapFlags::MAP_FIXED).

                    if fixed_noreplace {
                        println!("grant: conflicts with: {:#x} - {:#x}", grant_start, grant_end);
                        return Err(Error::new(EEXIST));
                    } else if fixed {
                        /*
                        // shrink the grant, removing it if necessary. since the to_address isn't
                        // changed at all when mapping to a fixed address, we can just continue to
                        // the next grant and shrink or remove that one if it was also overlapping.
                        if to_address + full_size > grant_start {
                            let new_start = core::cmp::min(grant_end, to_address + full_size);

                            let new_size = grant.size() - (new_start - grant_start);
                            unsafe { grant.set_size(new_size) };
                            grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

                            let new_start = VirtualAddress::new(new_start);
                            unsafe { grant.set_start_address(new_start) };
                            grant_start = new_start;

                            grant_end = grant_start + grant_len;
                        }
                        */
                        // TODO
                        return Err(Error::new(EOPNOTSUPP));
                    } else {
                        to_address = grant_end;
                        i += 1;
                    }
                    continue;
                }

                i += 1;
            }

            grants.insert(i, Grant::map(
                VirtualAddress::new(to_address),
                full_size,
                entry_flags,
            ));

            Ok(to_address)
        }
    }
    fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
        if map.flags.contains(MapFlags::MAP_FIXED) {
            // not supported for fmap, which lacks the address argument.
            return Err(Error::new(EINVAL));
        }
        self.fmap2(id, &Map2 {
            offset: map.offset,
            size: map.size,
            flags: map.flags,
            address: 0,
        })
    }

    fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
        Ok(0)
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"memory:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn close(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use std::collections::HashMap;
use consts::{WatchedEventType, ZkError, ZkState};
use paths::make_path;
use proto::WatchedEvent;
use zookeeper::{ZkResult, ZooKeeper};
use zookeeper_ext::ZooKeeperExt;
use listeners::{ListenerSet, Subscription};

pub type ChildData = Arc<Vec<u8>>;
pub type Data = HashMap<String, ChildData>;

#[derive(Debug,Clone)]
pub enum PathChildrenCacheEvent {
    Initialized(Data),
    ConnectionSuspended,
    ConnectionLost,
    ConnectionReconnected,
    ChildRemoved(String),
    ChildAdded(String, ChildData),
    ChildUpdated(String, ChildData),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum RefreshMode {
    Standard,
    ForceGetDataAndStat,
}

#[derive(Debug)]
enum Operation {
    Initialize,
    Shutdown,
    Refresh(RefreshMode),
    Event(PathChildrenCacheEvent),
    GetData(String /* path */),
}

pub struct PathChildrenCache {
    path: Arc<String>,
    zk: Arc<ZooKeeper>,
    data: Arc<Mutex<Data>>,
    worker_thread: Option<thread::JoinHandle<()>>,
    channel: Option<Sender<Operation>>,
    listener_subscription: Option<Subscription>,
    event_listeners: ListenerSet<PathChildrenCacheEvent>,
}

impl PathChildrenCache {

    fn get_children(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan: Sender<Operation>, mode: RefreshMode) -> ZkResult<()> {
        let ops_chan1 = ops_chan.clone();

        let watcher = move |event: &WatchedEvent| {
            match event.event_type {
                WatchedEventType::NodeChildrenChanged => {
                    let _path = event.path.as_ref().expect("Path absent");

                    // Subscribe to new changes recursively
                    if let Err(err) = ops_chan1.send(Operation::Refresh(RefreshMode::Standard)) {
                        warn!("error sending Refresh operation to ops channel: {:?}", err);
                    }
                },
                _ => error!("Unexpected: {:?}", event)
            };
        };

        let children = try!(zk.get_children_w(&path, watcher));

        let mut data_locked = data.lock().unwrap();

        for child in &children {
            let child_path = make_path(path, child);

            if mode == RefreshMode::ForceGetDataAndStat || !data_locked.contains_key(&child_path) {

                let child_data = try!(Self::get_data(zk.clone(), &child_path, data.clone(), ops_chan.clone()));

                let child_data = Arc::new(child_data);

                data_locked.insert(child_path.clone(), child_data.clone());

                try!(ops_chan.send(Operation::Event(PathChildrenCacheEvent::ChildAdded(child_path, child_data))).map_err(|err| {
                    info!("error sending ChildAdded event: {:?}", err);
                    ZkError::APIError
                }));
            }
        }

        trace!("New data: {:?}", *data_locked);

        Ok(())
    }

    fn get_data(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan: Sender<Operation>) -> ZkResult<Vec<u8>> {
        let path1 = path.to_owned();

        let data_watcher = move |event: &WatchedEvent| {
            let mut data_locked = data.lock().unwrap();
            match event.event_type {
                WatchedEventType::NodeDeleted => {
                    data_locked.remove(&path1);

                    if let Err(err) = ops_chan.send(Operation::Event(PathChildrenCacheEvent::ChildRemoved(path1.clone()))) {
                        warn!("error sending ChildRemoved event: {:?}", err);
                    }
                },
                WatchedEventType::NodeDataChanged => {
                    // Subscribe to new changes recursively
                    if let Err(err) = ops_chan.send(Operation::GetData(path1)) {
                        warn!("error sending GetData to op channel: {:?}", err);
                    }
                },
                _ => error!("Unexpected: {:?}", event)
            };

            trace!("New data: {:?}", *data_locked);
        };

        zk.get_data_w(path, data_watcher).map(|stuff| { stuff.0 })
    }

    fn update_data(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan_tx: Sender<Operation>) -> ZkResult<()> {
        let mut data_locked = data.lock().unwrap();

        let path = path.to_owned();

        let result = Self::get_data(zk.clone(), &path, data.clone(), ops_chan_tx.clone());

        match result {
            Ok(child_data) => {
                trace!("got data {:?}", child_data);

                let child_data = Arc::new(child_data);

                data_locked.insert(path.clone(), child_data.clone());

                ops_chan_tx.send(Operation::Event(PathChildrenCacheEvent::ChildUpdated(path, child_data))).map_err(|err| {
                    warn!("error sending ChildUpdated event: {:?}", err);
                    ZkError::APIError
                })
            },
            Err(err) => {
                warn!("error getting child data: {:?}", err);
                Err(ZkError::APIError)
            }
        }
    }

    pub fn new(zk: Arc<ZooKeeper>, path: &str) -> ZkResult<PathChildrenCache> {

        let data = Arc::new(Mutex::new(HashMap::new()));

        try!(zk.ensure_path(path));

        Ok(PathChildrenCache{
            path: Arc::new(path.to_owned()),
            zk: zk,
            data: data,
            worker_thread: None,
            channel: None,
            listener_subscription: None,
            event_listeners: ListenerSet::new(),
        })
    }

    pub fn get_current_data(&self) -> Data {
        self.data.lock().unwrap().clone()
    }

    pub fn clear(&self) {
        self.data.lock().unwrap().clear()
    }

    fn handle_state_change(state: ZkState, ops_chan_tx: Sender<Operation>) -> bool {
        let mut done = false;

        debug!("zk state change {:?}", state);
        if let ZkState::Connected = state {
            if let Err(err) = ops_chan_tx.send(Operation::Refresh(RefreshMode::ForceGetDataAndStat)) {
                warn!("error sending Refresh to op channel: {:?}", err);
                done = true;
            }
        }

        done
    }

    fn handle_operation(op: Operation, zk: Arc<ZooKeeper>, path: Arc<String>, data: Arc<Mutex<Data>>, event_listeners: ListenerSet<PathChildrenCacheEvent>, ops_chan_tx: Sender<Operation>) -> bool {
        let mut done = false;

        match op {
            Operation::Initialize => {
                debug!("initialising...");
                let result = Self::get_children(zk.clone(), &*path, data.clone(), ops_chan_tx.clone(), RefreshMode::ForceGetDataAndStat);
                debug!("got children {:?}", result);

                match data.lock() {
                    Ok(data) => {
                        event_listeners.notify(&PathChildrenCacheEvent::Initialized(data.clone()));
                    },
                    Err(err) => {
                        error!("error locking data hashmap: {:?}", err);
                        done = true;
                    }
                }
            },
            Operation::Shutdown => {
                debug!("shutting down worker thread");
                done = true;
            },
            Operation::Refresh(mode) => {
                debug!("getting children");
                let result = Self::get_children(zk.clone(), &*path, data.clone(), ops_chan_tx.clone(), mode);
                debug!("got children {:?}", result);
            },
            Operation::GetData(path) => {
                debug!("getting data");
                let result = Self::update_data(zk.clone(), &*path, data.clone(), ops_chan_tx.clone());
                if let Err(err) = result {
                    warn!("error getting child data: {:?}", err);
                }
            },
            Operation::Event(event) => {
                debug!("received event {:?}", event);
                event_listeners.notify(&event);
            },
        }

        done
    }

    pub fn start(&mut self) -> ZkResult<()> {
        let (ops_chan_tx, ops_chan_rx) = mpsc::channel();
        let (listener_chan_tx, listener_chan_rx) = mpsc::channel();

        let sub = self.zk.add_listener(listener_chan_tx.clone());
        self.listener_subscription = Some(sub);

        let zk = self.zk.clone();
        let path = self.path.clone();
        let data = self.data.clone();
        let event_listeners = self.event_listeners.clone();
        self.channel = Some(ops_chan_tx.clone());

        self.worker_thread = Some(thread::spawn(move || {
            let mut done = false;

            while !done {
                select! (
                    state_result = listener_chan_rx.recv() => {
                        match state_result {
                            Ok(state) => {
                                done = Self::handle_state_change(state, ops_chan_tx.clone());
                            },
                            Err(err) => {
                                info!("zk state chan err. shutting down. {:?}", err);
                                done = true;
                            }
                        }
                    },
                    op_result = ops_chan_rx.recv() => {
                        match op_result {
                            Ok(operation) => {
                                done = Self::handle_operation(operation, zk.clone(), path.clone(), data.clone(), event_listeners.clone(), ops_chan_tx.clone());
                            },
                            Err(err) => {
                                info!("error receiving from operations channel ({:?}). shutting down worker thread", err);
                                done = true;
                            }
                        }
                    });
            }
        }));

        self.offer_operation(Operation::Initialize)
    }

    pub fn add_listener(&self, subscriber: Sender<PathChildrenCacheEvent>) -> Subscription {
        self.event_listeners.subscribe(subscriber)
    }

    pub fn remove_listener(&self, sub: Subscription) -> () {
        self.event_listeners.unsubscribe(sub)
    }

    fn offer_operation(&self, op: Operation) -> ZkResult<()> {
        match self.channel {
            Some(ref chan) => {
                chan.send(op).map_err(|err| {
                    warn!("error submitting op to channel: {:?}", err);
                    ZkError::APIError
                })
            },
            None => Err(ZkError::APIError)
        }
    }

}
<commit_msg>Remove unnecessary clone() call<commit_after>use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use std::collections::HashMap;
use consts::{WatchedEventType, ZkError, ZkState};
use paths::make_path;
use proto::WatchedEvent;
use zookeeper::{ZkResult, ZooKeeper};
use zookeeper_ext::ZooKeeperExt;
use listeners::{ListenerSet, Subscription};

pub type ChildData = Arc<Vec<u8>>;
pub type Data = HashMap<String, ChildData>;

#[derive(Debug,Clone)]
pub enum PathChildrenCacheEvent {
    Initialized(Data),
    ConnectionSuspended,
    ConnectionLost,
    ConnectionReconnected,
    ChildRemoved(String),
    ChildAdded(String, ChildData),
    ChildUpdated(String, ChildData),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum RefreshMode {
    Standard,
    ForceGetDataAndStat,
}

#[derive(Debug)]
enum Operation {
    Initialize,
    Shutdown,
    Refresh(RefreshMode),
    Event(PathChildrenCacheEvent),
    GetData(String /* path */),
}

pub struct PathChildrenCache {
    path: Arc<String>,
    zk: Arc<ZooKeeper>,
    data: Arc<Mutex<Data>>,
    worker_thread: Option<thread::JoinHandle<()>>,
    channel: Option<Sender<Operation>>,
    listener_subscription: Option<Subscription>,
    event_listeners: ListenerSet<PathChildrenCacheEvent>,
}

impl PathChildrenCache {

    fn get_children(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan: Sender<Operation>, mode: RefreshMode) -> ZkResult<()> {
        let ops_chan1 = ops_chan.clone();

        let watcher = move |event: &WatchedEvent| {
            match event.event_type {
                WatchedEventType::NodeChildrenChanged => {
                    let _path = event.path.as_ref().expect("Path absent");

                    // Subscribe to new changes recursively
                    if let Err(err) = ops_chan1.send(Operation::Refresh(RefreshMode::Standard)) {
                        warn!("error sending Refresh operation to ops channel: {:?}", err);
                    }
                },
                _ => error!("Unexpected: {:?}", event)
            };
        };

        let children = try!(zk.get_children_w(&path, watcher));

        let mut data_locked = data.lock().unwrap();

        for child in &children {
            let child_path = make_path(path, child);

            if mode == RefreshMode::ForceGetDataAndStat || !data_locked.contains_key(&child_path) {

                let child_data = try!(Self::get_data(zk.clone(), &child_path, data.clone(), ops_chan.clone()));

                let child_data = Arc::new(child_data);

                data_locked.insert(child_path.clone(), child_data.clone());

                try!(ops_chan.send(Operation::Event(PathChildrenCacheEvent::ChildAdded(child_path, child_data))).map_err(|err| {
                    info!("error sending ChildAdded event: {:?}", err);
                    ZkError::APIError
                }));
            }
        }

        trace!("New data: {:?}", *data_locked);

        Ok(())
    }

    fn get_data(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan: Sender<Operation>) -> ZkResult<Vec<u8>> {
        let path1 = path.to_owned();

        let data_watcher = move |event: &WatchedEvent| {
            let mut data_locked = data.lock().unwrap();
            match event.event_type {
                WatchedEventType::NodeDeleted => {
                    data_locked.remove(&path1);

                    if let Err(err) = ops_chan.send(Operation::Event(PathChildrenCacheEvent::ChildRemoved(path1.clone()))) {
                        warn!("error sending ChildRemoved event: {:?}", err);
                    }
                },
                WatchedEventType::NodeDataChanged => {
                    // Subscribe to new changes recursively
                    if let Err(err) = ops_chan.send(Operation::GetData(path1)) {
                        warn!("error sending GetData to op channel: {:?}", err);
                    }
                },
                _ => error!("Unexpected: {:?}", event)
            };

            trace!("New data: {:?}", *data_locked);
        };

        zk.get_data_w(path, data_watcher).map(|stuff| { stuff.0 })
    }

    fn update_data(zk: Arc<ZooKeeper>, path: &str, data: Arc<Mutex<Data>>, ops_chan_tx: Sender<Operation>) -> ZkResult<()> {
        let mut data_locked = data.lock().unwrap();

        let path = path.to_owned();

        let result = Self::get_data(zk.clone(), &path, data.clone(), ops_chan_tx.clone());

        match result {
            Ok(child_data) => {
                trace!("got data {:?}", child_data);

                let child_data = Arc::new(child_data);

                data_locked.insert(path.clone(), child_data.clone());

                ops_chan_tx.send(Operation::Event(PathChildrenCacheEvent::ChildUpdated(path, child_data))).map_err(|err| {
                    warn!("error sending ChildUpdated event: {:?}", err);
                    ZkError::APIError
                })
            },
            Err(err) => {
                warn!("error getting child data: {:?}", err);
                Err(ZkError::APIError)
            }
        }
    }

    pub fn new(zk: Arc<ZooKeeper>, path: &str) -> ZkResult<PathChildrenCache> {

        let data = Arc::new(Mutex::new(HashMap::new()));

        try!(zk.ensure_path(path));

        Ok(PathChildrenCache{
            path: Arc::new(path.to_owned()),
            zk: zk,
            data: data,
            worker_thread: None,
            channel: None,
            listener_subscription: None,
            event_listeners: ListenerSet::new(),
        })
    }

    pub fn get_current_data(&self) -> Data {
        self.data.lock().unwrap().clone()
    }

    pub fn clear(&self) {
        self.data.lock().unwrap().clear()
    }

    fn handle_state_change(state: ZkState, ops_chan_tx: Sender<Operation>) -> bool {
        let mut done = false;

        debug!("zk state change {:?}", state);
        if let ZkState::Connected = state {
            if let Err(err) = ops_chan_tx.send(Operation::Refresh(RefreshMode::ForceGetDataAndStat)) {
                warn!("error sending Refresh to op channel: {:?}", err);
                done = true;
            }
        }

        done
    }

    fn handle_operation(op: Operation, zk: Arc<ZooKeeper>, path: Arc<String>, data: Arc<Mutex<Data>>, event_listeners: ListenerSet<PathChildrenCacheEvent>, ops_chan_tx: Sender<Operation>) -> bool {
        let mut done = false;

        match op {
            Operation::Initialize => {
                debug!("initialising...");
                let result = Self::get_children(zk.clone(), &*path, data.clone(), ops_chan_tx.clone(), RefreshMode::ForceGetDataAndStat);
                debug!("got children {:?}", result);

                match data.lock() {
                    Ok(data) => {
                        event_listeners.notify(&PathChildrenCacheEvent::Initialized(data.clone()));
                    },
                    Err(err) => {
                        error!("error locking data hashmap: {:?}", err);
                        done = true;
                    }
                }
            },
            Operation::Shutdown => {
                debug!("shutting down worker thread");
                done = true;
            },
            Operation::Refresh(mode) => {
                debug!("getting children");
                let result = Self::get_children(zk.clone(), &*path, data.clone(), ops_chan_tx.clone(), mode);
                debug!("got children {:?}", result);
            },
            Operation::GetData(path) => {
                debug!("getting data");
                let result = Self::update_data(zk.clone(), &*path, data.clone(), ops_chan_tx.clone());
                if let Err(err) = result {
                    warn!("error getting child data: {:?}", err);
                }
            },
            Operation::Event(event) => {
                debug!("received event {:?}", event);
                event_listeners.notify(&event);
            },
        }

        done
    }

    pub fn start(&mut self) -> ZkResult<()> {
        let (ops_chan_tx, ops_chan_rx) = mpsc::channel();
        let (listener_chan_tx, listener_chan_rx) = mpsc::channel();

        let sub = self.zk.add_listener(listener_chan_tx);
        self.listener_subscription = Some(sub);

        let zk = self.zk.clone();
        let path = self.path.clone();
        let data = self.data.clone();
        let event_listeners = self.event_listeners.clone();
        self.channel = Some(ops_chan_tx.clone());

        self.worker_thread = Some(thread::spawn(move || {
            let mut done = false;

            while !done {
                select! (
                    state_result = listener_chan_rx.recv() => {
                        match state_result {
                            Ok(state) => {
                                done = Self::handle_state_change(state, ops_chan_tx.clone());
                            },
                            Err(err) => {
                                info!("zk state chan err. shutting down. {:?}", err);
                                done = true;
                            }
                        }
                    },
                    op_result = ops_chan_rx.recv() => {
                        match op_result {
                            Ok(operation) => {
                                done = Self::handle_operation(operation, zk.clone(), path.clone(), data.clone(), event_listeners.clone(), ops_chan_tx.clone());
                            },
                            Err(err) => {
                                info!("error receiving from operations channel ({:?}). shutting down worker thread", err);
                                done = true;
                            }
                        }
                    });
            }
        }));

        self.offer_operation(Operation::Initialize)
    }

    pub fn add_listener(&self, subscriber: Sender<PathChildrenCacheEvent>) -> Subscription {
        self.event_listeners.subscribe(subscriber)
    }

    pub fn remove_listener(&self, sub: Subscription) -> () {
        self.event_listeners.unsubscribe(sub)
    }

    fn offer_operation(&self, op: Operation) -> ZkResult<()> {
        match self.channel {
            Some(ref chan) => {
                chan.send(op).map_err(|err| {
                    warn!("error submitting op to channel: {:?}", err);
                    ZkError::APIError
                })
            },
            None => Err(ZkError::APIError)
        }
    }

}
<|endoftext|>