use crate::common::*;
use std::io;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "browser.js")]
extern "C" {
#[wasm_bindgen(js_name = existsAsync)]
async fn js_exists_async(path: String) -> JsValue;
#[wasm_bindgen(js_name = isDirectoryAsync)]
async fn js_is_directory_async(path: String) -> JsValue;
#[wasm_bindgen(js_name = isFileAsync)]
async fn js_is_file_async(path: String) -> JsValue;
#[wasm_bindgen(catch, js_name = createDirectoryAsync)]
async fn js_create_directory(parent_path: String, name: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = createDirectoryAllAsync)]
async fn js_create_directory_all(path: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = readBytesAsync)]
async fn js_read_bytes_async(path: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = directoryListingAsync)]
async fn js_directory_listing_async(path: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = deleteEmptyDirectoryAsync)]
async fn js_delete_empty_directory_async(parent_path: String, name: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = deleteDirectoryAllAsync)]
async fn js_delete_directory_all_async(parent_path: String, name: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = deleteFileAsync)]
async fn js_delete_file_async(parent_path: String, name: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = writeAsync)]
async fn js_write_async(path: String, data: JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = modificationEpochMillisecondsAsync)]
async fn js_modification_epoch_milliseconds_async(path: String) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = sizeAsync)]
async fn js_size_async(path: String) -> Result<JsValue, JsValue>;
}
pub async fn exists_async(path: String) -> bool {
js_exists_async(path).await.as_bool().unwrap()
}
pub async fn is_directory_async(path: String) -> bool {
js_is_directory_async(path).await.as_bool().unwrap()
}
pub async fn is_file_async(path: String) -> bool {
js_is_file_async(path).await.as_bool().unwrap()
}
pub async fn create_directory_async(parent_path: String, name: String) -> io::Result<()> {
js_create_directory(parent_path, name).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error(error, true))
}
pub async fn create_directory_all_async(path: String) -> io::Result<()> {
js_create_directory_all(path).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error(error, true))
}
pub async fn read_bytes_async(path: String) -> io::Result<Bytes> {
js_read_bytes_async(path).await.map(|ba| Bytes::from(js_sys::Uint8Array::try_from(ba).unwrap().to_vec())).map_err(|error| js_io_error_to_rs_io_error(error, false))
}
pub async fn read_utf8_async(path: String) -> io::Result<String> {
js_read_bytes_async(path).await.map(|ba| String::from_utf8_lossy(&js_sys::Uint8Array::try_from(ba).unwrap().to_vec()).into_owned()).map_err(|error| js_io_error_to_rs_io_error(error, false))
}
pub async fn directory_listing_async(path: String) -> io::Result<Vec<String>> {
let listing1 = js_directory_listing_async(path).await.map_err(|error| js_io_error_to_rs_io_error(error, true))?;
let mut listing2 = vec![];
for name in js_sys::Array::try_from(listing1).unwrap() {
listing2.push(name.as_string().unwrap());
}
Ok(listing2)
}
pub async fn delete_empty_directory_async(parent_path: String, name: String) -> io::Result<()> {
js_delete_empty_directory_async(parent_path, name).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error_for_delete_directory(error))
}
pub async fn delete_directory_all_async(parent_path: String, name: String) -> io::Result<()> {
js_delete_directory_all_async(parent_path, name).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error_for_delete_directory(error))
}
pub async fn delete_file_async(parent_path: String, name: String) -> io::Result<()> {
js_delete_file_async(parent_path, name).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error(error, false))
}
pub async fn write_async(path: String, data: &[u8]) -> io::Result<()> {
let uint8array = js_sys::Uint8Array::from(data);
js_write_async(path, uint8array.buffer().into()).await.map(|_| ()).map_err(|error| js_io_error_to_rs_io_error(error, false))
}
pub async fn modification_date_async(path: String) -> io::Result<Option<std::time::SystemTime>> {
let ms = js_modification_epoch_milliseconds_async(path).await.map_err(|error| js_io_error_to_rs_io_error(error, false))?;
if ms.is_undefined() {
return Ok(None);
}
let ms: u64 = unsafe { ms.as_f64().unwrap().to_int_unchecked() };
Ok(Some(std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_millis(ms)))
}
pub async fn size_async(path: String) -> io::Result<usize> {
let size = js_size_async(path).await.map_err(|error| js_io_error_to_rs_io_error(error, false))?;
Ok(unsafe { size.as_f64().unwrap().to_int_unchecked() })
}
pub(crate) fn js_io_error_to_rs_io_error(error: JsValue, is_directory: bool) -> io::Error {
let error = error.as_f64().unwrap();
if error == 0.0 {
io::Error::new(io::ErrorKind::NotFound, "File or directory not found")
} else if error == 1.0 {
if is_directory {
io::Error::new(io::ErrorKind::NotADirectory, "Not a directory")
} else {
io::Error::new(io::ErrorKind::IsADirectory, "Found directory")
}
} else if error == 2.0 {
io::Error::new(io::ErrorKind::PermissionDenied, "Permission denied")
} else if error == 3.0 {
io::Error::new(io::ErrorKind::InvalidFilename, "Invalid filename")
} else if [4.0, 5.0].contains(&error) {
io::Error::new(io::ErrorKind::Other, "Invalidated origin private file system state")
} else {
io::Error::new(io::ErrorKind::Other, "Unknown error")
}
}
pub(crate) fn js_io_error_to_rs_io_error_for_delete_directory(js_error: JsValue) -> io::Error {
let error = js_error.as_f64().unwrap();
if error == 6.0 {
io::Error::new(io::ErrorKind::DirectoryNotEmpty, "Directory not empty")
} else {
js_io_error_to_rs_io_error(js_error, true)
}
}