<commit_before>// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate steven_resources as internal;

use std::thread;
use std::path;
use std::io;
use std::fs;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};

use hyper;
use zip;

use ui;

const RESOURCES_VERSION: &'static str = "1.9.2";
const VANILLA_CLIENT_URL: &'static str = "https://launcher.mojang.com/mc/game/1.9.2/client/19106fd5e222dca0f2dde9f66db8384c9a7db957/client.jar";

pub trait Pack: Sync + Send {
    fn open(&self, name: &str) -> Option<Box<io::Read>>;
}

pub struct Manager {
    packs: Vec<Box<Pack>>,
    version: usize,

    vanilla_chan: Option<mpsc::Receiver<bool>>,
    vanilla_progress: Arc<Mutex<Progress>>,
}

pub struct ManagerUI {
    progress_ui: Vec<ProgressUI>,
}

struct ProgressUI {
    task_name: String,
    task_file: String,
    position: f64,
    closing: bool,
    progress: f64,

    background: ui::ImageRef,
    progress_bar: ui::ImageRef,
}

struct Progress {
    tasks: Vec<Task>,
}

struct Task {
    task_name: String,
    task_file: String,
    total: u64,
    progress: u64,
}

unsafe impl Sync for Manager {}

impl Manager {
    pub fn new() -> (Manager, ManagerUI) {
        let mut m = Manager {
            packs: Vec::new(),
            version: 0,
            vanilla_chan: None,
            vanilla_progress: Arc::new(Mutex::new(Progress {
                tasks: vec![],
            })),
        };
        m.add_pack(Box::new(InternalPack));
        m.download_vanilla();
        (m, ManagerUI { progress_ui: vec!{} })
    }

    /// Returns the 'version' of the manager. The version is
    /// increase everytime a pack is added or removed.
    pub fn version(&self) -> usize {
        self.version
    }

    pub fn open(&self, plugin: &str, name: &str) -> Option<Box<io::Read>> {
        for pack in self.packs.iter().rev() {
            let path = format!("assets/{}/{}", plugin, name);
            if let Some(val) = pack.open(&path) {
                return Some(val);
            }
        }
        None
    }

    pub fn open_all(&self, plugin: &str, name: &str) -> Vec<Box<io::Read>> {
        let mut ret = Vec::new();
        for pack in self.packs.iter().rev() {
            let path = format!("assets/{}/{}", plugin, name);
            if let Some(val) = pack.open(&path) {
                ret.push(val);
            }
        }
        ret
    }

    pub fn tick(&mut self, mui: &mut ManagerUI, ui_container: &mut ui::Container, delta: f64) {
        // Check to see if the download of vanilla has completed
        // (if it was started)
        let mut done = false;
        if let Some(ref recv) = self.vanilla_chan {
            if let Ok(_) = recv.try_recv() {
                done = true;
            }
        }
        if done {
            self.vanilla_chan = None;
            self.load_vanilla();
        }

        const UI_HEIGHT: f64 = 32.0;

        let mut progress = self.vanilla_progress.lock().unwrap();
        progress.tasks.retain(|v| v.progress < v.total);
        // Find out what we have to work with
        for task in &progress.tasks {
            if !mui.progress_ui.iter()
                .filter(|v| v.task_file == task.task_file)
                .any(|v| v.task_name == task.task_name) {
                // Add a ui element for it
                let background = ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, -UI_HEIGHT)
                    .size(300.0, UI_HEIGHT)
                    .colour((0, 0, 0, 100))
                    .draw_index(100)
                    .alignment(ui::VAttach::Bottom, ui::HAttach::Left)
                    .create(ui_container);

                ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, 0.0)
                    .size(300.0, 10.0)
                    .colour((0, 0, 0, 200))
                    .attach(&mut *background.borrow_mut());
                ui::TextBuilder::new()
                    .text(&*task.task_name)
                    .position(3.0, 0.0)
                    .scale_x(0.5)
                    .scale_y(0.5)
                    .draw_index(1)
                    .attach(&mut *background.borrow_mut());
                ui::TextBuilder::new()
                    .text(&*task.task_file)
                    .position(3.0, 12.0)
                    .scale_x(0.5)
                    .scale_y(0.5)
                    .draw_index(1)
                    .attach(&mut *background.borrow_mut());

                let progress_bar = ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, 0.0)
                    .size(0.0, 10.0)
                    .colour((0, 255, 0, 255))
                    .alignment(ui::VAttach::Bottom, ui::HAttach::Left)
                    .attach(&mut *background.borrow_mut());

                mui.progress_ui.push(ProgressUI {
                    task_name: task.task_name.clone(),
                    task_file: task.task_file.clone(),
                    position: -UI_HEIGHT,
                    closing: false,
                    progress: 0.0,
                    background: background,
                    progress_bar: progress_bar,
                });
            }
        }
        for ui in &mut mui.progress_ui {
            let mut found = false;
            let mut prog = 1.0;
            for task in progress.tasks.iter()
                .filter(|v| v.task_file == ui.task_file)
                .filter(|v| v.task_name == ui.task_name) {
                found = true;
                prog = task.progress as f64 / task.total as f64;
            }
            if !found {
                ui.closing = true;
                ui.position = -UI_HEIGHT;
            }
            ui.progress = prog;
        }
        let mut offset = 0.0;
        for ui in &mut mui.progress_ui {
            if ui.closing {
                continue;
            }
            ui.position = offset;
            offset += UI_HEIGHT;
        }
        // Move elements
        let delta = delta.min(5.0);
        for ui in &mut mui.progress_ui {
            let mut background = ui.background.borrow_mut();
            if (background.y - ui.position).abs() < 0.7 * delta {
                background.y = ui.position;
            } else {
                background.y += (ui.position - background.y).signum() * 0.7 * delta;
            }
            let mut bar = ui.progress_bar.borrow_mut();
            let target_size = (300.0 * ui.progress).min(300.0);
            if (bar.width - target_size).abs() < 1.0 * delta {
                bar.width = target_size;
            } else {
                bar.width += ((target_size - bar.width).signum() * delta).max(0.0);
            }
        }

        // Clean up dead elements
        mui.progress_ui.retain(|v| v.position >= -UI_HEIGHT && !v.closing);
    }

    fn add_pack(&mut self, pck: Box<Pack>) {
        self.packs.push(pck);
        self.version += 1;
    }

    fn load_vanilla(&mut self) {
        let loc = format!("./resources-{}", RESOURCES_VERSION);
        let location = path::Path::new(&loc);
        self.add_pack(Box::new(DirPack { root: location.to_path_buf() }))
    }

    fn download_vanilla(&mut self) {
        let loc = format!("./resources-{}", RESOURCES_VERSION);
        let location = path::Path::new(&loc);
        if fs::metadata(location.join("steven.assets")).is_ok() {
            self.load_vanilla();
            return;
        }
        let (send, recv) = mpsc::channel();
        self.vanilla_chan = Some(recv);

        let progress_info = self.vanilla_progress.clone();
        thread::spawn(move || {
            let client = hyper::Client::new();
            let res = client.get(VANILLA_CLIENT_URL)
                            .send()
                            .unwrap();
            let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();

            let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
            let task_file = format!("./resources-{}", RESOURCES_VERSION);
            Self::add_task(&progress_info, "Downloading Core Assets", &task_file, *length);
            {
                let mut progress = ProgressRead {
                    read: res,
                    progress: &progress_info,
                    task_name: "Downloading Core Assets".into(),
                    task_file: task_file,
                };
                io::copy(&mut progress, &mut file).unwrap();
            }

            // Copy the resources from the zip
            let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
            let mut zip = zip::ZipArchive::new(file).unwrap();

            let task_file = format!("./resources-{}", RESOURCES_VERSION);
            Self::add_task(&progress_info, "Unpacking Core Assets", &task_file, zip.len() as u64);

            let loc = format!("./resources-{}", RESOURCES_VERSION);
            let location = path::Path::new(&loc);
            let count = zip.len();
            for i in 0..count {
                Self::add_task_progress(&progress_info, "Unpacking Core Assets", &task_file, 1);
                let mut file = zip.by_index(i).unwrap();
                if !file.name().starts_with("assets/") {
                    continue;
                }
                let path = location.join(file.name());
                fs::create_dir_all(path.parent().unwrap()).unwrap();
                let mut out = fs::File::create(path).unwrap();
                io::copy(&mut file, &mut out).unwrap();
            }

            fs::File::create(location.join("steven.assets")).unwrap(); // Marker file
            send.send(true).unwrap();

            fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
        });
    }

    fn add_task(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, length: u64) {
        let mut info = progress.lock().unwrap();
        info.tasks.push(Task {
            task_name: name.into(),
            task_file: file.into(),
            total: length,
            progress: 0,
        });
    }

    fn add_task_progress(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, prog: u64) {
        let mut progress = progress.lock().unwrap();
        for task in progress.tasks.iter_mut()
            .filter(|v| v.task_file == file)
            .filter(|v| v.task_name == name) {
            task.progress += prog as u64;
        }
    }
}

struct DirPack {
    root: path::PathBuf,
}

impl Pack for DirPack {
    fn open(&self, name: &str) -> Option<Box<io::Read>> {
        match fs::File::open(self.root.join(name)) {
            Ok(val) => Some(Box::new(val)),
            Err(_) => None,
        }
    }
}

struct InternalPack;

impl Pack for InternalPack {
    fn open(&self, name: &str) -> Option<Box<io::Read>> {
        match internal::get_file(name) {
            Some(val) => Some(Box::new(io::Cursor::new(val))),
            None => None,
        }
    }
}

struct ProgressRead<'a, T> {
    read: T,
    progress: &'a Arc<Mutex<Progress>>,
    task_name: String,
    task_file: String,
}

impl <'a, T: io::Read> io::Read for ProgressRead<'a, T> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let size = try!(self.read.read(buf));
        Manager::add_task_progress(self.progress, &self.task_name, &self.task_file, size as u64);
        Ok(size)
    }
}
<commit_msg>Download vanilla assets (sounds/locales) and load as a pack<commit_after>// Copyright 2016 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate steven_resources as internal;

use std::thread;
use std::path;
use std::io;
use std::fs;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use serde_json;

use hyper;
use zip;

use types::hash::FNVHash;
use ui;

const RESOURCES_VERSION: &'static str = "1.9.2";
const VANILLA_CLIENT_URL: &'static str = "https://launcher.mojang.com/mc/game/1.9.2/client/19106fd5e222dca0f2dde9f66db8384c9a7db957/client.jar";
const ASSET_VERSION: &'static str = "1.9";
const ASSET_INDEX_URL: &'static str = "https://launchermeta.mojang.com/mc-staging/assets/1.9/13f566a82f5a8a1b5a5552d968a93c2d9b86df66/1.9.json";

pub trait Pack: Sync + Send {
    fn open(&self, name: &str) -> Option<Box<io::Read>>;
}

pub struct Manager {
    packs: Vec<Box<Pack>>,
    version: usize,

    vanilla_chan: Option<mpsc::Receiver<bool>>,
    vanilla_assets_chan: Option<mpsc::Receiver<bool>>,
    vanilla_progress: Arc<Mutex<Progress>>,
}

pub struct ManagerUI {
    progress_ui: Vec<ProgressUI>,
    num_tasks: isize,
}

struct ProgressUI {
    task_name: String,
    task_file: String,
    position: f64,
    closing: bool,
    progress: f64,

    background: ui::ImageRef,
    progress_bar: ui::ImageRef,
}

struct Progress {
    tasks: Vec<Task>,
}

struct Task {
    task_name: String,
    task_file: String,
    total: u64,
    progress: u64,
}

unsafe impl Sync for Manager {}

