<commit_before>#![feature(io, path, core, rustdoc, collections)]

extern crate docopt;
extern crate rustdoc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate handlebars;

use docopt::Docopt;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{ FileType, File, USER_DIR, USER_FILE };
use rustdoc::html::markdown::Markdown;
use handlebars::Handlebars;
use rustc_serialize::json::{ ToJson, Json };
use std::collections::BTreeMap;

// Docopt usage string
static USAGE: &'static str = "
Usage: rust-notes <source> <dest>
";

#[derive(Show, RustcDecodable)]
struct Args {
    arg_source: String,
    arg_dest: String
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    //Generator(args).start();
    match Generator::new(args) {
        Ok(generator) => {
            generator.begin();
        },
        Err(message) => panic!(message)
    }
}

struct Link {
    name: String,
    url: String
}

impl ToJson for Link {
    fn to_json(&self) -> Json {
        let mut m = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("url".to_string(), self.url.to_json());
        Json::Object(m)
    }
}

struct DirModel {
    name: String,
    parents: Vec<Link>,
    dirs: Vec<Link>,
    notes: Vec<Link>
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        let mut m = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("parents".to_string(), self.parents.to_json());
        m.insert("dirs".to_string(), self.dirs.to_json());
        m.insert("notes".to_string(), self.notes.to_json());
        Json::Object(m)
    }
}

/*
#[derive(Show, Decodable)]
struct NoteModel {
    name: String,
    parents: Vec<Link>,
    content: String,
}
*/

struct Generator {
    root_source_path: Path,
    root_dest_path: Path,
    notes_source_path: Path,
    handlebars: Handlebars,
    dir_template_name: &'static str,
    note_template_name: &'static str
}

impl Generator {

    pub fn new(args: Args) -> Result<Generator, &'static str> {
        let source_path = Path::new(args.arg_source.as_slice());
        let dest_path = Path::new(args.arg_dest.as_slice());

        if !source_path.is_dir() {
            return Err("Invalid source path");
        }

        if !dest_path.is_dir() {
            match fs::mkdir_recursive(&dest_path, USER_DIR) {
                Err(_) => return Err("Cannot create destination directory"),
                _ => ()
            }
        }

        // Validate source
        let notes_source_path = source_path.clone().join("notes");
        if !notes_source_path.is_dir() {
            return Err("Source directory missing required files");
        }

        let dir_hbs_path = source_path.clone().join("dir.hbs");
        if !dir_hbs_path.exists() {
            return Err("Source directory missing dir.hbs");
        }

        // Good to go! Let's return something good

        let dir_template_name = "dir_template";
        let note_template_name = "note_template";

        let dir_hbs_contents = File::open(&dir_hbs_path).read_to_string().unwrap();
        //println!("dir_hbs_contents: {}", dir_hbs_contents);
        let mut handlebars = Handlebars::new();
        match handlebars.register_template_string(dir_template_name, dir_hbs_contents.to_string()) {
            Err(why) => panic!("Error registering template: {:?}", why),
            _ => ()
        }

        Ok(Generator{
            root_source_path: source_path,
            root_dest_path: dest_path,
            notes_source_path: notes_source_path,
            handlebars: handlebars,
            dir_template_name: dir_template_name,
            note_template_name: note_template_name
        })
    }

    pub fn begin(&self) {
        self.clean_dest();
        self.generate_dir(&Path::new(""));
    }

    fn generate_dir(&self, relative_path: &Path) {
        let full_dest_path = self.root_dest_path.clone()
            .join(relative_path.as_str().unwrap());
        let full_source_path = self.notes_source_path.clone()
            .join(relative_path.as_str().unwrap());

        self.create_dir_index(&full_dest_path, relative_path);

        match fs::readdir(&full_source_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() {
                        self.convert_file(item, &full_dest_path);
                    } else {
                        self.convert_dir(item, &full_dest_path, relative_path);
                    }
                }
            },
            Err(_) => ()
        }
    }

    fn convert_file(&self, source_file_path: &Path, dest_parent_dir_path: &Path) {
        if Generator::is_markdown_file(source_file_path) {
            let source_contents = File::open(source_file_path).read_to_string().unwrap();
            let new_file_path = dest_parent_dir_path.join(format!("{}.html", source_file_path.filestem_str().unwrap()));
            //println!("Creating file '{:?}'", new_file_path);
            let mut file = File::create(&new_file_path).ok().expect("Could not create html file");
            fs::chmod(&new_file_path, USER_FILE).ok().expect("Couldn't chmod new file");
            let markdown_content = Markdown(source_contents.as_slice());
            file.write_str(format!("{}", markdown_content).as_slice())
                .ok().expect("Could not write html to file");
        }
    }

    fn convert_dir(&self, source_dir_path: &Path, dest_parent_dir_path: &Path, relative_path: &Path) {
        let mut contains_note: bool = false;
        let mut dirs = fs::walk_dir(source_dir_path)
            .ok().expect("Could not walk through directories recursively");
        for item2 in dirs {
            contains_note |= Generator::is_markdown_file(&item2);
        }

        if contains_note {
            let dirname = source_dir_path.filename_str().unwrap();
            let new_dir_path = dest_parent_dir_path.clone().join(dirname);
            //println!("creating directory: {:?}", new_dir_path);
            fs::mkdir(&new_dir_path, USER_DIR).ok().expect("Cannot create destination subdir");
            self.generate_dir(&relative_path.clone().join(dirname));
        }
    }

    fn is_markdown_file(file_path: &Path) -> bool {
        let name = file_path.as_str().unwrap();
        let stat = fs::stat(file_path).ok().expect("Could not get stat for file");
        if stat.kind != FileType::RegularFile {
            return false;
        }
        if !name.ends_with(".md") && !name.ends_with(".markdown") {
            return false;
        }
        true
    }

    fn create_dir_index(&self, dir_path: &Path, relative_path: &Path) {
        let new_file_path = dir_path.join("index.html");
        let dirname = dir_path.filename_str().unwrap();
        let mut file = File::create(&new_file_path).ok().expect("Could not create html file");
        fs::chmod(&new_file_path, USER_FILE).ok().expect("Couldn't chmod new file");

        //  Parents
        let mut parents: Vec<Link> = Vec::new();

        let mut temp = relative_path.clone();
        temp.pop();
        while temp.filename_str().is_some() {
            parents.insert(0, Link {
                name: String::from_str(temp.filename_str().unwrap()),
                url: format!("/{}", temp.as_str().unwrap())
            });
            temp.pop();
        }

        // Name
        let name = if relative_path.as_str().unwrap() == "." {
            "Notes"
        } else {
            parents.insert(0, Link {
                name: String::from_str("Home"),
                url: String::from_str("/")
            });
            dirname
        };

        let mut dirs: Vec<Link> = Vec::new();
        let mut notes: Vec<Link> = Vec::new();

        let source_path = self.notes_source_path.clone().join(relative_path);
        match fs::readdir(&source_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() && Generator::is_markdown_file(item) {
                        let name = item.filestem_str().unwrap();
                        let url = relative_path.clone().join(format!("{}.html", name).as_slice());
                        notes.push(Link{
                            name: String::from_str(name),
                            url: String::from_str(format!("/{}", url.as_str().unwrap()).as_slice())
                        });
                    } else {
                        let name = item.filename_str().unwrap();
                        let url = relative_path.clone().join(name);
                        dirs.push(Link{
                            name: String::from_str(name),
                            url: String::from_str(format!("/{}", url.as_str().unwrap()).as_slice())
                        });
                    }
                }
            },
            Err(_) => ()
        }

        let model = DirModel {
            name: String::from_str(name),
            parents: parents,
            dirs: dirs,
            notes: notes
        };

        match self.handlebars.render(self.dir_template_name, &model) {
            Ok(rendered) => {
                file.write_str(rendered.as_slice())
                    .ok().expect("Could not write html to file");
            },
            Err(why) => panic!("Error rendering: {:?}", why)
        }
    }

    fn clean_dest(&self) {
        match fs::readdir(&self.root_dest_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() {
                        //println!("removing file: {:?}", item);
                        fs::unlink(item).ok().expect("Could not remove file");
                    } else {
                        //println!("removing directory: {:?}", item);
                        fs::rmdir_recursive(item).ok().expect("Could not remove directory");
                    }
                }
            },
            Err(_) => ()
        }
    }
}
<commit_msg>added note.hbs<commit_after>#![feature(io, path, core, rustdoc, collections)]

