|
@@ -0,0 +1,103 @@
|
|
|
+use dioxus_html::FileEngine;
|
|
|
+use futures_channel::oneshot;
|
|
|
+use js_sys::Uint8Array;
|
|
|
+use wasm_bindgen::{prelude::Closure, JsCast};
|
|
|
+use web_sys::{File, FileList, FileReader};
|
|
|
+
|
|
|
+pub(crate) struct WebFileEngine {
|
|
|
+ file_reader: FileReader,
|
|
|
+ file_list: FileList,
|
|
|
+}
|
|
|
+
|
|
|
+impl WebFileEngine {
|
|
|
+ pub fn new(file_list: FileList) -> Option<Self> {
|
|
|
+ Some(Self {
|
|
|
+ file_list,
|
|
|
+ file_reader: FileReader::new().ok()?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ fn len(&self) -> usize {
|
|
|
+ self.file_list.length() as usize
|
|
|
+ }
|
|
|
+
|
|
|
+ fn get(&self, index: usize) -> Option<File> {
|
|
|
+ self.file_list.item(index as u32)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn find(&self, name: &str) -> Option<File> {
|
|
|
+ (0..self.len())
|
|
|
+ .filter_map(|i| self.get(i))
|
|
|
+ .find(|f| f.name() == name)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[async_trait::async_trait(?Send)]
|
|
|
+impl FileEngine for WebFileEngine {
|
|
|
+ fn files(&self) -> Vec<String> {
|
|
|
+ (0..self.len())
|
|
|
+ .filter_map(|i| self.get(i).map(|f| f.name()))
|
|
|
+ .collect()
|
|
|
+ }
|
|
|
+
|
|
|
+ // read a file to bytes
|
|
|
+ async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
|
|
+ let file = self.find(file)?;
|
|
|
+
|
|
|
+ let file_reader = self.file_reader.clone();
|
|
|
+ let (rx, tx) = oneshot::channel();
|
|
|
+ let on_load: Closure<dyn FnMut()> = Closure::new({
|
|
|
+ let mut rx = Some(rx);
|
|
|
+ move || {
|
|
|
+ let result = file_reader.result();
|
|
|
+ let _ = rx
|
|
|
+ .take()
|
|
|
+ .expect("multiple files read without refreshing the channel")
|
|
|
+ .send(result);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ self.file_reader
|
|
|
+ .set_onload(Some(on_load.as_ref().unchecked_ref()));
|
|
|
+ on_load.forget();
|
|
|
+ self.file_reader.read_as_array_buffer(&file).ok()?;
|
|
|
+
|
|
|
+ if let Ok(Ok(js_val)) = tx.await {
|
|
|
+ let as_u8_arr = Uint8Array::new(&js_val);
|
|
|
+ let as_u8_vec = as_u8_arr.to_vec();
|
|
|
+
|
|
|
+ Some(as_u8_vec)
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // read a file to string
|
|
|
+ async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
|
|
+ let file = self.find(file)?;
|
|
|
+
|
|
|
+ let file_reader = self.file_reader.clone();
|
|
|
+ let (rx, tx) = oneshot::channel();
|
|
|
+ let on_load: Closure<dyn FnMut()> = Closure::new({
|
|
|
+ let mut rx = Some(rx);
|
|
|
+ move || {
|
|
|
+ let result = file_reader.result();
|
|
|
+ let _ = rx
|
|
|
+ .take()
|
|
|
+ .expect("multiple files read without refreshing the channel")
|
|
|
+ .send(result);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ self.file_reader
|
|
|
+ .set_onload(Some(on_load.as_ref().unchecked_ref()));
|
|
|
+ on_load.forget();
|
|
|
+ self.file_reader.read_as_text(&file).ok()?;
|
|
|
+
|
|
|
+ if let Ok(Ok(js_val)) = tx.await {
|
|
|
+ js_val.as_string()
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|