impl Manager {
    pub fn new() -> (Manager, ManagerUI) {
        let mut m = Manager {
            packs: Vec::new(),
            version: 0,
            vanilla_chan: None,
            vanilla_assets_chan: None,
            vanilla_progress: Arc::new(Mutex::new(Progress {
                tasks: vec![],
            })),
        };
        m.add_pack(Box::new(InternalPack));
        m.download_vanilla();
        m.download_assets();
        (m, ManagerUI { progress_ui: vec!{}, num_tasks: 0 })
    }

    /// Returns the 'version' of the manager. The version is
    /// increase everytime a pack is added or removed.
    pub fn version(&self) -> usize {
        self.version
    }

    pub fn open(&self, plugin: &str, name: &str) -> Option<Box<io::Read>> {
        let path = format!("assets/{}/{}", plugin, name);
        for pack in self.packs.iter().rev() {
            if let Some(val) = pack.open(&path) {
                return Some(val);
            }
        }
        None
    }

    pub fn open_all(&self, plugin: &str, name: &str) -> Vec<Box<io::Read>> {
        let mut ret = Vec::new();
        let path = format!("assets/{}/{}", plugin, name);
        for pack in self.packs.iter().rev() {
            if let Some(val) = pack.open(&path) {
                ret.push(val);
            }
        }
        ret
    }

    pub fn tick(&mut self, mui: &mut ManagerUI, ui_container: &mut ui::Container, delta: f64) {
        let delta = delta.min(5.0);
        // Check to see if the download of vanilla has completed
        // (if it was started)
        let mut done = false;
        if let Some(ref recv) = self.vanilla_chan {
            if let Ok(_) = recv.try_recv() {
                done = true;
            }
        }
        if done {
            self.vanilla_chan = None;
            self.load_vanilla();
        }
        let mut done = false;
        if let Some(ref recv) = self.vanilla_assets_chan {
            if let Ok(_) = recv.try_recv() {
                done = true;
            }
        }
        if done {
            self.vanilla_assets_chan = None;
            self.load_assets();
        }

        const UI_HEIGHT: f64 = 32.0;

        let mut progress = self.vanilla_progress.lock().unwrap();
        progress.tasks.retain(|v| v.progress < v.total);
        // Find out what we have to work with
        for task in &progress.tasks {
            if !mui.progress_ui.iter()
                .filter(|v| v.task_file == task.task_file)
                .any(|v| v.task_name == task.task_name) {
                mui.num_tasks += 1;
                // Add a ui element for it
                let background = ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, -UI_HEIGHT)
                    .size(350.0, UI_HEIGHT)
                    .colour((0, 0, 0, 100))
                    .draw_index(0xFFFFFF - mui.num_tasks)
                    .alignment(ui::VAttach::Bottom, ui::HAttach::Left)
                    .create(ui_container);

                ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, 0.0)
                    .size(350.0, 10.0)
                    .colour((0, 0, 0, 200))
                    .attach(&mut *background.borrow_mut());
                ui::TextBuilder::new()
                    .text(&*task.task_name)
                    .position(3.0, 0.0)
                    .scale_x(0.5)
                    .scale_y(0.5)
                    .draw_index(1)
                    .attach(&mut *background.borrow_mut());
                ui::TextBuilder::new()
                    .text(&*task.task_file)
                    .position(3.0, 12.0)
                    .scale_x(0.5)
                    .scale_y(0.5)
                    .draw_index(1)
                    .attach(&mut *background.borrow_mut());

                let progress_bar = ui::ImageBuilder::new()
                    .texture("steven:solid")
                    .position(0.0, 0.0)
                    .size(0.0, 10.0)
                    .colour((0, 255, 0, 255))
                    .draw_index(2)
                    .alignment(ui::VAttach::Bottom, ui::HAttach::Left)
                    .attach(&mut *background.borrow_mut());

                mui.progress_ui.push(ProgressUI {
                    task_name: task.task_name.clone(),
                    task_file: task.task_file.clone(),
                    position: -UI_HEIGHT,
                    closing: false,
                    progress: 0.0,
                    background: background,
                    progress_bar: progress_bar,
                });
            }
        }
        for ui in &mut mui.progress_ui {
            if ui.closing {
                continue;
            }
            let mut found = false;
            let mut prog = 1.0;
            for task in progress.tasks.iter()
                .filter(|v| v.task_file == ui.task_file)
                .filter(|v| v.task_name == ui.task_name) {
                found = true;
                prog = task.progress as f64 / task.total as f64;
            }
            let background = ui.background.borrow();
            let bar = ui.progress_bar.borrow();
            // Let the progress bar finish
            if !found && (background.y - ui.position).abs() < 0.7 * delta && (bar.width - 350.0).abs() < 1.0 * delta {
                ui.closing = true;
                ui.position = -UI_HEIGHT;
            }
            ui.progress = prog;
        }
        let mut offset = 0.0;
        for ui in &mut mui.progress_ui {
            if ui.closing {
                continue;
            }
            ui.position = offset;
            offset += UI_HEIGHT;
        }
        // Move elements
        for ui in &mut mui.progress_ui {
            let mut background = ui.background.borrow_mut();
            if (background.y - ui.position).abs() < 0.7 * delta {
                background.y = ui.position;
            } else {
                background.y += (ui.position - background.y).signum() * 0.7 * delta;
            }
            let mut bar = ui.progress_bar.borrow_mut();
            let target_size = (350.0 * ui.progress).min(350.0);
            if (bar.width - target_size).abs() < 1.0 * delta {
                bar.width = target_size;
            } else {
                bar.width += ((target_size - bar.width).signum() * delta).max(0.0);
            }
        }

        // Clean up dead elements
        mui.progress_ui.retain(|v| v.position >= -UI_HEIGHT || !v.closing);
    }

    fn add_pack(&mut self, pck: Box<Pack>) {
        self.packs.push(pck);
        self.version += 1;
    }

    fn load_vanilla(&mut self) {
        let loc = format!("./resources-{}", RESOURCES_VERSION);
        let location = path::Path::new(&loc);
        self.packs.insert(1, Box::new(DirPack { root: location.to_path_buf() }));
        self.version += 1;
    }

    fn load_assets(&mut self) {
        self.packs.insert(1, Box::new(ObjectPack::new()));
        self.version += 1;
    }

    fn download_assets(&mut self) {
        let loc = format!("./index/{}.json", ASSET_VERSION);
        let location = path::Path::new(&loc).to_owned();
        let progress_info = self.vanilla_progress.clone();
        let (send, recv) = mpsc::channel();
        if fs::metadata(&location).is_ok(){
            self.load_assets();
        } else {
            self.vanilla_assets_chan = Some(recv);
        }
        thread::spawn(move || {
            let client = hyper::Client::new();
            if fs::metadata(&location).is_err(){
                fs::create_dir_all(location.parent().unwrap()).unwrap();
                let res = client.get(ASSET_INDEX_URL)
                                .send()
                                .unwrap();

                let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
                Self::add_task(&progress_info, "Downloading Asset Index", &*location.to_string_lossy(), *length);
                {
                    let mut file = fs::File::create(format!("index-{}.tmp", ASSET_VERSION)).unwrap();
                    let mut progress = ProgressRead {
                        read: res,
                        progress: &progress_info,
                        task_name: "Downloading Asset Index".into(),
                        task_file: location.to_string_lossy().into_owned(),
                    };
                    io::copy(&mut progress, &mut file).unwrap();
                }
                fs::rename(format!("index-{}.tmp", ASSET_VERSION), &location).unwrap();
                send.send(true).unwrap();
            }
            let file = fs::File::open(&location).unwrap();
            let index: serde_json::Value = serde_json::from_reader(&file).unwrap();
            let root_location = path::Path::new("./objects/");
            let objects = index.find("objects").and_then(|v| v.as_object()).unwrap();
            Self::add_task(&progress_info, "Downloading Assets", "./objects", objects.len() as u64);
            for (k, v) in objects {
                let hash = v.find("hash").and_then(|v| v.as_string()).unwrap();
                let hash_path = format!("{}/{}", &hash[..2], hash);
                let location = root_location.join(&hash_path);
                if fs::metadata(&location).is_err(){
                    fs::create_dir_all(location.parent().unwrap()).unwrap();
                    let res = client.get(&format!("http://resources.download.minecraft.net/{}", hash_path))
                                    .send()
                                    .unwrap();
                    let length = v.find("size").and_then(|v| v.as_u64()).unwrap();
                    Self::add_task(&progress_info, "Downloading Asset", k, length);
                    let mut tmp_file = location.to_owned();
                    tmp_file.set_file_name(format!("{}.tmp", hash));
                    {
                        let mut file = fs::File::create(&tmp_file).unwrap();
                        let mut progress = ProgressRead {
                            read: res,
                            progress: &progress_info,
                            task_name: "Downloading Asset".into(),
                            task_file: k.to_owned(),
                        };
                        io::copy(&mut progress, &mut file).unwrap();
                    }
                    fs::rename(&tmp_file, &location).unwrap();
                }
                Self::add_task_progress(&progress_info, "Downloading Assets", "./objects", 1);
            }
        });
    }

    fn download_vanilla(&mut self) {
        let loc = format!("./resources-{}", RESOURCES_VERSION);
        let location = path::Path::new(&loc);
        if fs::metadata(location.join("steven.assets")).is_ok() {
            self.load_vanilla();
            return;
        }
        let (send, recv) = mpsc::channel();
        self.vanilla_chan = Some(recv);

        let progress_info = self.vanilla_progress.clone();
        thread::spawn(move || {
            let client = hyper::Client::new();
            let res = client.get(VANILLA_CLIENT_URL)
                            .send()
                            .unwrap();
            let mut file = fs::File::create(format!("{}.tmp", RESOURCES_VERSION)).unwrap();

            let length = *res.headers.get::<hyper::header::ContentLength>().unwrap();
            let task_file = format!("./resources-{}", RESOURCES_VERSION);
            Self::add_task(&progress_info, "Downloading Core Assets", &task_file, *length);
            {
                let mut progress = ProgressRead {
                    read: res,
                    progress: &progress_info,
                    task_name: "Downloading Core Assets".into(),
                    task_file: task_file,
                };
                io::copy(&mut progress, &mut file).unwrap();
            }

            // Copy the resources from the zip
            let file = fs::File::open(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
            let mut zip = zip::ZipArchive::new(file).unwrap();

            let task_file = format!("./resources-{}", RESOURCES_VERSION);
            Self::add_task(&progress_info, "Unpacking Core Assets", &task_file, zip.len() as u64);

            let loc = format!("./resources-{}", RESOURCES_VERSION);
            let location = path::Path::new(&loc);
            let count = zip.len();
            for i in 0..count {
                Self::add_task_progress(&progress_info, "Unpacking Core Assets", &task_file, 1);
                let mut file = zip.by_index(i).unwrap();
                if !file.name().starts_with("assets/") {
                    continue;
                }
                let path = location.join(file.name());
                fs::create_dir_all(path.parent().unwrap()).unwrap();
                let mut out = fs::File::create(path).unwrap();
                io::copy(&mut file, &mut out).unwrap();
            }

            fs::File::create(location.join("steven.assets")).unwrap(); // Marker file
            send.send(true).unwrap();

            fs::remove_file(format!("{}.tmp", RESOURCES_VERSION)).unwrap();
        });
    }

    fn add_task(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, length: u64) {
        let mut info = progress.lock().unwrap();
        info.tasks.push(Task {
            task_name: name.into(),
            task_file: file.into(),
            total: length,
            progress: 0,
        });
    }

    fn add_task_progress(progress: &Arc<Mutex<Progress>>, name: &str, file: &str, prog: u64) {
        let mut progress = progress.lock().unwrap();
        for task in progress.tasks.iter_mut()
            .filter(|v| v.task_file == file)
            .filter(|v| v.task_name == name) {
            task.progress += prog as u64;
        }
    }
}