extern crate docopt;
extern crate rustdoc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate handlebars;

use docopt::Docopt;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{ FileType, File, USER_DIR, USER_FILE };
use rustdoc::html::markdown::Markdown;
use handlebars::Handlebars;
use rustc_serialize::json::{ ToJson, Json };
use std::collections::BTreeMap;

// Docopt usage string
static USAGE: &'static str = "
Usage: rust-notes <source> <dest>
";

#[derive(Show, RustcDecodable)]
struct Args {
    arg_source: String,
    arg_dest: String
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    //Generator(args).start();
    match Generator::new(args) {
        Ok(generator) => {
            generator.begin();
        },
        Err(message) => panic!(message)
    }
}

struct Link {
    name: String,
    url: String
}

impl ToJson for Link {
    fn to_json(&self) -> Json {
        let mut m = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("url".to_string(), self.url.to_json());
        Json::Object(m)
    }
}

struct DirModel {
    name: String,
    parents: Vec<Link>,
    dirs: Vec<Link>,
    notes: Vec<Link>
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        let mut m = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("parents".to_string(), self.parents.to_json());
        m.insert("dirs".to_string(), self.dirs.to_json());
        m.insert("notes".to_string(), self.notes.to_json());
        Json::Object(m)
    }
}

struct NoteModel {
    name: String,
    parents: Vec<Link>,
    content: String,
}

impl ToJson for NoteModel {
    fn to_json(&self) -> Json {
        let mut m = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("parents".to_string(), self.parents.to_json());
        m.insert("content".to_string(), self.content.to_json());
        Json::Object(m)
    }
}

struct Generator {
    root_source_path: Path,
    root_dest_path: Path,
    notes_source_path: Path,
    handlebars: Handlebars,
    dir_template_name: &'static str,
    note_template_name: &'static str
}

impl Generator {

    pub fn new(args: Args) -> Result<Generator, &'static str> {
        let source_path = Path::new(args.arg_source.as_slice());
        let dest_path = Path::new(args.arg_dest.as_slice());

        if !source_path.is_dir() {
            return Err("Invalid source path");
        }

        if !dest_path.is_dir() {
            match fs::mkdir_recursive(&dest_path, USER_DIR) {
                Err(_) => return Err("Cannot create destination directory"),
                _ => ()
            }
        }

        // Validate source
        let notes_source_path = source_path.clone().join("notes");
        if !notes_source_path.is_dir() {
            return Err("Source directory missing required files");
        }

        let dir_hbs_path = source_path.clone().join("dir.hbs");
        if !dir_hbs_path.exists() {
            return Err("Source directory missing dir.hbs");
        }

        let note_hbs_path = source_path.clone().join("note.hbs");
        if !note_hbs_path.exists() {
            return Err("Source directory missing note.hbs");
        }

        // Good to go! Let's return something good

        let dir_template_name = "dir_template";
        let note_template_name = "note_template";

        let dir_hbs_contents = File::open(&dir_hbs_path).read_to_string().unwrap();
        let note_hbs_contents = File::open(&note_hbs_path).read_to_string().unwrap();

        let mut handlebars = Handlebars::new();
        handlebars.register_template_string(dir_template_name, dir_hbs_contents.to_string())
            .ok().expect("Error registering dir template");
        handlebars.register_template_string(note_template_name, note_hbs_contents.to_string())
            .ok().expect("Error registering note template");

        Ok(Generator{
            root_source_path: source_path,
            root_dest_path: dest_path,
            notes_source_path: notes_source_path,
            handlebars: handlebars,
            dir_template_name: dir_template_name,
            note_template_name: note_template_name
        })
    }

    pub fn begin(&self) {
        self.clean_dest();
        self.generate_dir(&Path::new(""));
    }

    fn generate_dir(&self, relative_path: &Path) {
        let full_dest_path = self.root_dest_path.clone()
            .join(relative_path.as_str().unwrap());
        let full_source_path = self.notes_source_path.clone()
            .join(relative_path.as_str().unwrap());

        self.create_dir_index(&full_dest_path, relative_path);

        match fs::readdir(&full_source_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() {
                        self.convert_file(item, &full_dest_path, relative_path);
                    } else {
                        self.convert_dir(item, &full_dest_path, relative_path);
                    }
                }
            },
            Err(_) => ()
        }
    }

    fn convert_file(&self, source_file_path: &Path, dest_parent_dir_path: &Path, relative_path: &Path) {
        if Generator::is_markdown_file(source_file_path) {
            let source_contents = File::open(source_file_path).read_to_string().unwrap();
            let file_name = source_file_path.filestem_str().unwrap();

            // Create Model
            let content = Markdown(source_contents.as_slice());
            let parents = Generator::get_parent_links(relative_path, true);

            let model = NoteModel {
                name: String::from_str(file_name),
                parents : parents,
                content : format!("{}", content)
            };

            println!("content: {}", content);

            match self.handlebars.render(self.note_template_name, &model) {
                Ok(rendered) => {
                    // Create File
                    let new_rendered = String::from_str(rendered.as_slice()).replace("\\n", "");
                    let new_file_path = dest_parent_dir_path.join(format!("{}.html", file_name));
                    let mut file = File::create(&new_file_path).ok().expect("Could not note html file");
                    fs::chmod(&new_file_path, USER_FILE).ok().expect("Couldn't chmod new file");
                    file.write_str(new_rendered.as_slice())
                        .ok().expect("Could not write html to file");
                },
                Err(why) => panic!("Error rendering note: {:?}", why)
            }
        }
    }

    fn dir_contains_note(path: &Path) -> bool {
        let mut contains_note: bool = false;
        let mut dirs = fs::walk_dir(path)
            .ok().expect("Could not walk through directories recursively");
        for item2 in dirs {
            contains_note |= Generator::is_markdown_file(&item2);
        }
        contains_note
    }

    fn convert_dir(&self, source_dir_path: &Path, dest_parent_dir_path: &Path, relative_path: &Path) {
        if Generator::dir_contains_note(source_dir_path) {
            let dirname = source_dir_path.filename_str().unwrap();
            let new_dir_path = dest_parent_dir_path.clone().join(dirname);
            //println!("creating directory: {:?}", new_dir_path);
            fs::mkdir(&new_dir_path, USER_DIR).ok().expect("Cannot create destination subdir");
            self.generate_dir(&relative_path.clone().join(dirname));
        }
    }

    fn is_markdown_file(file_path: &Path) -> bool {
        let name = file_path.as_str().unwrap();
        let stat = fs::stat(file_path).ok().expect("Could not get stat for file");
        if stat.kind != FileType::RegularFile {
            return false;
        }
        if !name.ends_with(".md") && !name.ends_with(".markdown") {
            return false;
        }
        true
    }

    fn get_parent_links(path: &Path, is_note: bool) -> Vec<Link> {
        if !is_note && path.filename().is_none() {
            Vec::new()
        } else {
            let mut result: Vec<Link> = vec![Link {
                name: String::from_str("root"),
                url: String::from_str("/")
            }];
            let mut temp = path.clone().dir_path();
            while temp.filename().is_some() {
                result.insert(1, Link {
                    name: String::from_str(temp.filename_str().unwrap()),
                    url: format!("/{}", temp.as_str().unwrap())
                });
                temp.pop();
            }
            result
        }
    }

    fn create_dir_index(&self, dir_path: &Path, relative_path: &Path) {
        let new_file_path = dir_path.join("index.html");
        let dirname = dir_path.filename_str().unwrap();
        let mut file = File::create(&new_file_path).ok().expect("Could not create html file");
        fs::chmod(&new_file_path, USER_FILE).ok().expect("Couldn't chmod new file");

        // Name
        let name = if relative_path.as_str().unwrap() == "." {
            "root"
        } else {
            dirname
        };

        let mut dirs: Vec<Link> = Vec::new();
        let mut notes: Vec<Link> = Vec::new();

        let source_path = self.notes_source_path.clone().join(relative_path);
        match fs::readdir(&source_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() && Generator::is_markdown_file(item) {
                        let name = item.filestem_str().unwrap();
                        let url = relative_path.clone().join(format!("{}.html", name).as_slice());
                        notes.push(Link{
                            name: String::from_str(name),
                            url: String::from_str(format!("/{}", url.as_str().unwrap()).as_slice())
                        });
                    } else if Generator::dir_contains_note(item) {
                        let name = item.filename_str().unwrap();
                        let url = relative_path.clone().join(name);
                        dirs.push(Link{
                            name: String::from_str(name),
                            url: String::from_str(format!("/{}", url.as_str().unwrap()).as_slice())
                        });
                    }
                }
            },
            Err(_) => ()
        }

        let model = DirModel {
            name: String::from_str(name),
            parents: Generator::get_parent_links(relative_path, false),
            dirs: dirs,
            notes: notes
        };

        match self.handlebars.render(self.dir_template_name, &model) {
            Ok(rendered) => {
                file.write_str(rendered.as_slice())
                    .ok().expect("Could not write html to file");
            },
            Err(why) => panic!("Error rendering: {:?}", why)
        }
    }

    fn clean_dest(&self) {
        match fs::readdir(&self.root_dest_path) {
            Ok(items) => {
                for item in items.iter() {
                    if item.is_file() {
                        //println!("removing file: {:?}", item);
                        fs::unlink(item).ok().expect("Could not remove file");
                    } else {
                        //println!("removing directory: {:?}", item);
                        fs::rmdir_recursive(item).ok().expect("Could not remove directory");
                    }
                }
            },
            Err(_) => ()
        }
    }
}
<|endoftext|>


