rtools 0.1.27

Set of tools for my needs
Documentation
#![allow(clippy::mismatched_target_os)]

use std::{
    fs,
    path::{Path, PathBuf},
};

cfg_if::cfg_if! { if #[cfg(android)] {
    use android_ndk_sys::AAsset_getLength;
    use android_ndk_sys::AASSET_MODE_STREAMING;
    use android_ndk_sys::AAssetManager_open;
    use android_ndk_sys::AAssetManager_fromJava;
    use android_ndk_sys::AAsset_read;
    use android_ndk_sys::AAsset_close;
}}

pub struct File {}

impl File {
    pub fn exists(path: impl AsRef<Path>) -> bool {
        Path::new(path.as_ref()).exists()
    }

    pub fn mkdir(path: impl AsRef<Path>) {
        if File::exists(&path) {
            return;
        }
        fs::create_dir(path.as_ref())
            .unwrap_or_else(|_| panic!("Failed to mkdir: {}", path.as_ref().display()))
    }

    pub fn get_files(path: impl AsRef<Path>) -> Vec<PathBuf> {
        fs::read_dir(path)
            .unwrap()
            .map(|a| a.unwrap().path())
            .collect()
    }
}

#[cfg(android)]
static mut ASSET_MANAGER: *mut android_ndk_sys::AAssetManager = std::ptr::null_mut();

#[cfg(android)]
pub fn set_asset_manager(env: android_ndk_sys::JNIEnv, asset_manager: android_ndk_sys::jobject) {
    unsafe { ASSET_MANAGER = AAssetManager_fromJava(env as _, asset_manager) }
}

impl File {
    pub fn read_to_string(path: impl AsRef<Path>) -> String {
        cfg_if::cfg_if! { if #[cfg(android)] {
            std::str::from_utf8(&unsafe { android_read(path) }).unwrap().to_string()
        } else {
            match fs::read_to_string(&path) {
                Ok(data) => data,
                Err(err) => {
                    error!("Failed to read file: {:?}, {}", path.as_ref(), err);
                    panic!("Failed to read file: {:?}, {}", path.as_ref(), err);
                }
            }
        }}
    }

    pub fn read(path: impl AsRef<Path>) -> Vec<u8> {
        cfg_if::cfg_if! { if #[cfg(android)] {
            unsafe { android_read(path) }
        } else {
            match fs::read(&path) {
                Ok(data) => data,
                Err(err) => {
                    error!("Failed to read file: {:?}, {}", path.as_ref(), err);
                    panic!("Failed to read file: {:?}, {}", path.as_ref(), err);
                }
            }
        }}
    }
}

#[cfg(android)]
pub unsafe fn android_read(path: impl AsRef<Path>) -> Vec<u8> {
    use std::ffi::CString;

    if ASSET_MANAGER.is_null() {
        error!("Android asset manager is null");
        panic!("Android asset manager is null");
    }

    let c_code = CString::new(path.as_ref().to_string_lossy().into_owned()).unwrap();

    let code_ptr = c_code.as_ptr();

    let asset = AAssetManager_open(ASSET_MANAGER, code_ptr, AASSET_MODE_STREAMING as _);
    if asset.is_null() {
        error!("Failed to open file: {:?}. Asset is null.", path.as_ref());
        panic!("Failed to open file: {:?}. Asset is null.", path.as_ref());
    }

    let size = AAsset_getLength(asset);
    let mut data: Vec<u8> = vec![0; size as _];
    AAsset_read(asset, data.as_mut_ptr() as _, size as _);
    AAsset_close(asset);
    data
}