struct DirPack {
    root: path::PathBuf,
}

impl Pack for DirPack {
    fn open(&self, name: &str) -> Option<Box<io::Read>> {
        match fs::File::open(self.root.join(name)) {
            Ok(val) => Some(Box::new(val)),
            Err(_) => None,
        }
    }
}

struct InternalPack;

impl Pack for InternalPack {
    fn open(&self, name: &str) -> Option<Box<io::Read>> {
        match internal::get_file(name) {
            Some(val) => Some(Box::new(io::Cursor::new(val))),
            None => None,
        }
    }
}

struct ObjectPack {
    objects: HashMap<String, String, BuildHasherDefault<FNVHash>>,
}

impl ObjectPack {
    fn new() -> ObjectPack {
        let loc = format!("./index/{}.json", ASSET_VERSION);
        let location = path::Path::new(&loc);
        let file = fs::File::open(&location).unwrap();
        let index: serde_json::Value = serde_json::from_reader(&file).unwrap();
        let objects = index.find("objects").and_then(|v| v.as_object()).unwrap();
        let mut hash_objs = HashMap::with_hasher(BuildHasherDefault::default());
        for (k, v) in objects {
            hash_objs.insert(k.clone(), v.find("hash").and_then(|v| v.as_string()).unwrap().to_owned());
        }
        ObjectPack {
            objects: hash_objs,
        }
    }
}

impl Pack for ObjectPack {
    fn open(&self, name: &str) -> Option<Box<io::Read>> {
        if !name.starts_with("assets/") {
            return None;
        }
        let name = &name["assets/".len()..];
        if let Some(hash) = self.objects.get(name) {
            let root_location = path::Path::new("./objects/");
            let hash_path = format!("{}/{}", &hash[..2], hash);
            let location = root_location.join(&hash_path);
            match fs::File::open(location) {
                Ok(val) => Some(Box::new(val)),
                Err(_) => None,
            }
        } else {
            None
        }
    }
}

struct ProgressRead<'a, T> {
    read: T,
    progress: &'a Arc<Mutex<Progress>>,
    task_name: String,
    task_file: String,
}

impl <'a, T: io::Read> io::Read for ProgressRead<'a, T> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let size = try!(self.read.read(buf));
        Manager::add_task_progress(self.progress, &self.task_name, &self.task_file, size as u64);
        Ok(size)
    }
}
<|endoftext|>


<commit_before>mod de;
mod ser;

pub use self::de::Deserializer;
pub use self::ser::Serializer;

use serde_base::{Deserialize, Serialize};
use std::io::{Read, Seek, Write};

use Result;
use xml::EventReader;
use xml::EventWriter;

pub fn deserialize<R: Read + Seek, T: Deserialize>(reader: R) -> Result<T> {
    let reader = EventReader::new(reader);
    let mut de = Deserializer::new(reader);
    Deserialize::deserialize(&mut de)
}

pub fn serialize_to_xml<W: Write, T: Serialize>(writer: W, value: &T) -> Result<()> {
    let writer = EventWriter::new(writer);
    let mut ser = Serializer::new(writer);
    value.serialize(&mut ser)
}
<commit_msg>serde::deserialize should support binary and xml encodings.<commit_after>mod de;
mod ser;

pub use self::de::Deserializer;
pub use self::ser::Serializer;

use serde_base::{Deserialize, Serialize};
use std::io::{Read, Seek, Write};

use Result;
use EventReader;
use xml;

pub fn deserialize<R: Read + Seek, T: Deserialize>(reader: R) -> Result<T> {
    let reader = EventReader::new(reader);
    let mut de = Deserializer::new(reader);
    Deserialize::deserialize(&mut de)
}

pub fn serialize_to_xml<W: Write, T: Serialize>(writer: W, value: &T) -> Result<()> {
    let writer = xml::EventWriter::new(writer);
    let mut ser = Serializer::new(writer);
    value.serialize(&mut ser)
}
<|endoftext|>


<commit_before>//! Energy (base unit joule, kg · m² · s⁻²).

quantity! {
    /// Energy (base unit joule, kg · m² · s⁻²).
    quantity: Energy; "energy";
    /// Dimension of energy, L²MT⁻² (base unit joule, kg · m² · s⁻²).
    dimension: ISQ<
        P2,     // length
        P1,     // mass
        N2,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottajoule: prefix!(yotta); "YJ", "yottajoule", "yottajoules";
        @zettajoule: prefix!(zetta); "ZJ", "zettajoule", "zettajoules";
        @exajoule: prefix!(exa); "EJ", "exajoule", "exajoules";
        @petajoule: prefix!(peta); "PJ", "petajoule", "petajoules";
        @terajoule: prefix!(tera); "TJ", "terajoule", "terajoules";
        @gigajoule: prefix!(giga); "GJ", "gigajoule", "gigajoules";
        @megajoule: prefix!(mega); "MJ", "megajoule", "megajoules";
        @kilojoule: prefix!(kilo); "kJ", "kilojoule", "kilojoules";
        @hectojoule: prefix!(hecto); "hJ", "hectojoule", "hectojoules";
        @decajoule: prefix!(deca); "daJ", "decajoule", "decajoules";
        /// Derived unit of energy.
        @joule: prefix!(none); "J", "joule", "joules";
        @decijoule: prefix!(deci); "dJ", "decijoule", "decijoules";
        @centijoule: prefix!(centi); "cJ", "centijoule", "centijoules";
        @millijoule: prefix!(milli); "mJ", "milljoule", "milljoules";
        @microjoule: prefix!(micro); "µJ", "microjoule", "microjoules";
        @nanojoule: prefix!(nano); "nJ", "nanojoule", "nanojoules";
        @picojoule: prefix!(pico); "pJ", "picojoule", "picojoules";
        @femtojoule: prefix!(femto); "fJ", "femtojoule", "femtojoules";
        @attojoule: prefix!(atto); "aJ", "attojoule", "attojoules";
        @zeptojoule: prefix!(zepto); "zJ", "zeptojoule", "zeptojoules";
        @yoctojoule: prefix!(yocto); "yJ", "yoctojoule", "yoctojoules";

        @petawatt_hour: 3.6_E18; "PW · h", "petawatt hour", "petawatt hours";
        @terawatt_hour: 3.6_E15; "TW · h", "terawatt hour", "terawatt hours";
        @gigawatt_hour: 3.6_E12; "GW · h", "gigawatt hour", "gigawatt hours";
        @megawatt_hour: 3.6_E9; "MW · h", "megawatt hour", "megawatt hours";
        @kilowatt_hour: 3.6_E6; "kW · h", "kilowatt hour", "kilowatt hours";
        @hectowatt_hour: 3.6_E5; "hW · h", "hectowatt hour", "hectowatt hours";
        @decawatt_hour: 3.6_E4; "daW · h", "decawatt hour", "decawatt hours";
        @watt_hour: 3.6_E3; "W · h", "watt hour", "watt hours";
        @milliwatt_hour: 3.6_E0; "mW · h", "milliwatt hour", "milliwatt hours";
        @microwatt_hour: 3.6_E-3; "µW · h", "microwatt hour", "microwatt hours";

        @btu_it: 1.055_056_E3; "Btu (IT)", "British thermal unit (IT)",
            "British thermal units (IT)";
        @btu: 1.054_350_E3; "Btu", "British thermal unit", "British thermal units";
        @btu_39: 1.059_67_E3; "Btu₃₉", "British thermal unit (39 °F)",
            "British thermal units (39 °F)";
        @btu_59: 1.054_80_E3; "Btu₅₉", "British thermal unit (59 °F)",
            "British thermal units (59 °F)";
        @btu_60: 1.054_68_E3; "Btu₆₀", "British thermal unit (60 °F)",
            "British thermal units (60 °F)";
        @calorie_it: 4.186_8_E0; "cal (IT)", "calorie (IT)", "calories (IT)";
        @calorie: 4.184_E0; "cal", "calorie", "calories";
        @calorie_15: 4.185_80_E0; "cal₁₅", "calorie (15 °C)", "calories (15 °C)";
        @calorie_20: 4.181_90_E0; "cal₂₀", "calorie (20 °C)", "calories (20 °C)";
        @calorie_it_nutrition: 4.186_8_E3; "Cal (IT)", "Calorie (IT)", "Calories (IT)";
        @calorie_nutrition: 4.184_E3; "Cal", "Calorie", "Calories";
        @electronvolt: 1.602_177_E-19; "eV", "electronvolt", "electronvolts";
        @erg: 1.0_E-7; "erg", "erg", "ergs";
        @foot_poundal: 4.214_011_E-2; "ft · pdl", "foot poundal", "foot poundals";
        @foot_pound: 1.355_818_E0; "ft · lbf", "foot pound-force",
            "foot pounds-force"; // @foot_pound_force
        @kilocalorie_it: 4.186_8_E3; "kcal (IT)", "kilocalorie (IT)", "kilocalories (IT)";
        @kilocalorie: 4.184_E3; "kcal", "kilocalorie", "kilocalories";
        @quad: 1.055_056_E18; "10¹⁵ Btu (IT)", "quad", "quads";
        @therm_ec: 1.055_06_E8; "thm (EC)", "therm (EC)", "therms (EC)";
        @therm_us: 1.054_804_E8; "thm", "therm", "therms";
        @ton_tnt: 4.184_E9; "t of TNT", "ton of TNT", "tons of TNT";
        @watt_second: 1.0_E0; "W · s", "watt second", "watt seconds";
    }
}

#[cfg(test)]
mod tests {
    storage_types! {
        use num::One;
        use si::quantities::*;
        use si::area as a;
        use si::mass as m;
        use si::time as t;
        use si::energy as e;
        use tests::Test;

        #[test]
        fn check_dimension() {
            let _: Energy<V> = Area::new::<a::square_meter>(V::one())
                * Mass::new::<m::kilogram>(V::one())
                / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()));
        }

        #[test]
        fn check_units() {
            test::<m::yottagram, e::zettajoule>();
            test::<m::zettagram, e::exajoule>();
            test::<m::exagram, e::petajoule>();
            test::<m::petagram, e::terajoule>();
            test::<m::teragram, e::gigajoule>();
            test::<m::gigagram, e::megajoule>();
            test::<m::megagram, e::kilojoule>();
            test::<m::kilogram, e::joule>();
            test::<m::decagram, e::centijoule>();
            test::<m::gram, e::millijoule>();
            test::<m::milligram, e::microjoule>();
            test::<m::microgram, e::nanojoule>();
            test::<m::nanogram, e::picojoule>();
            test::<m::picogram, e::femtojoule>();
            test::<m::femtogram, e::attojoule>();
            test::<m::attogram, e::zeptojoule>();
            test::<m::zeptogram, e::yoctojoule>();

            fn test<M: m::Conversion<V>, E: e::Conversion<V>>() {
                Test::assert_approx_eq(&Energy::new::<E>(V::one()),
                    &(Area::new::<a::square_meter>(V::one())
                        * Mass::new::<M>(V::one())
                        / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()))));
            }
        }
    }
}
<commit_msg>Added prefixes for electronvolt (eV).<commit_after>//! Energy (base unit joule, kg · m² · s⁻²).