<commit_before>use jsonrpc;
use jsonrpc::{OkResponse, ErrResponse};

use std::io::{Read, Write};
use std::sync::Mutex;
use std::sync::mpsc::Sender;

use hyper::server::{Handler, Request, Response};
use rustc_serialize::{json, Decodable};
use rustc_serialize::json::Json;

use std::collections::HashMap;

use rvi::{Message, RVIHandler, Service};

use message::{BackendServices, PackageId, LocalServices, Notification};
use handler::{NotifyParams, StartParams, ChunkParams, FinishParams};
use handler::{ReportParams, AbortParams, HandleMessageParams};
use persistence::Transfer;

pub struct ServiceHandler {
    rvi_url: String,
    sender: Mutex<Sender<Notification>>,
    services: Mutex<BackendServices>,
    transfers: Mutex<HashMap<PackageId, Transfer>>,
    storage_dir: String,
    vin: String
}

impl ServiceHandler {
    pub fn new(sender: Sender<Notification>,
               url: String, dir: String) -> ServiceHandler {
        let services = BackendServices {
            start: String::new(),
            cancel: String::new(),
            ack: String::new(),
            report: String::new()
        };

        ServiceHandler {
            rvi_url: url,
            sender: Mutex::new(sender),
            services: Mutex::new(services),
            transfers: Mutex::new(HashMap::new()),
            vin: String::new(),
            storage_dir: dir
        }
    }

    fn push_notify(&self, m: Notification) {
        try_or!(self.sender.lock().unwrap().send(m), return);
    }

    fn handle_message_params<D>(&self, message: &str)
        -> Option<Result<OkResponse<i32>, ErrResponse>>
        where D: Decodable + HandleMessageParams {
        json::decode::<jsonrpc::Request<Message<D>>>(&message).map(|p| {
            let handler = &p.params.parameters[0];
            let result = handler.handle(&self.services,
                                        &self.transfers,
                                        &self.rvi_url,
                                        &self.vin,
                                        &self.storage_dir);
            if result {
                handler.get_message().map(|m| { self.push_notify(m); });
                Ok(OkResponse::new(p.id, None))
            } else {
                // TODO: don't just return true/false, but the actual error,
                // so we can send apropriate responses back.
                Err(ErrResponse::invalid_request(p.id))
            }
        }).ok()
    }

    fn handle_message(&self, message: &str)
        -> Result<OkResponse<i32>, ErrResponse> {
        // TODO: refactor
        macro_rules! handle_params {
            ($handler:ident, $message:ident, $service:ident, $id:ident,
             $( $x:ty, $i:expr), *) => {{
                $(
                    if $i == $service {
                        match $handler.handle_message_params::<$x>($message) {
                            Some(r) => return r,
                            None => return Err(ErrResponse::invalid_params($id))
                        }
                    }
                )*
            }}
        }

        macro_rules! try_or_parse_error {
            ($run:expr) => {
                match $run {
                    Some(val) => val,
                    None =>  return Err(ErrResponse::parse_error())
                }
            }
        }

        macro_rules! try_or_invalid {
            ($run:expr, $id:ident) => {
                match $run {
                    Some(val) => val,
                    None => return Err(ErrResponse::invalid_request($id))
                }
            }
        }

        let data = try!(Json::from_str(message).map_err(|_| ErrResponse::parse_error()));
        let obj = try_or_parse_error!(data.as_object());
        let rpc_id_data = try_or_parse_error!(obj.get("id"));
        let rpc_id = try_or_parse_error!(rpc_id_data.as_u64());

        let method_data = try_or_invalid!(obj.get("method"), rpc_id);
        let method = try_or_invalid!(method_data.as_string(), rpc_id);

        if method == "services_available" {
            Ok(OkResponse::new(rpc_id, None))
        }
        else if method != "message" {
            Err(ErrResponse::method_not_found(rpc_id))
        } else {
            let params_data = try_or_invalid!(obj.get("params"), rpc_id);
            let params = try_or_invalid!(params_data.as_object(), rpc_id);
            let service_data = try_or_invalid!(params.get("service_name"), rpc_id);
            let service = try_or_invalid!(service_data.as_string(), rpc_id);

            handle_params!(self, message, service, rpc_id,
                           NotifyParams, "/sota/notify",
                           StartParams,  "/sota/start",
                           ChunkParams,  "/sota/chunk",
                           FinishParams, "/sota/finish",
                           ReportParams, "/sota/report",
                           AbortParams,  "/sota/abort");

            Err(ErrResponse::invalid_request(rpc_id))
        }
    }
}

impl Handler for ServiceHandler {
    fn handle(&self, mut req: Request, resp: Response) {
        let mut rbody = String::new();
        try_or!(req.read_to_string(&mut rbody), return);
        debug!(">>> Received Message: {}", rbody);
        let mut resp = try_or!(resp.start(), return);

        macro_rules! send_response {
            ($rtype:ty, $resp:ident) => {
                match json::encode::<$rtype>(&$resp) {
                    Ok(decoded_msg) => {
                        try_or!(resp.write_all(decoded_msg.as_bytes()), return);
                        debug!("<<< Sent Response: {}", decoded_msg);
                    },
                    Err(p) => { error!("{}", p); }
                }
            };
        }

        match self.handle_message(&rbody) {
            Ok(msg) => { send_response!(OkResponse<i32>, msg) },
            Err(msg) => { send_response!(ErrResponse, msg) }
        }

        try_or!(resp.end(), return);
    }
}

impl RVIHandler for ServiceHandler {
    fn register(&mut self, services: Vec<Service>) {
        self.vin = LocalServices::new(&services).get_vin();
    }
}
<commit_msg>Improve error handling in service handler<commit_after>use jsonrpc;
use jsonrpc::{OkResponse, ErrResponse};

