pf_sandbox_lib 0.3.1

Data structures and helper code for PF Sandbox
Documentation
use std::fs::{DirBuilder, File};
use std::fs;
use std::io::{Cursor, Read, Write, Seek};
use std::path::{PathBuf, Path};

use dirs;
use reqwest::Url;
use reqwest;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use serde_json::Value;
use serde_json;
use zip::write::FileOptions;
use zip::{ZipArchive, ZipWriter};

pub fn write_to_zip<TObject: Serialize, TWriter: Write + Seek>(zip: &mut ZipWriter<TWriter>, path: &str, object: &TObject) {
    zip.start_file(path, FileOptions::default()).unwrap();
    let json = serde_json::to_string_pretty(object).unwrap();
    zip.write_all(json.as_bytes()).unwrap();
}

pub fn save_struct<T: Serialize>(filename: PathBuf, object: &T) {
    // ensure parent directories exists
    DirBuilder::new().recursive(true).create(filename.parent().unwrap()).unwrap();

    // save
    let json = serde_json::to_string_pretty(object).unwrap();
    File::create(filename).unwrap().write_all(&json.as_bytes()).unwrap();
}

pub fn save_struct_compressed<T: Serialize>(filename: PathBuf, object: &T) {
    // ensure parent directories exists
    DirBuilder::new().recursive(true).create(filename.parent().unwrap()).unwrap();

    // save
    let json = serde_json::to_string_pretty(object).unwrap();
    let mut zip = ZipWriter::new(File::create(filename).unwrap());
    zip.start_file("data.json", FileOptions::default()).unwrap();
    zip.write_all(&json.as_bytes()).unwrap();
    zip.finish().unwrap();
}

pub fn load_struct<T: DeserializeOwned>(filename: PathBuf) -> Result<T, String> {
    let json = load_file(filename)?;
    serde_json::from_str(&json).map_err(|x| format!("{:?}", x))
}

pub fn load_struct_compressed<T: DeserializeOwned>(filename: PathBuf) -> Result<T, String> {
    let mut zip = ZipArchive::new(File::open(filename).unwrap()).map_err(|x| format!("{:?}", x))?;
    let zip_file = zip.by_name("data.json").map_err(|x| format!("{:?}", x))?;
    serde_json::from_reader(zip_file).map_err(|x| format!("{:?}", x))
}

pub fn load_json(filename: PathBuf) -> Result<Value, String> {
    let json = load_file(filename)?;
    serde_json::from_str(&json).map_err(|x| format!("{:?}", x))
}

pub fn load_file(filename: PathBuf) -> Result<String, String> {
    let mut file = match File::open(&filename) {
        Ok(file) => file,
        Err(err) => return Err(format!("Failed to open file: {} because: {}", filename.to_str().unwrap(), err))
    };

    let mut contents = String::new();
    if let Err(err) = file.read_to_string(&mut contents) {
        return Err(format!("Failed to read file {} because: {}", filename.to_str().unwrap(), err))
    };
    Ok(contents)
}

/// Load the json file at the passed URL directly into a struct
pub fn load_struct_from_url<T: DeserializeOwned>(url: Url) -> Option<T> {
    if let Ok(mut response) = reqwest::get(url) {
        if response.status().is_success() {
            return response.json().ok();
        }
    }
    None
}

/// Returns the bytes of the file stored at the url
pub fn load_bin_from_url(url: Url) -> Option<Vec<u8>> {
    if let Ok(mut response) = reqwest::get(url) {
        if response.status().is_success() {
            let mut buf: Vec<u8> = vec!();
            if let Ok(_) = response.read_to_end(&mut buf) {
                return Some(buf);
            }
        }
    }
    None
}

/// deletes all files in the passed directory
/// if the directory does not exist it is created
pub fn nuke_dir(path: &Path) {
    fs::remove_dir_all(path).ok();
    fs::create_dir_all(path).unwrap();
}

/// Delete contents of destination directory
/// Extract contents of zip into destination
pub fn extract_zip(zip: &[u8], destination: &Path) {
    nuke_dir(destination);

    if let Ok(mut zip) = ZipArchive::new(Cursor::new(zip)) {
        for i in 0..zip.len() {
            let mut file = zip.by_index(i).unwrap();
            let mut path = PathBuf::from(destination);
            path.push(file.name());

            if file.name().ends_with("/") { // TODO: Is this cross platform?
                fs::create_dir_all(path).unwrap();
            }
            else {
                let mut buf = Vec::<u8>::new();
                file.read_to_end(&mut buf).unwrap();
                File::create(path).unwrap().write_all(&buf).unwrap();
            }
        }
    }
}

/// Delete contents of destination directory
/// Extract contents of zip into destination
pub fn extract_zip_fs(source: &Path, destination: &Path) {
    if let Ok(source_file) = File::open(source) {
        nuke_dir(destination);
        if let Ok(mut zip) = ZipArchive::new(source_file) {
            for i in 0..zip.len() {
                let mut file = zip.by_index(i).unwrap();
                let mut path = PathBuf::from(destination);
                path.push(file.name());

                if file.name().ends_with("/") { // TODO: Is this cross platform?
                    fs::create_dir_all(path).unwrap();
                }
                else {
                    let mut buf = Vec::<u8>::new();
                    file.read_to_end(&mut buf).unwrap();
                    File::create(path).unwrap().write_all(&buf).unwrap();
                }
            }
        }
    }
}

pub fn has_ext(path: &PathBuf, check_ext: &str) -> bool {
    if let Some(ext) = path.extension() {
        if let Some(ext) = ext.to_str() {
            if ext.to_lowercase().as_str() == check_ext {
                return true
            }
        }
    }
    false
}

pub fn get_path() -> PathBuf {
    let mut data_local = dirs::data_local_dir().expect("Could not get data_local_dir");
    data_local.push("PF_Sandbox");
    data_local
}