quantity! {
    /// Energy (base unit joule, kg · m² · s⁻²).
    quantity: Energy; "energy";
    /// Dimension of energy, L²MT⁻² (base unit joule, kg · m² · s⁻²).
    dimension: ISQ<
        P2,     // length
        P1,     // mass
        N2,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        Z0,     // amount of substance
        Z0>;    // luminous intensity
    units {
        @yottajoule: prefix!(yotta); "YJ", "yottajoule", "yottajoules";
        @zettajoule: prefix!(zetta); "ZJ", "zettajoule", "zettajoules";
        @exajoule: prefix!(exa); "EJ", "exajoule", "exajoules";
        @petajoule: prefix!(peta); "PJ", "petajoule", "petajoules";
        @terajoule: prefix!(tera); "TJ", "terajoule", "terajoules";
        @gigajoule: prefix!(giga); "GJ", "gigajoule", "gigajoules";
        @megajoule: prefix!(mega); "MJ", "megajoule", "megajoules";
        @kilojoule: prefix!(kilo); "kJ", "kilojoule", "kilojoules";
        @hectojoule: prefix!(hecto); "hJ", "hectojoule", "hectojoules";
        @decajoule: prefix!(deca); "daJ", "decajoule", "decajoules";
        /// Derived unit of energy.
        @joule: prefix!(none); "J", "joule", "joules";
        @decijoule: prefix!(deci); "dJ", "decijoule", "decijoules";
        @centijoule: prefix!(centi); "cJ", "centijoule", "centijoules";
        @millijoule: prefix!(milli); "mJ", "milljoule", "milljoules";
        @microjoule: prefix!(micro); "µJ", "microjoule", "microjoules";
        @nanojoule: prefix!(nano); "nJ", "nanojoule", "nanojoules";
        @picojoule: prefix!(pico); "pJ", "picojoule", "picojoules";
        @femtojoule: prefix!(femto); "fJ", "femtojoule", "femtojoules";
        @attojoule: prefix!(atto); "aJ", "attojoule", "attojoules";
        @zeptojoule: prefix!(zepto); "zJ", "zeptojoule", "zeptojoules";
        @yoctojoule: prefix!(yocto); "yJ", "yoctojoule", "yoctojoules";

        @petawatt_hour: 3.6_E18; "PW · h", "petawatt hour", "petawatt hours";
        @terawatt_hour: 3.6_E15; "TW · h", "terawatt hour", "terawatt hours";
        @gigawatt_hour: 3.6_E12; "GW · h", "gigawatt hour", "gigawatt hours";
        @megawatt_hour: 3.6_E9; "MW · h", "megawatt hour", "megawatt hours";
        @kilowatt_hour: 3.6_E6; "kW · h", "kilowatt hour", "kilowatt hours";
        @hectowatt_hour: 3.6_E5; "hW · h", "hectowatt hour", "hectowatt hours";
        @decawatt_hour: 3.6_E4; "daW · h", "decawatt hour", "decawatt hours";
        @watt_hour: 3.6_E3; "W · h", "watt hour", "watt hours";
        @milliwatt_hour: 3.6_E0; "mW · h", "milliwatt hour", "milliwatt hours";
        @microwatt_hour: 3.6_E-3; "µW · h", "microwatt hour", "microwatt hours";

        @petaelectronvolt: 1.602_176_634_E-4; "PeV", "petaelectronvolt", "petaelectronvolts";
        @teraelectronvolt: 1.602_176_634_E-7; "TeV", "teraelectronvolt", "teraelectronvolts";
        @gigaelectronvolt: 1.602_176_634_E-10; "GeV", "gigaelectronvolt", "gigaelectronvolts";
        @megaelectronvolt: 1.602_176_634_E-13; "MeV", "megaelectronvolt", "megaelectronvolts";
        @kiloelectronvolt: 1.602_176_634_E-16; "keV", "kiloelectronvolt", "kiloelectronvolts";
        @hectoelectronvolt: 1.602_176_634_E-17; "heV", "hectoelectronvolt", "hectoelectronvolts";
        @decaelectronvolt: 1.602_176_634_E-18; "daeV", "decaelectronvolt", "decaelectronvolts";
        @electronvolt: 1.602_176_634_E-19; "eV", "electronvolt", "electronvolts";

        @btu_it: 1.055_056_E3; "Btu (IT)", "British thermal unit (IT)",
            "British thermal units (IT)";
        @btu: 1.054_350_E3; "Btu", "British thermal unit", "British thermal units";
        @btu_39: 1.059_67_E3; "Btu₃₉", "British thermal unit (39 °F)",
            "British thermal units (39 °F)";
        @btu_59: 1.054_80_E3; "Btu₅₉", "British thermal unit (59 °F)",
            "British thermal units (59 °F)";
        @btu_60: 1.054_68_E3; "Btu₆₀", "British thermal unit (60 °F)",
            "British thermal units (60 °F)";
        @calorie_it: 4.186_8_E0; "cal (IT)", "calorie (IT)", "calories (IT)";
        @calorie: 4.184_E0; "cal", "calorie", "calories";
        @calorie_15: 4.185_80_E0; "cal₁₅", "calorie (15 °C)", "calories (15 °C)";
        @calorie_20: 4.181_90_E0; "cal₂₀", "calorie (20 °C)", "calories (20 °C)";
        @calorie_it_nutrition: 4.186_8_E3; "Cal (IT)", "Calorie (IT)", "Calories (IT)";
        @calorie_nutrition: 4.184_E3; "Cal", "Calorie", "Calories";
        @erg: 1.0_E-7; "erg", "erg", "ergs";
        @foot_poundal: 4.214_011_E-2; "ft · pdl", "foot poundal", "foot poundals";
        @foot_pound: 1.355_818_E0; "ft · lbf", "foot pound-force",
            "foot pounds-force"; // @foot_pound_force
        @kilocalorie_it: 4.186_8_E3; "kcal (IT)", "kilocalorie (IT)", "kilocalories (IT)";
        @kilocalorie: 4.184_E3; "kcal", "kilocalorie", "kilocalories";
        @quad: 1.055_056_E18; "10¹⁵ Btu (IT)", "quad", "quads";
        @therm_ec: 1.055_06_E8; "thm (EC)", "therm (EC)", "therms (EC)";
        @therm_us: 1.054_804_E8; "thm", "therm", "therms";
        @ton_tnt: 4.184_E9; "t of TNT", "ton of TNT", "tons of TNT";
        @watt_second: 1.0_E0; "W · s", "watt second", "watt seconds";
    }
}