use std::io::{Read, Write};
use std::sync::Mutex;
use std::sync::mpsc::Sender;

use hyper::server::{Handler, Request, Response};
use rustc_serialize::{json, Decodable};
use rustc_serialize::json::Json;

use std::collections::HashMap;

use rvi::{Message, RVIHandler, Service};

use message::{BackendServices, PackageId, LocalServices, Notification};
use handler::{NotifyParams, StartParams, ChunkParams, FinishParams};
use handler::{ReportParams, AbortParams, HandleMessageParams};
use persistence::Transfer;

pub struct ServiceHandler {
    rvi_url: String,
    sender: Mutex<Sender<Notification>>,
    services: Mutex<BackendServices>,
    transfers: Mutex<HashMap<PackageId, Transfer>>,
    storage_dir: String,
    vin: String
}

impl ServiceHandler {
    pub fn new(sender: Sender<Notification>,
               url: String, dir: String) -> ServiceHandler {
        let services = BackendServices {
            start: String::new(),
            cancel: String::new(),
            ack: String::new(),
            report: String::new()
        };

        ServiceHandler {
            rvi_url: url,
            sender: Mutex::new(sender),
            services: Mutex::new(services),
            transfers: Mutex::new(HashMap::new()),
            vin: String::new(),
            storage_dir: dir
        }
    }

    fn push_notify(&self, m: Notification) {
        try_or!(self.sender.lock().unwrap().send(m), return);
    }

    fn handle_message_params<D>(&self, message: &str)
        -> Option<Result<OkResponse<i32>, ErrResponse>>
        where D: Decodable + HandleMessageParams {
        json::decode::<jsonrpc::Request<Message<D>>>(&message).map(|p| {
            let handler = &p.params.parameters[0];
            let result = handler.handle(&self.services,
                                        &self.transfers,
                                        &self.rvi_url,
                                        &self.vin,
                                        &self.storage_dir);
            if result {
                handler.get_message().map(|m| { self.push_notify(m); });
                Ok(OkResponse::new(p.id, None))
            } else {
                // TODO: don't just return true/false, but the actual error,
                // so we can send apropriate responses back.
                Err(ErrResponse::invalid_request(p.id))
            }
        }).ok()
    }

    fn handle_message(&self, message: &str)
        -> Result<OkResponse<i32>, ErrResponse> {
        macro_rules! handle_params {
            ($handler:ident, $message:ident, $service:ident, $id:ident,
             $( $x:ty, $i:expr), *) => {{
                $(
                    if $i == $service {
                        match $handler.handle_message_params::<$x>($message) {
                            Some(r) => return r,
                            None => return Err(ErrResponse::invalid_params($id))
                        }
                    }
                )*
            }}
        }

        let data = try!(Json::from_str(message)
                        .map_err(|_| ErrResponse::parse_error()));
        let obj = try!(data.as_object().ok_or(ErrResponse::parse_error()));
        let rpc_id = try!(obj.get("id").and_then(|x| x.as_u64())
                          .ok_or(ErrResponse::parse_error()));

        let method = try!(obj.get("method").and_then(|x| x.as_string())
                          .ok_or(ErrResponse::invalid_request(rpc_id)));

        if method == "services_available" {
            Ok(OkResponse::new(rpc_id, None))
        }
        else if method != "message" {
            Err(ErrResponse::method_not_found(rpc_id))
        } else {
            let service = try!(obj.get("params")
                               .and_then(|x| x.as_object())
                               .and_then(|x| x.get("service_name"))
                               .and_then(|x| x.as_string())
                               .ok_or(ErrResponse::invalid_request(rpc_id)));

            handle_params!(self, message, service, rpc_id,
                           NotifyParams, "/sota/notify",
                           StartParams,  "/sota/start",
                           ChunkParams,  "/sota/chunk",
                           FinishParams, "/sota/finish",
                           ReportParams, "/sota/report",
                           AbortParams,  "/sota/abort");

            Err(ErrResponse::invalid_request(rpc_id))
        }
    }
}

impl Handler for ServiceHandler {
    fn handle(&self, mut req: Request, resp: Response) {
        let mut rbody = String::new();
        try_or!(req.read_to_string(&mut rbody), return);
        debug!(">>> Received Message: {}", rbody);
        let mut resp = try_or!(resp.start(), return);

        macro_rules! send_response {
            ($rtype:ty, $resp:ident) => {
                match json::encode::<$rtype>(&$resp) {
                    Ok(decoded_msg) => {
                        try_or!(resp.write_all(decoded_msg.as_bytes()), return);
                        debug!("<<< Sent Response: {}", decoded_msg);
                    },
                    Err(p) => { error!("{}", p); }
                }
            };
        }

        match self.handle_message(&rbody) {
            Ok(msg) => { send_response!(OkResponse<i32>, msg) },
            Err(msg) => { send_response!(ErrResponse, msg) }
        }

        try_or!(resp.end(), return);
    }
}

impl RVIHandler for ServiceHandler {
    fn register(&mut self, services: Vec<Service>) {
        self.vin = LocalServices::new(&services).get_vin();
    }
}
<|endoftext|>


<commit_before>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

impl Request {
    fn get_matching_response<'a>(&'a self, responses: &'a Vec<Response>) -> Option<&'a Response> {
        for response in responses {
            if self.id == response.id {
                return Some(&response)
            }
        }

        None
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_get_matching_response() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = requests[0].get_matching_response(&responses);

        assert_eq!(*result.unwrap(), responses[0]);
    }

    #[test]
    fn test_get_matching_response_none_found() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let request_without_matching = Request {
            id: 999,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = request_without_matching.get_matching_response(&responses);

        assert!(result.is_none());
    }
}
<commit_msg>Use new Response to avoid reference<commit_after>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