#[cfg(test)]
mod tests {
    storage_types! {
        use num::One;
        use si::quantities::*;
        use si::area as a;
        use si::mass as m;
        use si::time as t;
        use si::energy as e;
        use tests::Test;

        #[test]
        fn check_dimension() {
            let _: Energy<V> = Area::new::<a::square_meter>(V::one())
                * Mass::new::<m::kilogram>(V::one())
                / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()));
        }

        #[test]
        fn check_units() {
            test::<m::yottagram, e::zettajoule>();
            test::<m::zettagram, e::exajoule>();
            test::<m::exagram, e::petajoule>();
            test::<m::petagram, e::terajoule>();
            test::<m::teragram, e::gigajoule>();
            test::<m::gigagram, e::megajoule>();
            test::<m::megagram, e::kilojoule>();
            test::<m::kilogram, e::joule>();
            test::<m::decagram, e::centijoule>();
            test::<m::gram, e::millijoule>();
            test::<m::milligram, e::microjoule>();
            test::<m::microgram, e::nanojoule>();
            test::<m::nanogram, e::picojoule>();
            test::<m::picogram, e::femtojoule>();
            test::<m::femtogram, e::attojoule>();
            test::<m::attogram, e::zeptojoule>();
            test::<m::zeptogram, e::yoctojoule>();

            fn test<M: m::Conversion<V>, E: e::Conversion<V>>() {
                Test::assert_approx_eq(&Energy::new::<E>(V::one()),
                    &(Area::new::<a::square_meter>(V::one())
                        * Mass::new::<M>(V::one())
                        / (Time::new::<t::second>(V::one()) * Time::new::<t::second>(V::one()))));
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust core library

#![crate_id = "core#0.11-pre"]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://static.rust-lang.org/doc/master")]

#![no_std]
#![feature(globs, macro_rules, managed_boxes, phase)]
#![deny(missing_doc)]

#[cfg(test)] extern crate realcore = "core";
#[cfg(test)] extern crate libc;
#[cfg(test)] extern crate native;
#[phase(syntax, link)] #[cfg(test)] extern crate realstd = "std";
#[phase(syntax, link)] #[cfg(test)] extern crate log;

#[cfg(test)] pub use cmp = realcore::cmp;
#[cfg(test)] pub use kinds = realcore::kinds;
#[cfg(test)] pub use ops = realcore::ops;
#[cfg(test)] pub use owned = realcore::owned;
#[cfg(test)] pub use ty = realcore::ty;

#[cfg(not(test))]
mod macros;

#[path = "num/float_macros.rs"] mod float_macros;
#[path = "num/int_macros.rs"]   mod int_macros;
#[path = "num/uint_macros.rs"]  mod uint_macros;

#[path = "num/int.rs"]  pub mod int;
#[path = "num/i8.rs"]   pub mod i8;
#[path = "num/i16.rs"]  pub mod i16;
#[path = "num/i32.rs"]  pub mod i32;
#[path = "num/i64.rs"]  pub mod i64;

#[path = "num/uint.rs"] pub mod uint;
#[path = "num/u8.rs"]   pub mod u8;
#[path = "num/u16.rs"]  pub mod u16;
#[path = "num/u32.rs"]  pub mod u32;
#[path = "num/u64.rs"]  pub mod u64;

#[path = "num/f32.rs"]   pub mod f32;
#[path = "num/f64.rs"]   pub mod f64;

pub mod num;

/* The libcore prelude, not as all-encompassing as the libstd prelude */

pub mod prelude;

/* Core modules for ownership management */

pub mod cast;
pub mod intrinsics;
pub mod mem;
pub mod ptr;

/* Core language traits */

#[cfg(not(test))] pub mod kinds;
#[cfg(not(test))] pub mod ops;
#[cfg(not(test))] pub mod ty;
#[cfg(not(test))] pub mod cmp;
#[cfg(not(test))] pub mod owned;
pub mod clone;
pub mod default;
pub mod container;

/* Core types and methods on primitives */

mod unicode;
mod unit;
pub mod any;
pub mod bool;
pub mod cell;
pub mod char;
pub mod finally;
pub mod iter;
pub mod option;
pub mod raw;
pub mod result;
pub mod slice;
pub mod str;
pub mod tuple;

mod failure;

// FIXME: this module should not exist. Once owned allocations are no longer a
//        language type, this module can move outside to the owned allocation
//        crate.
mod should_not_exist;

mod std {
    pub use clone;
    pub use cmp;

    #[cfg(test)] pub use realstd::fmt;    // needed for fail!()
    #[cfg(test)] pub use realstd::rt;     // needed for fail!()
    #[cfg(test)] pub use realstd::option; // needed for assert!()
    #[cfg(test)] pub use realstd::os;     // needed for tests
}
<commit_msg>core: Allow kinds derivings<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust core library

#![crate_id = "core#0.11-pre"]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://static.rust-lang.org/doc/master")]

#![no_std]
#![feature(globs, macro_rules, managed_boxes, phase)]
#![deny(missing_doc)]

#[cfg(test)] extern crate realcore = "core";
#[cfg(test)] extern crate libc;
#[cfg(test)] extern crate native;
#[phase(syntax, link)] #[cfg(test)] extern crate realstd = "std";
#[phase(syntax, link)] #[cfg(test)] extern crate log;

#[cfg(test)] pub use cmp = realcore::cmp;
#[cfg(test)] pub use kinds = realcore::kinds;
#[cfg(test)] pub use ops = realcore::ops;
#[cfg(test)] pub use owned = realcore::owned;
#[cfg(test)] pub use ty = realcore::ty;

#[cfg(not(test))]
mod macros;

#[path = "num/float_macros.rs"] mod float_macros;
#[path = "num/int_macros.rs"]   mod int_macros;
#[path = "num/uint_macros.rs"]  mod uint_macros;

#[path = "num/int.rs"]  pub mod int;
#[path = "num/i8.rs"]   pub mod i8;
#[path = "num/i16.rs"]  pub mod i16;
#[path = "num/i32.rs"]  pub mod i32;
#[path = "num/i64.rs"]  pub mod i64;

#[path = "num/uint.rs"] pub mod uint;
#[path = "num/u8.rs"]   pub mod u8;
#[path = "num/u16.rs"]  pub mod u16;
#[path = "num/u32.rs"]  pub mod u32;
#[path = "num/u64.rs"]  pub mod u64;

#[path = "num/f32.rs"]   pub mod f32;
#[path = "num/f64.rs"]   pub mod f64;

pub mod num;

/* The libcore prelude, not as all-encompassing as the libstd prelude */

pub mod prelude;

/* Core modules for ownership management */

pub mod cast;
pub mod intrinsics;
pub mod mem;
pub mod ptr;

/* Core language traits */

#[cfg(not(test))] pub mod kinds;
#[cfg(not(test))] pub mod ops;
#[cfg(not(test))] pub mod ty;
#[cfg(not(test))] pub mod cmp;
#[cfg(not(test))] pub mod owned;
pub mod clone;
pub mod default;
pub mod container;

/* Core types and methods on primitives */

mod unicode;
mod unit;
pub mod any;
pub mod bool;
pub mod cell;
pub mod char;
pub mod finally;
pub mod iter;
pub mod option;
pub mod raw;
pub mod result;
pub mod slice;
pub mod str;
pub mod tuple;

mod failure;

// FIXME: this module should not exist. Once owned allocations are no longer a
//        language type, this module can move outside to the owned allocation
//        crate.
mod should_not_exist;

mod std {
    pub use clone;
    pub use cmp;
    pub use kinds;

    #[cfg(test)] pub use realstd::fmt;    // needed for fail!()
    #[cfg(test)] pub use realstd::rt;     // needed for fail!()
    #[cfg(test)] pub use realstd::option; // needed for assert!()
    #[cfg(test)] pub use realstd::os;     // needed for tests
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;

use ansi_term::{ANSIString, Color, Style};

use util::Shell;

/// Representation of config options that all modules have
#[derive(Clone)]
pub struct ModuleOptions<'a> {
    /// String to display to the left of the content
    pub padding_left: &'a str,
    /// String to display to the right of the content
    pub padding_right: &'a str,
    /// String to print out after the content and right padding
    pub separator: &'a str,
    /// Background color, foreground color, etc.
    pub style: ModuleStyle,
}

/// Representation of how to style a module
#[derive(Clone)]
pub struct ModuleStyle {
    /// Color behind the text
    pub background: Option<Color>,
    /// Color of the text
    pub foreground: Option<Color>,
    /// Combination of attributes like bold, italicized, etc.
    pub text_properties: Option<Style>,
}

/// Formats a string with the given `ModuleOptions` for a specific
/// `Shell`.
///
/// # Parameters
///
/// - `s` - the contents of the module to be formatted
/// - `options` - the background, foreground, padding, etc. to apply
/// - `next_bg` - the background color, if any, of the next visible module
/// - `shell` - the type of shell to format the string for
pub fn format_for_module(s: &str,
                         options: &ModuleOptions,
                         next_bg: Option<Color>,
                         shell: Shell)
                         -> ANSIString<'static> {

    let style = style_from_modulestyle(&options.style);

    // Each shell keeps track of the number of characters that make up
    // the prompt. The ANSI escape-sequences that color the text will
    // be accidentally included in this length *unless* we prefix and
    // suffix them with these shell-specific escape-sequences. We
    // don't want the shell to mistakenly think there's fewer
    // characters remaining on the current line than there actually
    // are.
    let (len_esc_prefix, len_esc_suffix) = match shell {
        Shell::Bash => ("\\[", "\\]"),
        Shell::Zsh => ("%{", "%}"),
    };

    // Every time there is a color escape-sequence, it must be
    // surrounded by the length escape-codes. We also include the
    // padding before and after the content.
    let content = format!("{}{}{}{}{}{}{}{}{}",
                    len_esc_prefix,
                    style.prefix(),
                    len_esc_suffix,
                    options.padding_left,
                    s,
                    options.padding_right,
                    len_esc_prefix,
                    style.suffix(),
                    len_esc_suffix,
    );

    // We must format the separator differently depending on whether
    // there exists a visible module after this one or not.
    let separator_style = ModuleStyle {
        foreground: options.style.background,
        background: next_bg,
        text_properties: options.style.text_properties,
    };
    let separator_style = style_from_modulestyle(&separator_style);
    let separator = format!("{}{}{}{}{}{}{}",
                            len_esc_prefix,
                            separator_style.prefix(),
                            len_esc_suffix,
                            options.separator,
                            len_esc_prefix,
                            separator_style.suffix(),
                            len_esc_suffix);

    ANSIString::from(format!("{}{}", content, separator))
}

/// Converts a `ModuleStyle` into an `ansi_term::Style`.
fn style_from_modulestyle(s: &ModuleStyle) -> Style {
    let mut style = s.text_properties.unwrap_or_default();
    if let Some(bg) = s.background {
        style = style.on(bg);
    }
    if let Some(fg) = s.foreground {
        style = style.fg(fg);
    }
    style
}

// We use methods of the form "try_*_from_*" because the user should
// just be able to specify (in their config) what color/style they
// want, and we have to be able to parse what they say into a
// meaningful type.

/// Attempts to convert a string into an `ansi_term::Color`.
///
/// Returns a `ConvertError::NoSuchMatch` if the provided string
/// doesn't match any of the colors defined in crate `ansi_term`.
///
/// # Examples
/// ```
/// assert_eq!(try_color_from_str("black"), Ok(Color::Black));
/// assert_eq!(try_color_from_str("green"), Ok(Color::Green));
/// assert_eq!(try_color_from_str("turquoise"), Err(ConvertError::NoSuchMatch));
/// ```
pub fn try_color_from_str(s: &str) -> Result<Color, ConvertError> {
    match s.to_lowercase().as_ref() {
        "black" => Ok(Color::Black),
        "red" => Ok(Color::Red),
        "green" => Ok(Color::Green),
        "yellow" => Ok(Color::Yellow),
        "blue" => Ok(Color::Blue),
        "purple" => Ok(Color::Purple),
        "cyan" => Ok(Color::Cyan),
        "white" => Ok(Color::White),
        _ => Err(ConvertError::NoSuchMatch),
    }
}

/// Attempts to convert a string into an `ansi_term::Color::RGB`.
///
/// Returns a `ConvertError::InvalidForm` if the provided string is
/// not a sequence of 3 `u8`s, separated by commas.
///
/// # Examples
/// ```
/// assert_eq!(try_rgb_from_str("(14, 76, 1)"), Ok(Color::RGB(14, 76, 1)));
/// assert_eq!(try_rgb_from_str("0, 100, 0"), Ok(Color::RGB(0, 100, 0)));
/// assert_eq!(try_rgb_from_str("1000, b, c, -1"), Err(ConvertError::InvalidForm));
/// ```
pub fn try_rgb_from_str(s: &str) -> Result<Color, ConvertError> {
    // Strip out non-integer characters
    let cleaned: Vec<String> =
        s.split(',').map(|i| i.replace(|j| j == '(' || j == ')' || j == ' ', "")).collect();

    // We want to immediately return an Error if a conversion fails
    let mut ints: Vec<u8> = Vec::new();
    for i in &cleaned {
        ints.push(i.parse::<u8>()?);
    }

    // Note: somewhat-malformed input, like "()()(0, 0, 0)" can still
    // get through, but we can still make "sense" of it so it's good
    // to go.
    if ints.len() != 3 {
        Err(ConvertError::InvalidForm)
    } else {
        Ok(Color::RGB(ints[0], ints[1], ints[2]))
    }
}

// NOTE: We do not need a try_color_from_u8 OR a try_rgb_from_vec.
// The implementation would just look like:
// pub fn try_color_from_u8(i: u8) -> Color { Color::u8(i) }
// It would always succeed due to Rust's type system.

#[derive(Debug, PartialEq)]
/// Error type for when a conversion due to parsing fails
pub enum ConvertError {
    /// Input doesn't correspond to a valid result
    NoSuchMatch,
    /// Input is malformed and cannot be parsed
    InvalidForm,
}

impl fmt::Display for ConvertError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl Error for ConvertError {
    fn description(&self) -> &str {
        match *self {
            ConvertError::NoSuchMatch => "no match was found for the provided input",
            ConvertError::InvalidForm => "provided input was malformed and could not be parsed",
        }
    }
}

// So that we can use try!() and ? to return early if we encounter a
// ParseIntError
impl From<ParseIntError> for ConvertError {
    fn from(_: ParseIntError) -> ConvertError {
        ConvertError::InvalidForm
    }
}

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

    #[test]
    fn test_format_for_module() {
        const CONTENT: &'static str = "Hello";
        const PADDING: &'static str = " ";
        const SEPARATOR: &'static str = ">";

        let options = ModuleOptions {
            padding_left: PADDING,
            padding_right: PADDING,
            separator: SEPARATOR,
            style: ModuleStyle {
                background: Some(Color::Blue),
                foreground: Some(Color::White),
                text_properties: Some(Style::default().bold()),
            },
        };

        let formatted_string = format_for_module(CONTENT, &options, None, Shell::Bash);
        assert_eq!(format!("\\[\x1B[1;44;37m\\]{}{}{}\\[\x1B[0m\\]\\[\x1B[1;34m\\]{}\\[\x1B[0m\\]",
                           PADDING,
                           CONTENT,
                           PADDING,
                           SEPARATOR),
                   format!("{}", formatted_string))
    }

    #[test]
    fn test_style_from_modulestyle() {
        const CONTENT: &'static str = "Hello";

        struct Test {
            style: ModuleStyle,
            expected: String,
        }

        let tests = [// Normal text, no bg, custom fg
                     Test {
                         style: ModuleStyle {
                             background: None,
                             foreground: Some(Color::White),
                             text_properties: None,
                         },
                         expected: format!("\x1B[37m{}\x1B[0m", CONTENT),
                     },
                     // Bold text, no fg, custom bg
                     Test {
                         style: ModuleStyle {
                             background: Some(Color::Blue),
                             foreground: None,
                             text_properties: Some(Style::default().bold()),
                         },
                         expected: format!("\x1B[1;44m{}\x1B[0m", CONTENT),
                     },
                     // Underlined text, custom bg and fg
                     Test {
                         style: ModuleStyle {
                             background: Some(Color::Blue),
                             foreground: Some(Color::White),
                             text_properties: Some(Style::default().underline()),
                         },
                         expected: format!("\x1B[4;44;37m{}\x1B[0m", CONTENT),
                     },
                     // Normal text, no bg nor fg
                     Test {
                         style: ModuleStyle {
                             background: None,
                             foreground: None,
                             text_properties: None,
                         },
                         expected: format!("{}", CONTENT),
                     }];

        for test in &tests {
            let result = format!("{}", style_from_modulestyle(&test.style).paint(CONTENT));
            assert_eq!(result, test.expected);
        }
    }

    #[test]
    fn test_color_try_from_str() {
        assert_eq!(try_color_from_str("blue"), Ok(Color::Blue));
        assert_eq!(try_color_from_str("teal"), Err(ConvertError::NoSuchMatch));
    }

    #[test]
    fn test_rgb_try_from_str() {
        // Valid inputs
        assert_eq!(try_rgb_from_str("(0, 0, 0)"), Ok(Color::RGB(0, 0, 0)));
        assert_eq!(try_rgb_from_str("(255, 255, 255)"),
                   Ok(Color::RGB(255, 255, 255)));

        // Questionable inputs (should still work)
        assert_eq!(try_rgb_from_str("(0, 0, 0))"), Ok(Color::RGB(0, 0, 0)));

        // Improperly formed ("too many" inputs)
        assert_eq!(try_rgb_from_str("(0, 0, 0,)"),
                   Err(ConvertError::InvalidForm));

        // Too few inputs
        assert_eq!(try_rgb_from_str("(0, 0)"), Err(ConvertError::InvalidForm));

        // Inputs aren't u8's
        assert_eq!(try_rgb_from_str("(1000, 0, 0)"),
                   Err(ConvertError::InvalidForm));
        assert_eq!(try_rgb_from_str("(0, 0, -1)"),
                   Err(ConvertError::InvalidForm));
    }
}
<commit_msg>feat: implement try_fixed_from_str<commit_after>use std::convert::From;
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;

use ansi_term::{ANSIString, Color, Style};

use util::Shell;

/// Representation of config options that all modules have
#[derive(Clone)]
pub struct ModuleOptions<'a> {
    /// String to display to the left of the content
    pub padding_left: &'a str,
    /// String to display to the right of the content
    pub padding_right: &'a str,
    /// String to print out after the content and right padding
    pub separator: &'a str,
    /// Background color, foreground color, etc.
    pub style: ModuleStyle,
}

/// Representation of how to style a module
#[derive(Clone)]
pub struct ModuleStyle {
    /// Color behind the text
    pub background: Option<Color>,
    /// Color of the text
    pub foreground: Option<Color>,
    /// Combination of attributes like bold, italicized, etc.
    pub text_properties: Option<Style>,
}

/// Formats a string with the given `ModuleOptions` for a specific
/// `Shell`.
///
/// # Parameters
///
/// - `s` - the contents of the module to be formatted
/// - `options` - the background, foreground, padding, etc. to apply
/// - `next_bg` - the background color, if any, of the next visible module
/// - `shell` - the type of shell to format the string for
pub fn format_for_module(s: &str,
                         options: &ModuleOptions,
                         next_bg: Option<Color>,
                         shell: Shell)
                         -> ANSIString<'static> {

    let style = style_from_modulestyle(&options.style);

    // Each shell keeps track of the number of characters that make up
    // the prompt. The ANSI escape-sequences that color the text will
    // be accidentally included in this length *unless* we prefix and
    // suffix them with these shell-specific escape-sequences. We
    // don't want the shell to mistakenly think there's fewer
    // characters remaining on the current line than there actually
    // are.
    let (len_esc_prefix, len_esc_suffix) = match shell {
        Shell::Bash => ("\\[", "\\]"),
        Shell::Zsh => ("%{", "%}"),
    };

    // Every time there is a color escape-sequence, it must be
    // surrounded by the length escape-codes. We also include the
    // padding before and after the content.
    let content = format!("{}{}{}{}{}{}{}{}{}",
                    len_esc_prefix,
                    style.prefix(),
                    len_esc_suffix,
                    options.padding_left,
                    s,
                    options.padding_right,
                    len_esc_prefix,
                    style.suffix(),
                    len_esc_suffix,
    );

    // We must format the separator differently depending on whether
    // there exists a visible module after this one or not.
    let separator_style = ModuleStyle {
        foreground: options.style.background,
        background: next_bg,
        text_properties: options.style.text_properties,
    };
    let separator_style = style_from_modulestyle(&separator_style);
    let separator = format!("{}{}{}{}{}{}{}",
                            len_esc_prefix,
                            separator_style.prefix(),
                            len_esc_suffix,
                            options.separator,
                            len_esc_prefix,
                            separator_style.suffix(),
                            len_esc_suffix);

    ANSIString::from(format!("{}{}", content, separator))
}

/// Converts a `ModuleStyle` into an `ansi_term::Style`.
fn style_from_modulestyle(s: &ModuleStyle) -> Style {
    let mut style = s.text_properties.unwrap_or_default();
    if let Some(bg) = s.background {
        style = style.on(bg);
    }
    if let Some(fg) = s.foreground {
        style = style.fg(fg);
    }
    style
}

// We use methods of the form "try_*_from_*" because the user should
// just be able to specify (in their config) what color/style they
// want, and we have to be able to parse what they say into a
// meaningful type.

/// Attempts to convert a string into an `ansi_term::Color`.
///
/// Returns a `ConvertError::NoSuchMatch` if the provided string
/// doesn't match any of the colors defined in crate `ansi_term`.
///
/// # Examples
///
/// ```
/// assert_eq!(try_color_from_str("black"), Ok(Color::Black));
/// assert_eq!(try_color_from_str("green"), Ok(Color::Green));
/// assert_eq!(try_color_from_str("turquoise"), Err(ConvertError::NoSuchMatch));
/// ```
pub fn try_color_from_str(s: &str) -> Result<Color, ConvertError> {
    match s.to_lowercase().as_ref() {
        "black" => Ok(Color::Black),
        "red" => Ok(Color::Red),
        "green" => Ok(Color::Green),
        "yellow" => Ok(Color::Yellow),
        "blue" => Ok(Color::Blue),
        "purple" => Ok(Color::Purple),
        "cyan" => Ok(Color::Cyan),
        "white" => Ok(Color::White),
        _ => Err(ConvertError::NoSuchMatch),
    }
}

/// Attempts to convert a string into an `ansi_term::Color::RGB`.
///
/// Returns a `ConvertError::InvalidForm` if the provided string is
/// not a sequence of 3 `u8`s, separated by commas.
///
/// # Examples
///
/// ```
/// assert_eq!(try_rgb_from_str("(14, 76, 1)"), Ok(Color::RGB(14, 76, 1)));
/// assert_eq!(try_rgb_from_str("0, 100, 0"), Ok(Color::RGB(0, 100, 0)));
/// assert_eq!(try_rgb_from_str("1000, b, c, -1"), Err(ConvertError::InvalidForm));
/// ```
pub fn try_rgb_from_str(s: &str) -> Result<Color, ConvertError> {
    // Strip out non-integer characters
    let cleaned: Vec<String> =
        s.split(',').map(|i| i.replace(|j| j == '(' || j == ')' || j == ' ', "")).collect();

    // We want to immediately return an Error if a conversion fails
    let mut ints: Vec<u8> = Vec::new();
    for i in &cleaned {
        ints.push(i.parse::<u8>()?);
    }

    // Note: somewhat-malformed input, like "()()(0, 0, 0)" can still
    // get through, but we can still make "sense" of it so it's good
    // to go.
    if ints.len() != 3 {
        Err(ConvertError::InvalidForm)
    } else {
        Ok(Color::RGB(ints[0], ints[1], ints[2]))
    }
}

/// Attempts to convert a string into an `ansi_term::Color::Fixed`.
///
/// Returns a `ConvertError::InvalidForm` if the provided string fails
/// to coerce into a `u8`.
///
/// # Examples
///
/// ```
/// assert_eq!(try_fixed_from_str("63"), Ok(Color::Fixed(63)));
/// assert_eq!(try_fixed_from_str("257"), Err(ConvertError::InvalidForm));
/// ```
pub fn try_fixed_from_str(s: &str) -> Result<Color, ConvertError> {
    Ok(Color::Fixed(s.parse::<u8>()?))
}

// NOTE: We do not need a try_color_from_u8 OR a try_rgb_from_vec.
// The implementation would just look like:
// pub fn try_color_from_u8(i: u8) -> Color { Color::u8(i) }
// It would always succeed due to Rust's type system.

#[derive(Debug, PartialEq)]
/// Error type for when a conversion due to parsing fails
pub enum ConvertError {
    /// Input doesn't correspond to a valid result
    NoSuchMatch,
    /// Input is malformed and cannot be parsed
    InvalidForm,
}

impl fmt::Display for ConvertError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl Error for ConvertError {
    fn description(&self) -> &str {
        match *self {
            ConvertError::NoSuchMatch => "no match was found for the provided input",
            ConvertError::InvalidForm => "provided input was malformed and could not be parsed",
        }
    }
}

// So that we can use try!() and ? to return early if we encounter a
// ParseIntError
impl From<ParseIntError> for ConvertError {
    fn from(_: ParseIntError) -> ConvertError {
        ConvertError::InvalidForm
    }
}

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

    #[test]
    fn test_format_for_module() {
        const CONTENT: &'static str = "Hello";
        const PADDING: &'static str = " ";
        const SEPARATOR: &'static str = ">";

        let options = ModuleOptions {
            padding_left: PADDING,
            padding_right: PADDING,
            separator: SEPARATOR,
            style: ModuleStyle {
                background: Some(Color::Blue),
                foreground: Some(Color::White),
                text_properties: Some(Style::default().bold()),
            },
        };

        let formatted_string = format_for_module(CONTENT, &options, None, Shell::Bash);
        assert_eq!(format!("\\[\x1B[1;44;37m\\]{}{}{}\\[\x1B[0m\\]\\[\x1B[1;34m\\]{}\\[\x1B[0m\\]",
                           PADDING,
                           CONTENT,
                           PADDING,
                           SEPARATOR),
                   format!("{}", formatted_string))
    }

    #[test]
    fn test_style_from_modulestyle() {
        const CONTENT: &'static str = "Hello";

        struct Test {
            style: ModuleStyle,
            expected: String,
        }

        let tests = [// Normal text, no bg, custom fg
                     Test {
                         style: ModuleStyle {
                             background: None,
                             foreground: Some(Color::White),
                             text_properties: None,
                         },
                         expected: format!("\x1B[37m{}\x1B[0m", CONTENT),
                     },
                     // Bold text, no fg, custom bg
                     Test {
                         style: ModuleStyle {
                             background: Some(Color::Blue),
                             foreground: None,
                             text_properties: Some(Style::default().bold()),
                         },
                         expected: format!("\x1B[1;44m{}\x1B[0m", CONTENT),
                     },
                     // Underlined text, custom bg and fg
                     Test {
                         style: ModuleStyle {
                             background: Some(Color::Blue),
                             foreground: Some(Color::White),
                             text_properties: Some(Style::default().underline()),
                         },
                         expected: format!("\x1B[4;44;37m{}\x1B[0m", CONTENT),
                     },
                     // Normal text, no bg nor fg
                     Test {
                         style: ModuleStyle {
                             background: None,
                             foreground: None,
                             text_properties: None,
                         },
                         expected: format!("{}", CONTENT),
                     }];

        for test in &tests {
            let result = format!("{}", style_from_modulestyle(&test.style).paint(CONTENT));
            assert_eq!(result, test.expected);
        }
    }

    #[test]
    fn test_try_color_from_str() {
        // Corresponds to one of the colors defined in `ansi_term`
        assert_eq!(try_color_from_str("blue"), Ok(Color::Blue));

        // Not part of the `ansi_term::Color` enum
        assert_eq!(try_color_from_str("teal"), Err(ConvertError::NoSuchMatch));
    }

    #[test]
    fn test_try_fixed_from_str() {
        // Valid inputs
        assert_eq!(try_fixed_from_str("0"), Ok(Color::Fixed(0)));
        assert_eq!(try_fixed_from_str("100"), Ok(Color::Fixed(100)));

        // Inputs that can't be parsed to u8
        assert_eq!(try_fixed_from_str("256"), Err(ConvertError::InvalidForm));
        assert_eq!(try_fixed_from_str("-1"), Err(ConvertError::InvalidForm));
    }

    #[test]
    fn test_try_rgb_from_str() {
        // Valid inputs
        assert_eq!(try_rgb_from_str("(0, 0, 0)"), Ok(Color::RGB(0, 0, 0)));
        assert_eq!(try_rgb_from_str("(255, 255, 255)"),
                   Ok(Color::RGB(255, 255, 255)));

        // Questionable inputs (should still work)
        assert_eq!(try_rgb_from_str("(0, 0, 0))"), Ok(Color::RGB(0, 0, 0)));

        // Improperly formed ("too many" inputs)
        assert_eq!(try_rgb_from_str("(0, 0, 0,)"),
                   Err(ConvertError::InvalidForm));

        // Too few inputs
        assert_eq!(try_rgb_from_str("(0, 0)"), Err(ConvertError::InvalidForm));

        // Inputs aren't u8's
        assert_eq!(try_rgb_from_str("(1000, 0, 0)"),
                   Err(ConvertError::InvalidForm));
        assert_eq!(try_rgb_from_str("(0, 0, -1)"),
                   Err(ConvertError::InvalidForm));
    }
}
<|endoftext|>