impl Request {
    fn get_matching_response<'a>(&'a self, responses: &'a Vec<Response>) -> Option<&'a Response> {
        for response in responses {
            if self.id == response.id {
                return Some(&response)
            }
        }

        None
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_get_matching_response() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = requests[0].get_matching_response(&responses);

        let expected = Response {
            id: 1,
            time: strptime("08/Apr/2016:09:57:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(7),
            http_status: HttpStatus::OK,
        };

        assert_eq!(*result.unwrap(), expected);
    }

    #[test]
    fn test_get_matching_response_none_found() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let request_without_matching = Request {
            id: 999,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = request_without_matching.get_matching_response(&responses);

        assert!(result.is_none());
    }
}
<|endoftext|>


<commit_before>/*!
Treesum calculates check|sums| for files in directory |tree|s. The successful
results are written to stdout while errors and status messages are written to
stderr. Example output:

```
01010101010101010101	root/some_subdir/cat.gif
12121212121212121212	root/I Like Space In My Paths/HerpDerp Final 3 (17).xlsx
```
*/
#![deny(warnings)]

extern crate crypto;
extern crate sgiter;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io;
use std::path::Path;
use walkdir::DirEntry;
use walkdir::WalkDir;

/// Calculate a checksum for a file.
fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn print_success(t: (DirEntry, String)) {
    println!("{}\t{}", t.1, t.0.path().display());
}

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

/// Helper for handling error reporting levels and printing.
/// TODO: Use stderr. Find crate?
fn print_err(e: Error, verbose: bool) {
    match e {
        Error::Ignored(ent) => {
            if verbose {
                println!("ignored: {}", ent.path().display())
            }
        }
        _ => println!("ERROR: {:?}", e),
    }
}

type ResultIter = sgiter::GatherIter<Result<(DirEntry, String), Error>>;

/// Allocates memory for and collects all successfull hashes before sorting and
/// then printing. Errors are printed immidiately.
fn do_sorted_output(res:ResultIter, verbose:bool) {
    let mut tuples = vec![];
    for r in res {
        match r {
            Ok(t)  => tuples.push(t),
            Err(e) => print_err(e, verbose),
        }
    }
    tuples.sort_by(|a, b| a.1.cmp(&b.1));
    for t in tuples {
        print_success(t);
    }
}

/// Prints both errors and successful hashes immidiately without sorting.
fn do_unsorted_output(res:ResultIter, verbose:bool) {
    for r in res {
        match r {
            Ok(t)  => print_success(t),
            Err(e) => print_err(e, verbose),
        }
    }
}

/// Bind constructors for producer and consumers, start scatter gather and
/// pass on result iterator to handler for either sorted or unsorted output.
fn process_root(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let producer_ctor = || WalkDir::new(pb);
    let xform_ctor = || {
        let mut hasher = Sha1::new();
        let mut buf = [0; 1024 * 8];
        let f = move |e: walkdir::Result<DirEntry>| {
            let e = e.map_err(Error::WalkDir)?;
            if !e.file_type().is_file() {
                return Err(Error::Ignored(e));
            }
            calc_hash(e.path(), &mut hasher, &mut buf)
                .map_err(Error::Io)
                .map(|s| (e, s))
        };
        f
    };
    let results = sgiter::scatter_gather(producer_ctor, xform_ctor);
    let verbose = false;       // TODO: add command line flag
    let sort_successes = true; // TODO: add command line flag
    if sort_successes {
        do_sorted_output(results, verbose)
    } else {
        do_unsorted_output(results, verbose)
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    process_root(root).unwrap()
}

<commit_msg>Spelling.<commit_after>/*!
Treesum calculates check|sums| for files in directory |tree|s. The successful
results are written to stdout while errors and status messages are written to
stderr. Example output:

```
01010101010101010101	root/some_subdir/cat.gif
12121212121212121212	root/I Like Space In My Paths/HerpDerp Final 3 (17).xlsx
```
*/
#![deny(warnings)]

extern crate crypto;
extern crate sgiter;
extern crate walkdir;

use self::crypto::digest::Digest;
use self::crypto::sha1::Sha1;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io;
use std::path::Path;
use walkdir::DirEntry;
use walkdir::WalkDir;

/// Calculate a checksum for a file.
fn calc_hash(p: &Path, hasher: &mut Sha1, buf: &mut [u8]) -> io::Result<String> {
    hasher.reset();
    let mut f = File::open(p)?;
    loop {
        let num_read = f.read(buf)?;
        if num_read == 0 {
            break;
        }
        hasher.input(&buf[..num_read]);
    }
    Ok(hasher.result_str())
}

fn print_success(t: (DirEntry, String)) {
    println!("{}\t{}", t.1, t.0.path().display());
}

#[derive(Debug)]
enum Error {
    Io(io::Error),
    Ignored(DirEntry),
    WalkDir(walkdir::Error),
}

/// Helper for handling error reporting levels and printing.
/// TODO: Use stderr. Find crate?
fn print_err(e: Error, verbose: bool) {
    match e {
        Error::Ignored(ent) => {
            if verbose {
                println!("ignored: {}", ent.path().display())
            }
        }
        _ => println!("ERROR: {:?}", e),
    }
}

type ResultIter = sgiter::GatherIter<Result<(DirEntry, String), Error>>;

/// Allocates memory for and collects all successfull hashes before sorting and
/// then printing. Errors are printed immediately.
fn do_sorted_output(res:ResultIter, verbose:bool) {
    let mut tuples = vec![];
    for r in res {
        match r {
            Ok(t)  => tuples.push(t),
            Err(e) => print_err(e, verbose),
        }
    }
    tuples.sort_by(|a, b| a.1.cmp(&b.1));
    for t in tuples {
        print_success(t);
    }
}

/// Prints both errors and successful hashes immediately without sorting.
fn do_unsorted_output(res:ResultIter, verbose:bool) {
    for r in res {
        match r {
            Ok(t)  => print_success(t),
            Err(e) => print_err(e, verbose),
        }
    }
}

/// Bind constructors for producer and consumers, start scatter gather and
/// pass on result iterator to handler for either sorted or unsorted output.
fn process_root(root: &Path) -> io::Result<()> {
    let pb = root.to_path_buf();
    let producer_ctor = || WalkDir::new(pb);
    let xform_ctor = || {
        let mut hasher = Sha1::new();
        let mut buf = [0; 1024 * 8];
        let f = move |e: walkdir::Result<DirEntry>| {
            let e = e.map_err(Error::WalkDir)?;
            if !e.file_type().is_file() {
                return Err(Error::Ignored(e));
            }
            calc_hash(e.path(), &mut hasher, &mut buf)
                .map_err(Error::Io)
                .map(|s| (e, s))
        };
        f
    };
    let results = sgiter::scatter_gather(producer_ctor, xform_ctor);
    let verbose = false;       // TODO: add command line flag
    let sort_successes = true; // TODO: add command line flag
    if sort_successes {
        do_sorted_output(results, verbose)
    } else {
        do_unsorted_output(results, verbose)
    }
    Ok(())
}

fn main() {
    let root = env::args().nth(1).unwrap_or(".".to_string());
    let root = Path::new(root.as_str());
    process_root(root).unwrap()
}

<|endoftext|>


<commit_before>#![feature(globs, macro_rules, phase)]

extern crate debug;
extern crate piston;
extern crate sdl2;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate image;
extern crate libc;
extern crate cgmath;
extern crate time;

extern crate flate;
extern crate native;
extern crate rustrt;
extern crate serialize;

use sdl2_game_window::GameWindowSDL2 as Window;
use piston::input;
use piston::cam;
use piston::vecmath::{vec3_add, vec3_scale, vec3_normalized};
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindow,
    GameWindowSettings,
    Input,
    Render,
    Update
};

use array::*;

use std::cmp::max;
use std::f32::consts::PI;
use std::io::fs::File;

pub mod array;
pub mod chunk;
pub mod cube;
pub mod fps_counter;
pub mod shader;
pub mod texture;

pub mod minecraft {
    pub use self::data_1_8_pre2 as data;

    mod data_1_8_pre2;
    pub mod biome;
    pub mod block_state;
    pub mod model;
    pub mod nbt;
    pub mod region;
}

fn main() {
    let world = Path::new(std::os::args().as_slice().get(1).expect("Usage: ./hematite <path/to/world>").as_slice());

    let level = minecraft::nbt::Nbt::from_gzip(File::open(&world.join("level.dat")).read_to_end().unwrap().as_slice()).unwrap();
    println!("{}", level);
    let player_pos: [f32, ..3] = Array::from_iter(level["Data"]["Player"]["Pos"].as_double_list().unwrap().iter().map(|&x| x as f32));
    let player_chunk = [player_pos.x(), player_pos.z()].map(|x| (x / 16.0).floor() as i32);
    let player_rot = level["Data"]["Player"]["Rotation"].as_float_list().unwrap();
    let player_yaw = player_rot[0];
    let player_pitch = player_rot[1];

    let [region_x, region_z] = player_chunk.map(|x| x >> 5);
    let region_file = world.join(format!("region/r.{}.{}.mca", region_x, region_z));
    let region = minecraft::region::Region::open(&region_file);

    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_3,
        GameWindowSettings {
            title: format!("Hematite - {}", world.display()),
            size: [854, 480],
            fullscreen: false,
            exit_on_esc: true,
        }
    );
    let (mut device, frame) = window.gfx();

    let assets = &AssetStore::from_folder("../assets");

    // Load biomes.
    let biomes = minecraft::biome::Biomes::load(assets);

    // Load block state definitions and models.
    let block_states = minecraft::block_state::BlockStates::load(assets, &mut device);

    let mut renderer = shader::Renderer::new(device, frame, block_states.texture().tex);

    let mut chunk_manager = chunk::ChunkManager::new();

    println!("Started loading chunks...");
    let [cx_base, cz_base] = player_chunk.map(|x| max(0, (x & 0x1f) - 8) as u8);
    for cz in range(cz_base, cz_base + 16) {
        for cx in range(cx_base, cx_base + 16) {
            match region.get_chunk_column(cx, cz) {
                Some(column) => {
                    let [cx, cz] = [cx as i32 + region_x * 32, cz as i32 + region_z * 32];
                    chunk_manager.add_chunk_column(cx, cz, column)
                }
                None => {}
            }
        }
    }
    println!("Finished loading chunks.");

    renderer.set_projection(cam::CameraPerspective {
        fov: 70.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: {
            let (w, h) = window.get_size();
            (w as f32) / (h as f32)
        }
    }.projection());

    let mut first_person_settings = cam::FirstPersonSettings::default();
    first_person_settings.speed_horizontal = 8.0;
    first_person_settings.speed_vertical = 4.0;
    let mut first_person = cam::FirstPerson::new(
        player_pos,
        first_person_settings
    );
    first_person.yaw = PI - player_yaw / 180.0 * PI;
    first_person.pitch = player_pitch / 180.0 * PI;

    let game_iter_settings = GameIteratorSettings {
        updates_per_second: 120,
        max_frames_per_second: 10000
    };

    // Disable V-Sync.
    sdl2::video::gl_set_swap_interval(0);

    let mut fps_counter = fps_counter::FPSCounter::new();

    let mut pending_chunks = vec![];
    chunk_manager.each_chunk_and_neighbors(|coords, buffer, chunks, column_biomes| {
        pending_chunks.push((coords, buffer, chunks, column_biomes));
    });

    let mut capture_cursor = false;
    println!("Press C to capture mouse");

    let mut staging_buffer = vec![];
    let mut last_render = time::precise_time_ns();
    let mut events = GameIterator::new(&mut window, &game_iter_settings);
    for e in events {
        match e {
            Render(_) => {
                // Update camera.
                let now = time::precise_time_ns();
                let dt = (now - last_render) as f64 / 1_000_000_000.0f64;
                first_person.update(dt);
                last_render = now;

                // Apply the same y/z camera offset vanilla minecraft has.
                let mut camera = first_person.camera(0.0);
                camera.position[1] += 1.62;
                let mut xz_forward = camera.forward;
                xz_forward[1] = 0.0;
                xz_forward = vec3_normalized(xz_forward);
                camera.position = vec3_add(camera.position, vec3_scale(xz_forward, 0.1));

                renderer.set_view(camera.orthogonal());
                renderer.reset();
                chunk_manager.each_chunk(|_, _, _, _, buffer| {
                    match buffer {
                        Some(buffer) => renderer.render(buffer),
                        None => {}
                    }
                });
                renderer.end_frame();

                let fps = fps_counter.update();
                let title = format!("Hematite @ {}FPS - {}", fps, world.display());
                events.game_window.window.set_title(title.as_slice());
            }
            Update(_) => {
                // HACK(eddyb) find the closest chunk to the player.
                // The pending vector should be sorted instead.
                let closest = pending_chunks.iter().enumerate().min_by(|&(_, &([cx, cy, cz], _, _, _))| {
                    let [px, py, pz] = first_person.position.map(|x| (x / 16.0).floor() as i32);
                    let [x2, y2, z2] = [cx - px, cy - py, cz - pz].map(|x| x * x);
                    x2 + y2 + z2
                }).map(|(i, _)| i);

                let pending = closest.and_then(|i| pending_chunks.swap_remove(i));
                match pending {
                    Some((coords, buffer, chunks, column_biomes)) => {
                        match buffer.get() {
                            Some(buffer) => renderer.delete_buffer(buffer),
                            None => {}
                        }
                        minecraft::block_state::fill_buffer(&block_states, &biomes,
                                                            &mut staging_buffer,
                                                            coords, chunks,
                                                            column_biomes);
                        buffer.set(Some(renderer.create_buffer(staging_buffer.as_slice())));
                        staging_buffer.clear();

                        if pending_chunks.is_empty() {
                            println!("Finished filling chunk vertex buffers.");
                        }
                    }
                    None => {}
                }
            }
            Input(input::KeyPress { key: input::keyboard::C }) => {
                println!("Turned cursor capture {}",
                    if capture_cursor { "off" } else { "on" });
                capture_cursor = !capture_cursor;

                events.game_window.capture_cursor(capture_cursor);
            }
            Input(input::MouseRelativeMove { .. }) => {
                if !capture_cursor {
                    // Don't send the mouse event to the FPS controller.
                    continue;
                }
            }
            _ => {}
        }

        // Camera controller.
        match e {
            Input(ref args) => first_person.input(args),
            _ => {}
        }
    }
}
<commit_msg>Cull chunks if they're completely outside the screen space.<commit_after>#![feature(globs, macro_rules, phase)]

extern crate debug;
extern crate piston;
extern crate sdl2;
extern crate sdl2_game_window;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate image;
extern crate libc;
extern crate cgmath;
extern crate time;

extern crate flate;
extern crate native;
extern crate rustrt;
extern crate serialize;

use sdl2_game_window::GameWindowSDL2 as Window;
use piston::input;
use piston::cam;
use piston::vecmath::{vec3_add, vec3_scale, vec3_normalized};
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindow,
    GameWindowSettings,
    Input,
    Render,
    Update
};

use array::*;

use std::cmp::max;
use std::f32::INFINITY;
use std::f32::consts::PI;
use std::io::fs::File;

pub mod array;
pub mod chunk;
pub mod cube;
pub mod fps_counter;
pub mod shader;
pub mod texture;

pub mod minecraft {
    pub use self::data_1_8_pre2 as data;

    mod data_1_8_pre2;
    pub mod biome;
    pub mod block_state;
    pub mod model;
    pub mod nbt;
    pub mod region;
}