<commit_before>/*! Provides packet encoding and decoding functionality, as well as the packet enum.

This module does not handle the checksum.  If it did, it would be incredibly difficult to write Fastnet tests.*/
pub use self::encoder::*;
pub use self::decoder::*;
use uuid;

mod encoder;
mod encoder_tests;
mod decoder;
mod decoder_tests;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Packet {
    //Status request and response (channel -1)
    StatusRequest(StatusRequest),
    StatusResponse(StatusResponse),

    //Connection handshake (also channel -1).
    Connect(uuid::Uuid),
    Connected(uuid::Uuid),
    Aborted(String),

    //Heartbeat (channel -2).
    Heartbeat{counter: u64, sent: u64, received: u64},

    Echo{endpoint: uuid::Uuid, uuid: uuid::Uuid},
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StatusRequest {
    FastnetQuery,
    VersionQuery,
    ExtensionQuery(String),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StatusResponse {
    FastnetResponse(bool),
    VersionResponse(String),
    ExtensionResponse {name: String, supported: bool},
}

pub const CONNECTION_CHANNEL: i16 = -1;
pub const HEARTBEAT_CHANNEL: i16 = -2;
pub const ECHO_CHANNEL: i16 = -3;

pub const STATUS_REQUEST_SPECIFIER: u8 = 0;
pub const STATUS_RESPONSE_SPECIFIER: u8 = 1;
pub const CONNECT_SPECIFIER: u8 = 2;
pub const CONNECTED_SPECIFIER: u8 = 3;
pub const ABORTED_SPECIFIER: u8 = 4;

//These are used both for query and response.
pub const STATUS_FASTNET_SPECIFIER: u8 = 0;
pub const STATUS_VERSION_SPECIFIER: u8 = 1;
pub const STATUS_EXTENSION_SPECIFIER: u8 = 2;

//Flag bits for data packets, used in the impl of the struct.
pub const DATA_FRAME_START_BIT: u8 = 0;
pub const DATA_FRAME_END_BIT: u8 = 1;
pub const DATA_RELIABLE_BIT: u8 = 2;

#[derive(Debug, Clone)]
pub struct DataPacket {
    sequence_number: u64,
    flags: u8,
    payload: Vec<u8>,
}

#[derive(Debug, Clone)]
pub struct DataPacketBuilder {
    sequence_number: u64,
    is_reliable: bool,
    is_start: bool,
    is_end: bool,
    payload: Vec<u8>,
}

impl DataPacketBuilder {
    /**Initial state is unreliable, mid-frame, and empty payload.*/
    pub fn new(sequence_number: u64)->DataPacketBuilder {
        DataPacketBuilder::with_payload(sequence_number, Vec::default())
    }

    /**Configures the builder for mid-frame, unreliable but using the specified payload.*/
    pub fn with_payload(sequence_number: u64, payload: Vec<u8>)->DataPacketBuilder {
        DataPacketBuilder {
            sequence_number: sequence_number,
            is_reliable: false,
            is_start: false,
            is_end: false,
            payload: payload,
        }
    }

    pub fn set_payload(&mut self, payload: Vec<u8>) {
        self.payload = payload;
    }

    pub fn set_reliable(&mut self, reliable: bool) {
        self.is_reliable = reliable;
    }

    pub fn set_start(&mut self, start: bool) {
        self.is_start = start;
    }

    pub fn set_end(&mut self, end: bool) {
        self.is_end = end;
    }

    pub fn set_sequence_number(&mut self, sequence_number: u64) {
        self.sequence_number = sequence_number;
    }

    pub fn build(self)->Option<DataPacket> {
        let start_flag  = (self.is_start as u8) << DATA_FRAME_START_BIT;
        let end_flag = (self.is_end as u8) << DATA_FRAME_END_BIT;
        let reliable_flag = (self.is_reliable as u8) << DATA_RELIABLE_BIT;
        let flags = start_flag | end_flag | reliable_flag;
        Some(DataPacket {
            sequence_number: self.sequence_number,
            flags: flags,
            payload: self.payload,
        })
    }
}

impl DataPacket {
    pub fn is_reliable(&self)->bool {
        (self.flags & DATA_RELIABLE_BIT) > 0
    }

    pub fn is_frame_start(&self)->bool {
        (self.flags & DATA_FRAME_START_BIT) > 0
    }

    pub fn is_frame_end(&self)->bool {
        (self.flags & DATA_FRAME_END_BIT) > 0
    }

    pub fn borrow_payload(&self)->&Vec<u8> {
        &self.payload
    }

    pub fn into_payload(self)->Vec<u8> {
        self.payload
    }
}<commit_msg>Add a bunch of traits to DataPacket and DataPacketBuilder<commit_after>/*! Provides packet encoding and decoding functionality, as well as the packet enum.

This module does not handle the checksum.  If it did, it would be incredibly difficult to write Fastnet tests.*/
pub use self::encoder::*;
pub use self::decoder::*;
use uuid;
use std::cmp;

mod encoder;
mod encoder_tests;
mod decoder;
mod decoder_tests;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Packet {
    //Status request and response (channel -1)
    StatusRequest(StatusRequest),
    StatusResponse(StatusResponse),

    //Connection handshake (also channel -1).
    Connect(uuid::Uuid),
    Connected(uuid::Uuid),
    Aborted(String),

    //Heartbeat (channel -2).
    Heartbeat{counter: u64, sent: u64, received: u64},

    Echo{endpoint: uuid::Uuid, uuid: uuid::Uuid},
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StatusRequest {
    FastnetQuery,
    VersionQuery,
    ExtensionQuery(String),
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StatusResponse {
    FastnetResponse(bool),
    VersionResponse(String),
    ExtensionResponse {name: String, supported: bool},
}

pub const CONNECTION_CHANNEL: i16 = -1;
pub const HEARTBEAT_CHANNEL: i16 = -2;
pub const ECHO_CHANNEL: i16 = -3;

pub const STATUS_REQUEST_SPECIFIER: u8 = 0;
pub const STATUS_RESPONSE_SPECIFIER: u8 = 1;
pub const CONNECT_SPECIFIER: u8 = 2;
pub const CONNECTED_SPECIFIER: u8 = 3;
pub const ABORTED_SPECIFIER: u8 = 4;

//These are used both for query and response.
pub const STATUS_FASTNET_SPECIFIER: u8 = 0;
pub const STATUS_VERSION_SPECIFIER: u8 = 1;
pub const STATUS_EXTENSION_SPECIFIER: u8 = 2;

//Flag bits for data packets, used in the impl of the struct.
pub const DATA_FRAME_START_BIT: u8 = 0;
pub const DATA_FRAME_END_BIT: u8 = 1;
pub const DATA_RELIABLE_BIT: u8 = 2;

/**Represents the part of a data packet that a channel must use to assemble packets.

The actual channel itself is stored in the enum variant.

These are ordered by sequence number, for use in trees.*/
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct DataPacket {
    sequence_number: u64,
    flags: u8,
    payload: Vec<u8>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DataPacketBuilder {
    sequence_number: u64,
    is_reliable: bool,
    is_start: bool,
    is_end: bool,
    payload: Vec<u8>,
}

impl DataPacketBuilder {
    /**Initial state is unreliable, mid-frame, and empty payload.*/
    pub fn new(sequence_number: u64)->DataPacketBuilder {
        DataPacketBuilder::with_payload(sequence_number, Vec::default())
    }

    /**Configures the builder for mid-frame, unreliable but using the specified payload.*/
    pub fn with_payload(sequence_number: u64, payload: Vec<u8>)->DataPacketBuilder {
        DataPacketBuilder {
            sequence_number: sequence_number,
            is_reliable: false,
            is_start: false,
            is_end: false,
            payload: payload,
        }
    }

    pub fn set_payload(&mut self, payload: Vec<u8>) {
        self.payload = payload;
    }

    pub fn set_reliable(&mut self, reliable: bool) {
        self.is_reliable = reliable;
    }

    pub fn set_start(&mut self, start: bool) {
        self.is_start = start;
    }

    pub fn set_end(&mut self, end: bool) {
        self.is_end = end;
    }

    pub fn set_sequence_number(&mut self, sequence_number: u64) {
        self.sequence_number = sequence_number;
    }

    pub fn build(self)->Option<DataPacket> {
        let start_flag  = (self.is_start as u8) << DATA_FRAME_START_BIT;
        let end_flag = (self.is_end as u8) << DATA_FRAME_END_BIT;
        let reliable_flag = (self.is_reliable as u8) << DATA_RELIABLE_BIT;
        let flags = start_flag | end_flag | reliable_flag;
        Some(DataPacket {
            sequence_number: self.sequence_number,
            flags: flags,
            payload: self.payload,
        })
    }
}

impl DataPacket {
    pub fn is_reliable(&self)->bool {
        (self.flags & DATA_RELIABLE_BIT) > 0
    }

    pub fn is_frame_start(&self)->bool {
        (self.flags & DATA_FRAME_START_BIT) > 0
    }

    pub fn is_frame_end(&self)->bool {
        (self.flags & DATA_FRAME_END_BIT) > 0
    }

    pub fn borrow_payload(&self)->&Vec<u8> {
        &self.payload
    }

    pub fn into_payload(self)->Vec<u8> {
        self.payload
    }
}<|endoftext|>


<commit_before>use super::book::{Book, BookAccounting};
use std::collections::HashMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq)]
pub struct Record {
  pub rate: f32,
  pub amount: f32
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookStats {
  pub max_sell: f32,
  pub min_buy: f32,
  pub sum_sell: f32,
  pub sum_buy: f32,
  pub vec_buy: Vec<Record>,
  pub vec_sell: Vec<Record>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookWithStats {
  book: Book,
  stats: BookStats
}

// BookStats operations
impl BookStats {
  pub fn new() -> BookStats {
    BookStats {
      max_sell: 0.0,
      min_buy: 0.0,
      sum_sell: 0.0,
      sum_buy: 0.0,
      vec_buy: Vec::new(),
      vec_sell: Vec::new(),
    }
  }

  pub fn init(&mut self, book: &Book) {
    let hash_to_vec = |hash: &HashMap<String, f32>| -> Vec<Record> {
      hash.iter().filter_map(|(rate_s, amount)| {
        rate_s.parse::<f32>()
          .and_then(|rate| Ok(Record { rate, amount: *amount })).ok()
      }).collect()
    };
    self.vec_buy = hash_to_vec(&book.buy);
    self.vec_sell = hash_to_vec(&book.sell);
  }

  pub fn update_sell(&mut self, rate: f32, amount: f32, prev_amount: Option<f32>) {
    if amount > 0.0 && self.max_sell < rate {
      self.max_sell = rate
    };
    self.sum_sell = self.sum_sell + amount - prev_amount.unwrap_or(0.0);
  }

  pub fn update_buy(&mut self, rate: f32, amount: f32, prev_amount: Option<f32>) {
    if amount > 0.0 && self.min_buy > rate {
      self.min_buy = rate
    };
    self.sum_buy = self.sum_buy + amount - prev_amount.unwrap_or(0.0);
  }
}

impl fmt::Display for BookStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "(BUY min {} sum {} | SELL max {} sum {})", self.min_buy, self.sum_buy, self.max_sell, self.sum_sell)
  }
}

// BookWithStats operations
impl BookWithStats {
  pub fn new(book: Book) -> BookWithStats {
    BookWithStats {
      book,
      stats: BookStats::new()
    }
  }
}

impl BookAccounting for BookWithStats {
  fn update_sell(&mut self, rate: String, amount: f32) -> Option<f32> {
    let rate_f32 = rate.parse::<f32>();
    let prev_amount = self.book.update_sell(rate, amount);
    if let Ok(rate_f32) = rate_f32 {
      self.stats.update_sell(rate_f32, amount, prev_amount);
    };
    prev_amount
  }

  fn update_buy(&mut self, rate: String, amount: f32) -> Option<f32> {
    let rate_f32 = rate.parse::<f32>();
    let prev_amount = self.book.update_buy(rate, amount);
    if let Ok(rate_f32) = rate_f32 {
      self.stats.update_buy(rate_f32, amount, prev_amount);
    };
    prev_amount
  }

  fn book_ref(&self) -> &Book {
    &self.book
  }
}
<commit_msg>add stats display<commit_after>use super::book::{Book, BookAccounting};
use std::collections::HashMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq)]
pub struct Record {
  pub rate: f32,
  pub amount: f32
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookStats {
  pub max_sell: f32,
  pub min_buy: f32,
  pub sum_sell: f32,
  pub sum_buy: f32,
  pub vec_buy: Vec<Record>,
  pub vec_sell: Vec<Record>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct BookWithStats {
  book: Book,
  pub stats: BookStats
}

// BookStats operations
impl BookStats {
  pub fn new() -> BookStats {
    BookStats {
      max_sell: 0.0,
      min_buy: 0.0,
      sum_sell: 0.0,
      sum_buy: 0.0,
      vec_buy: Vec::new(),
      vec_sell: Vec::new(),
    }
  }

  pub fn init(&mut self, book: &Book) {
    let hash_to_vec = |hash: &HashMap<String, f32>| -> Vec<Record> {
      hash.iter().filter_map(|(rate_s, amount)| {
        rate_s.parse::<f32>()
          .and_then(|rate| Ok(Record { rate, amount: *amount })).ok()
      }).collect()
    };
    self.vec_buy = hash_to_vec(&book.buy);
    self.vec_sell = hash_to_vec(&book.sell);
  }

  pub fn update_sell(&mut self, rate: f32, amount: f32, prev_amount: Option<f32>) {
    if amount > 0.0 && self.max_sell < rate {
      self.max_sell = rate
    };
    self.sum_sell = self.sum_sell + amount - prev_amount.unwrap_or(0.0);
  }

  pub fn update_buy(&mut self, rate: f32, amount: f32, prev_amount: Option<f32>) {
    if amount > 0.0 && self.min_buy > rate {
      self.min_buy = rate
    };
    self.sum_buy = self.sum_buy + amount - prev_amount.unwrap_or(0.0);
  }
}

impl fmt::Display for BookStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "(BUY min {} sum {} | SELL max {} sum {})", self.min_buy, self.sum_buy, self.max_sell, self.sum_sell)
  }
}