fn main() {
    let world = Path::new(std::os::args().as_slice().get(1).expect("Usage: ./hematite <path/to/world>").as_slice());

    let level = minecraft::nbt::Nbt::from_gzip(File::open(&world.join("level.dat")).read_to_end().unwrap().as_slice()).unwrap();
    println!("{}", level);
    let player_pos: [f32, ..3] = Array::from_iter(level["Data"]["Player"]["Pos"].as_double_list().unwrap().iter().map(|&x| x as f32));
    let player_chunk = [player_pos.x(), player_pos.z()].map(|x| (x / 16.0).floor() as i32);
    let player_rot = level["Data"]["Player"]["Rotation"].as_float_list().unwrap();
    let player_yaw = player_rot[0];
    let player_pitch = player_rot[1];

    let [region_x, region_z] = player_chunk.map(|x| x >> 5);
    let region_file = world.join(format!("region/r.{}.{}.mca", region_x, region_z));
    let region = minecraft::region::Region::open(&region_file);

    let mut window = Window::new(
        piston::shader_version::opengl::OpenGL_3_3,
        GameWindowSettings {
            title: format!("Hematite - {}", world.display()),
            size: [854, 480],
            fullscreen: false,
            exit_on_esc: true,
        }
    );
    let (mut device, frame) = window.gfx();

    let assets = &AssetStore::from_folder("../assets");

    // Load biomes.
    let biomes = minecraft::biome::Biomes::load(assets);

    // Load block state definitions and models.
    let block_states = minecraft::block_state::BlockStates::load(assets, &mut device);

    let mut renderer = shader::Renderer::new(device, frame, block_states.texture().tex);

    let mut chunk_manager = chunk::ChunkManager::new();

    println!("Started loading chunks...");
    let [cx_base, cz_base] = player_chunk.map(|x| max(0, (x & 0x1f) - 8) as u8);
    for cz in range(cz_base, cz_base + 16) {
        for cx in range(cx_base, cx_base + 16) {
            match region.get_chunk_column(cx, cz) {
                Some(column) => {
                    let [cx, cz] = [cx as i32 + region_x * 32, cz as i32 + region_z * 32];
                    chunk_manager.add_chunk_column(cx, cz, column)
                }
                None => {}
            }
        }
    }
    println!("Finished loading chunks.");

    let projection_mat = cam::CameraPerspective {
        fov: 70.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: {
            let (w, h) = window.get_size();
            (w as f32) / (h as f32)
        }
    }.projection();
    renderer.set_projection(projection_mat);

    let mut first_person_settings = cam::FirstPersonSettings::default();
    first_person_settings.speed_horizontal = 8.0;
    first_person_settings.speed_vertical = 4.0;
    let mut first_person = cam::FirstPerson::new(
        player_pos,
        first_person_settings
    );
    first_person.yaw = PI - player_yaw / 180.0 * PI;
    first_person.pitch = player_pitch / 180.0 * PI;

    let game_iter_settings = GameIteratorSettings {
        updates_per_second: 120,
        max_frames_per_second: 10000
    };

    // Disable V-Sync.
    sdl2::video::gl_set_swap_interval(0);

    let mut fps_counter = fps_counter::FPSCounter::new();

    let mut pending_chunks = vec![];
    chunk_manager.each_chunk_and_neighbors(|coords, buffer, chunks, column_biomes| {
        pending_chunks.push((coords, buffer, chunks, column_biomes));
    });

    let mut capture_cursor = false;
    println!("Press C to capture mouse");

    let mut staging_buffer = vec![];
    let mut last_render = time::precise_time_ns();
    let mut events = GameIterator::new(&mut window, &game_iter_settings);
    for e in events {
        match e {
            Render(_) => {
                // Update camera.
                let now = time::precise_time_ns();
                let dt = (now - last_render) as f64 / 1_000_000_000.0f64;
                first_person.update(dt);
                last_render = now;

                // Apply the same y/z camera offset vanilla minecraft has.
                let mut camera = first_person.camera(0.0);
                camera.position[1] += 1.62;
                let mut xz_forward = camera.forward;
                xz_forward[1] = 0.0;
                xz_forward = vec3_normalized(xz_forward);
                camera.position = vec3_add(camera.position, vec3_scale(xz_forward, 0.1));

                let view_mat = camera.orthogonal();
                renderer.set_view(view_mat);
                renderer.reset();
                let mut num_chunks = 0u;
                let mut num_total_chunks = 0u;
                chunk_manager.each_chunk(|cx, cy, cz, _, buffer| {
                    match buffer {
                        Some(buffer) => {
                            num_total_chunks += 1;

                            let inf = INFINITY;
                            let mut bb_min = [inf, inf, inf];
                            let mut bb_max = [-inf, -inf, -inf];
                            let xyz = [cx, cy, cz].map(|x| x as f32 * 16.0);
                            for &dx in [0.0, 16.0].iter() {
                                for &dy in [0.0, 16.0].iter() {
                                    for &dz in [0.0, 16.0].iter() {
                                        use piston::vecmath::col_mat4_transform;
                                        let [x, y, z] = vec3_add(xyz, [dx, dy, dz]);
                                        let xyzw = col_mat4_transform(view_mat, [x, y, z, 1.0]);
                                        let [x, y, z, w] = col_mat4_transform(projection_mat, xyzw);
                                        let xyz = vec3_scale([x, y, z], 1.0 / w);
                                        bb_min = Array::from_fn(|i| bb_min[i].min(xyz[i]));
                                        bb_max = Array::from_fn(|i| bb_max[i].max(xyz[i]));
                                    }
                                }
                            }

                            let cull_bits: [bool, ..3] = Array::from_fn(|i| {
                                let (min, max) = (bb_min[i], bb_max[i]);
                                min.signum() == max.signum() && min.abs().min(max.abs()) >= 1.0
                            });

                            if !cull_bits.iter().any(|&cull| cull) {
                                renderer.render(buffer);
                                num_chunks += 1;
                            }
                        }
                        None => {}
                    }
                });
                renderer.end_frame();

                let fps = fps_counter.update();
                let title = format!("Hematite w/ {}/{}C @ {}FPS - {}",
                                    num_chunks, num_total_chunks, fps, world.display());
                events.game_window.window.set_title(title.as_slice());
            }
            Update(_) => {
                // HACK(eddyb) find the closest chunk to the player.
                // The pending vector should be sorted instead.
                let closest = pending_chunks.iter().enumerate().min_by(|&(_, &([cx, cy, cz], _, _, _))| {
                    let [px, py, pz] = first_person.position.map(|x| (x / 16.0).floor() as i32);
                    let [x2, y2, z2] = [cx - px, cy - py, cz - pz].map(|x| x * x);
                    x2 + y2 + z2
                }).map(|(i, _)| i);

                let pending = closest.and_then(|i| pending_chunks.swap_remove(i));
                match pending {
                    Some((coords, buffer, chunks, column_biomes)) => {
                        match buffer.get() {
                            Some(buffer) => renderer.delete_buffer(buffer),
                            None => {}
                        }
                        minecraft::block_state::fill_buffer(&block_states, &biomes,
                                                            &mut staging_buffer,
                                                            coords, chunks,
                                                            column_biomes);
                        buffer.set(Some(renderer.create_buffer(staging_buffer.as_slice())));
                        staging_buffer.clear();

                        if pending_chunks.is_empty() {
                            println!("Finished filling chunk vertex buffers.");
                        }
                    }
                    None => {}
                }
            }
            Input(input::KeyPress { key: input::keyboard::C }) => {
                println!("Turned cursor capture {}",
                    if capture_cursor { "off" } else { "on" });
                capture_cursor = !capture_cursor;

                events.game_window.capture_cursor(capture_cursor);
            }
            Input(input::MouseRelativeMove { .. }) => {
                if !capture_cursor {
                    // Don't send the mouse event to the FPS controller.
                    continue;
                }
            }
            _ => {}
        }

        // Camera controller.
        match e {
            Input(ref args) => first_person.input(args),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate byteorder;
extern crate sdl2;

use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;

mod cpu;
mod graphics;
mod input;
mod interconnect;
mod memory;
mod sound;
mod vm;

fn main() {
    if let Some(rom_file_name) = env::args().nth(1) {
        let rom = read_bin(rom_file_name);

        let mut vm = vm::VirtualMachine::new(rom);
        vm.run();
    } else {
        println!("noth {} a CHIP-8 Virtual Machine in Rust\n", env!("CARGO_PKG_VERSION"));
        println!("usage: {} <rom file>", env::args().nth(0).unwrap());
    }
}

fn read_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).unwrap();
    buffer
}
<commit_msg>Reformatted the usage info a bit<commit_after>extern crate rand;
extern crate byteorder;
extern crate sdl2;

use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;

mod cpu;
mod graphics;
mod input;
mod interconnect;
mod memory;
mod sound;
mod vm;

fn main() {
    // TODO: Use an actual argument parser when adding options in the future.
    if let Some(rom_file_name) = env::args().nth(1) {
        let rom = read_bin(rom_file_name);

        let mut vm = vm::VirtualMachine::new(rom);
        vm.run();
    } else {
        print_usage();
        std::process::exit(1);
    }
}

/// Prints the application name alongside the cargo version.
fn print_version() {
    println!("notch {}", env!("CARGO_PKG_VERSION"));
}

/// Prints usage information.
fn print_usage() {
    println!("Notch is a CHIP-8 virtual machine written in Rust.");
    println!("");
    println!("Usage: notch [ROM]");
    println!("");
    println!("To contribute or report bugs, please see:");
    println!("<https://github.com/Reshurum/notch>");
}

/// Reads a file into a vector of unsigned bytes.
fn read_bin<P: AsRef<Path>>(path: P) -> Vec<u8> {
    let mut file = fs::File::open(path).unwrap();
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).unwrap();
    buffer
}
<|endoftext|>


<commit_before>// fs.rs
use std::env; // env::home_dir()
use std::fs; // fs::read_dir()
//use std::path::Path;
use std::path::PathBuf;

enum ItemType {
   Dir,
   File,
   Other,
}

struct FoundItem {
   item_type: ItemType,
   item_path: PathBuf,
}

impl FoundItem {
   fn dump(self: FoundItem) {
      match self.item_type {
         // use .unwrap() to get the contents of the Option returned by
         // file_name()
         ItemType::Dir   => println!("d {:?}",
            self.item_path.file_name().unwrap()),
         ItemType::File  => println!("f {:?}",
            self.item_path.file_name().unwrap()),
         ItemType::Other => println!("o {:?}",
            self.item_path.file_name().unwrap()),
      }
   }
}

fn main() {
   //let mut idg_dir_path = PathBuf::from("~/Files/Doom/idgames");
   // create an empty PathBuf
   let mut idg_dir = PathBuf::new();
   // call `home_dir()` and act on what it returns
   match env::home_dir() {
      //Some(idg_dir) => println!("{}", idg_dir.display()),
      // home_dir() returned something
      Some(home_dir) => idg_dir = home_dir,
      // home_dir() returned nothing
      None => println!("Can't obtain home directory path!"),
   }

   // append more directories on to idg_dir
   idg_dir.push("Files");
   idg_dir.push("Doom");
   idg_dir.push("idgames");

   // working example from:
   // https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_name
   if let Ok(entries) = fs::read_dir(idg_dir) {
      for dir_entry in entries {
         if let Ok(dir_entry) = dir_entry {
            //let path = dir_entry.path();
            // get the metadata object for this DirEntry
            if let Ok(metadata) = dir_entry.metadata() {
               // below, `dir_entry` is a `DirEntry`.
               if metadata.is_file() {
                  let item = FoundItem {
                     item_type: ItemType::File,
                     item_path: dir_entry.path()
                  };
                  item.dump();
               }
               else if metadata.is_dir() {
                  let item = FoundItem {
                     item_type: ItemType::Dir,
                     item_path: dir_entry.path()
                  };
                  item.dump();
               }
               else {
                  let item = FoundItem {
                     item_type: ItemType::Other,
                     item_path: dir_entry.path()
                  };
                  item.dump();
               }
            }
         }
      }
   }

/*
// doesn't work, try!() won't handle _Result_ objects
   for entry in try!(fs::read_dir(idg_dir)) {
       let dir = try!(entry);
       println!("{:?}", dir.path());
   }
*/

/*
   // try and read the contents of idg_dir/
   // - doesn't work, try!() won't handle _Result_ objects
   for entry in try!(fs::read_dir(idg_dir)) {
      let dir = try!(entry);
      // 'entry' is still a _Result_, so this is broken
      let metadata = try!(entry.metadata());
      // 'dir' is still a _DirEntry_
      if dir.is_dir() {
         println!("d {:?}", dir.path());
      } else {
         println!("! {:?}", dir.path());
      }
   }
*/

/*
   if let Ok(entries) = fs::read_dir(idg_dir) {
      for entry in entries {
         // 'item' is a Result, unwrap it to get access to Metadata
         if let Ok(entry) = entry {
               if let Ok(metadata) = entry.metadata() {};
               // entry.path() returns a _Result_
               if let Ok(path) = entry.path() {};
               // 'entry' is a _DirEntry_, and doesn't have an 'is_dir()'
               // method
               if entry.is_dir() {
                  println!("d {:?} {:?}",
                     // 'metadata' isn't available here
                     metadata.file_type(),
                     // the compiler doesn't know what 'path' is here
                     path);
            }
         }
      }
   }
*/

/*
   // working example
   println!("Updated іdgames/ path: {}", idg_dir.display());
   // read the contents of idg_dir/
   match fs::read_dir(idg_dir) {
      Err(why) => println!("! {:?}", why.kind()),
      Ok(paths) => for path in paths {
         println!("> {:?}", path.unwrap().path());
      }
   }
*/

}
<commit_msg>src/main.rs: create 'item' at top of block, and dump item at bottom<commit_after>// fs.rs
use std::env; // env::home_dir()
use std::fs; // fs::read_dir()
//use std::path::Path;
use std::path::PathBuf;

enum ItemType {
   Dir,
   File,
   Other,
}

struct FoundItem {
   item_type: ItemType,
   item_path: PathBuf,
}

impl FoundItem {
   fn dump(self: FoundItem) {
      match self.item_type {
         // use .unwrap() to get the contents of the Option returned by
         // file_name()
         ItemType::Dir   => println!("d {:?}",
            self.item_path.file_name().unwrap()),
         ItemType::File  => println!("f {:?}",
            self.item_path.file_name().unwrap()),
         ItemType::Other => println!("o {:?}",
            self.item_path.file_name().unwrap()),
      }
   }
}

fn main() {
   //let mut idg_dir_path = PathBuf::from("~/Files/Doom/idgames");
   // create an empty PathBuf
   let mut idg_dir = PathBuf::new();
   // call `home_dir()` and act on what it returns
   match env::home_dir() {
      //Some(idg_dir) => println!("{}", idg_dir.display()),
      // home_dir() returned something
      Some(home_dir) => idg_dir = home_dir,
      // home_dir() returned nothing
      None => println!("Can't obtain home directory path!"),
   }

   // append more directories on to idg_dir
   idg_dir.push("Files");
   idg_dir.push("Doom");
   idg_dir.push("idgames");

   // working example from:
   // https://doc.rust-lang.org/std/fs/struct.DirEntry.html#method.file_name
   if let Ok(entries) = fs::read_dir(idg_dir) {
      for dir_entry in entries {
         if let Ok(dir_entry) = dir_entry {
            //let path = dir_entry.path();
            // get the metadata object for this DirEntry
            if let Ok(metadata) = dir_entry.metadata() {
               let item;
               // below, `dir_entry` is a `DirEntry`.
               if metadata.is_file() {
                  item = FoundItem {
                     item_type: ItemType::File,
                     item_path: dir_entry.path()
                  };
               }
               else if metadata.is_dir() {
                  item = FoundItem {
                     item_type: ItemType::Dir,
                     item_path: dir_entry.path()
                  };
               }
               else {
                  item = FoundItem {
                     item_type: ItemType::Other,
                     item_path: dir_entry.path()
                  };

               }
               // dump the item that we just created
               item.dump();
            }
         }
      }
   }

/*
// doesn't work, try!() won't handle _Result_ objects
   for entry in try!(fs::read_dir(idg_dir)) {
       let dir = try!(entry);
       println!("{:?}", dir.path());
   }
*/

/*
   // try and read the contents of idg_dir/
   // - doesn't work, try!() won't handle _Result_ objects
   for entry in try!(fs::read_dir(idg_dir)) {
      let dir = try!(entry);
      // 'entry' is still a _Result_, so this is broken
      let metadata = try!(entry.metadata());
      // 'dir' is still a _DirEntry_
      if dir.is_dir() {
         println!("d {:?}", dir.path());
      } else {
         println!("! {:?}", dir.path());
      }
   }
*/

/*
   if let Ok(entries) = fs::read_dir(idg_dir) {
      for entry in entries {
         // 'item' is a Result, unwrap it to get access to Metadata
         if let Ok(entry) = entry {
               if let Ok(metadata) = entry.metadata() {};
               // entry.path() returns a _Result_
               if let Ok(path) = entry.path() {};
               // 'entry' is a _DirEntry_, and doesn't have an 'is_dir()'
               // method
               if entry.is_dir() {
                  println!("d {:?} {:?}",
                     // 'metadata' isn't available here
                     metadata.file_type(),
                     // the compiler doesn't know what 'path' is here
                     path);
            }
         }
      }
   }
*/

/*
   // working example
   println!("Updated іdgames/ path: {}", idg_dir.display());
   // read the contents of idg_dir/
   match fs::read_dir(idg_dir) {
      Err(why) => println!("! {:?}", why.kind()),
      Ok(paths) => for path in paths {
         println!("> {:?}", path.unwrap().path());
      }
   }
*/

}
<|endoftext|>