// BookWithStats operations
impl BookWithStats {
  pub fn new(book: Book) -> BookWithStats {
    BookWithStats {
      book,
      stats: BookStats::new()
    }
  }
}

impl fmt::Display for BookWithStats {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    self.stats.fmt(f)
  }
}

impl BookAccounting for BookWithStats {
  fn update_sell(&mut self, rate: String, amount: f32) -> Option<f32> {
    let rate_f32 = rate.parse::<f32>();
    let prev_amount = self.book.update_sell(rate, amount);
    if let Ok(rate_f32) = rate_f32 {
      self.stats.update_sell(rate_f32, amount, prev_amount);
    };
    prev_amount
  }

  fn update_buy(&mut self, rate: String, amount: f32) -> Option<f32> {
    let rate_f32 = rate.parse::<f32>();
    let prev_amount = self.book.update_buy(rate, amount);
    if let Ok(rate_f32) = rate_f32 {
      self.stats.update_buy(rate_f32, amount, prev_amount);
    };
    prev_amount
  }

  fn book_ref(&self) -> &Book {
    &self.book
  }
}
<|endoftext|>


<commit_before>use std::cmp::PartialEq;
use std::convert::From;
use std::fmt::{self, Debug};
use std::ops::{Deref, DerefMut};

use std::ops::FnMut;

/// Top direction.
pub const TOP: usize = 0;
/// Top combined with right direction.
pub const TOP_RIGHT: usize = 1;
/// Right direction.
pub const RIGHT: usize = 2;
/// Bottom combined with right direction.
pub const BOT_RIGHT: usize = 3;
/// Bottom direction.
pub const BOT: usize = 4;
/// Bottom combined with left direction.
pub const BOT_LEFT: usize = 5;
/// Left direction.
pub const LEFT: usize = 6;
/// Top combined with left direction.
pub const TOP_LEFT: usize = 7;

/// Represent all the 8 directions.
/// The only way to create a Direction is from a `[T; 8]`.
pub struct Directions<T>([T; 8]);

impl<T> Directions<T> {
    pub fn top(&self) -> &T { &self.0[TOP] }

    pub fn top_right(&self) -> &T { &self.0[TOP_RIGHT] }

    pub fn right(&self) -> &T { &self.0[RIGHT] }

    pub fn bot_right(&self) -> &T { &self.0[BOT_RIGHT] }

    pub fn bot(&self) -> &T { &self.0[BOT] }

    pub fn bot_left(&self) -> &T { &self.0[BOT_LEFT] }

    pub fn left(&self) -> &T { &self.0[LEFT] }

    pub fn top_left(&self) -> &T { &self.0[TOP_LEFT] }

    pub fn count<P>(&self, mut predicate: P) -> usize where P: FnMut(&T) -> bool {
        let mut count = 0;
        for x in &self.0 {
            if predicate(x) { count += 1; }
        }
        count
    }

    pub fn any<P>(&self, predicate: P) -> bool where P: FnMut(&T) -> bool {
        self.0.iter().any(predicate)
    }
}

impl<T: Debug> Debug for Directions<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Directions({:?})", self.0)
    }
}

impl<T: PartialEq> PartialEq for Directions<T> {
    fn eq(&self, other: &Directions<T>) -> bool {
        self.0 == other.0
    }
}

impl<T> From<[T; 8]> for Directions<T> {
    fn from(array: [T; 8]) -> Self {
        Directions(array)
    }
}

impl<T> Deref for Directions<T> {
    type Target = [T; 8];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for Directions<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
<commit_msg>directions: Add the Direction type<commit_after>use std::cmp::PartialEq;
use std::convert::From;
use std::fmt::{self, Debug};
use std::ops::{Deref, DerefMut};

use std::ops::FnMut;

/// Used to represent 8 directions for more clarity.
pub type Direction = usize;

/// Top direction.
pub const TOP: usize = 0;
/// Top combined with right direction.
pub const TOP_RIGHT: usize = 1;
/// Right direction.
pub const RIGHT: usize = 2;
/// Bottom combined with right direction.
pub const BOT_RIGHT: usize = 3;
/// Bottom direction.
pub const BOT: usize = 4;
/// Bottom combined with left direction.
pub const BOT_LEFT: usize = 5;
/// Left direction.
pub const LEFT: usize = 6;
/// Top combined with left direction.
pub const TOP_LEFT: usize = 7;

/// Represent all the 8 directions.
/// The only way to create a Direction is from a `[T; 8]`.
pub struct Directions<T>([T; 8]);

impl<T> Directions<T> {
    pub fn top(&self) -> &T { &self.0[TOP] }

    pub fn top_right(&self) -> &T { &self.0[TOP_RIGHT] }

    pub fn right(&self) -> &T { &self.0[RIGHT] }

    pub fn bot_right(&self) -> &T { &self.0[BOT_RIGHT] }

    pub fn bot(&self) -> &T { &self.0[BOT] }

    pub fn bot_left(&self) -> &T { &self.0[BOT_LEFT] }

    pub fn left(&self) -> &T { &self.0[LEFT] }

    pub fn top_left(&self) -> &T { &self.0[TOP_LEFT] }

    pub fn count<P>(&self, mut predicate: P) -> usize where P: FnMut(&T) -> bool {
        let mut count = 0;
        for x in &self.0 {
            if predicate(x) { count += 1; }
        }
        count
    }

    pub fn any<P>(&self, predicate: P) -> bool where P: FnMut(&T) -> bool {
        self.0.iter().any(predicate)
    }
}

impl<T: Debug> Debug for Directions<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Directions({:?})", self.0)
    }
}

impl<T: PartialEq> PartialEq for Directions<T> {
    fn eq(&self, other: &Directions<T>) -> bool {
        self.0 == other.0
    }
}

impl<T> From<[T; 8]> for Directions<T> {
    fn from(array: [T; 8]) -> Self {
        Directions(array)
    }
}

impl<T> Deref for Directions<T> {
    type Target = [T; 8];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for Directions<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{Result, Write};
use std::path::Path;

use chunk::*;

pub trait SaveToFile {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
}

impl<I: Iterator<Item=Chunk>> SaveToFile for I {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        match create_backup(&path) {
            Err(why) => panic!("couldn't create backup for {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(_) => (),
        }
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(file) => file,
        };
        for ch in self {
            match file.write_all(&ch.to_string().into_bytes()) {
                Err(why) => panic!("couldn't write to {:?}: {}", path.as_ref(),
                                Error::description(&why)),
                Ok(_) => (),
            }
        }

        Ok(())
    }
}

/// Create .bak file, only if one not present
pub fn create_backup<P: AsRef<Path>>(path: P) -> Result<bool> {
    match fs::metadata(&path) {
        Err(_) => return Ok(false),
        Ok(_) => (),
    }
    let new_path = path.as_ref().with_extension("bak");
    match fs::metadata(&new_path) {
        Err(_) => (),
        Ok(_) => return Ok(false),
    }
    fs::rename(path, new_path).map(|_| true)
}
<commit_msg>Always create backup<commit_after>use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{Result, Write};
use std::path::Path;

use chunk::*;

pub trait SaveToFile {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
}

impl<I: Iterator<Item=Chunk>> SaveToFile for I {
    fn save_to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        match create_backup(&path) {
            Err(why) => panic!("couldn't create backup for {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(_) => (),
        }
        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {:?}: {}", path.as_ref(),
                            Error::description(&why)),
            Ok(file) => file,
        };
        for ch in self {
            match file.write_all(&ch.to_string().into_bytes()) {
                Err(why) => panic!("couldn't write to {:?}: {}", path.as_ref(),
                                Error::description(&why)),
                Ok(_) => (),
            }
        }

        Ok(())
    }
}

/// Create .bak file
pub fn create_backup<P: AsRef<Path>>(path: P) -> Result<bool> {
    match fs::metadata(&path) {
        Err(_) => return Ok(false),
        Ok(_) => (),
    }
    let new_path = path.as_ref().with_extension("bak");
    fs::rename(path, new_path).map(|_| true)
}
<|endoftext|>