batbox_file_dialog/
lib.rs1#![warn(missing_docs)]
3
4use futures::prelude::*;
5#[cfg(target_arch = "wasm32")]
6use wasm_bindgen::prelude::*;
7
8pub struct SelectedFile {
10 #[cfg(target_arch = "wasm32")]
11 file: web_sys::File,
12 #[cfg(not(target_arch = "wasm32"))]
13 path: std::path::PathBuf,
14}
15
16impl SelectedFile {
17 pub fn name(&self) -> std::ffi::OsString {
19 #[cfg(target_arch = "wasm32")]
20 {
21 self.file.name().into()
22 }
23 #[cfg(not(target_arch = "wasm32"))]
24 {
25 self.path.file_name().expect("no filename").to_owned()
26 }
27 }
28
29 #[cfg(not(target_arch = "wasm32"))]
33 pub fn path(&self) -> &std::path::Path {
34 &self.path
35 }
36
37 pub fn reader(self) -> anyhow::Result<impl AsyncBufRead> {
39 #[cfg(target_arch = "wasm32")]
40 {
41 Ok(futures::io::BufReader::new(batbox_file::read_stream(
42 self.file.stream(),
43 )))
44 }
45 #[cfg(not(target_arch = "wasm32"))]
46 {
47 futures::executor::block_on(batbox_file::load(self.path))
48 }
49 }
50}
51
52pub fn select(callback: impl FnOnce(SelectedFile) + 'static) {
59 #[cfg(target_arch = "wasm32")]
60 {
61 let input: web_sys::HtmlInputElement = web_sys::window()
62 .expect("no window")
63 .document()
64 .expect("no document")
65 .create_element("input")
66 .expect("failed to create input")
67 .unchecked_into();
68 input.set_type("file");
69 input.set_onchange(Some(
70 Closure::once_into_js({
71 let input = input.clone();
72 move || {
73 let Some(files) = input.files() else { return };
74 let Some(file) = files.get(0) else { return };
75 let file = SelectedFile { file };
76 callback(file);
77 }
78 })
79 .unchecked_ref(),
80 ));
81 input.click();
82 }
83 #[cfg(not(target_arch = "wasm32"))]
84 {
85 if let Some(path) = tinyfiledialogs::open_file_dialog("Select", "", None) {
87 let file = SelectedFile { path: path.into() };
88 callback(file);
89 }
90 }
91}
92
93pub fn save(file_name: &str, data: &[u8]) -> anyhow::Result<()> {
95 #[cfg(target_arch = "wasm32")]
96 {
97 let data = web_sys::Blob::new_with_u8_array_sequence(&js_sys::Array::of1(
98 &js_sys::Uint8Array::from(data), ))
100 .expect("failed to create blob");
101 let a: web_sys::HtmlAnchorElement = web_sys::window()
102 .expect("no window")
103 .document()
104 .expect("no document")
105 .create_element("a")
106 .expect("failed to create <a> element")
107 .unchecked_into();
108 let url =
109 web_sys::Url::create_object_url_with_blob(&data).expect("failed to create blob url");
110 a.set_href(&url);
111 a.set_download(file_name);
112 a.click();
114 web_sys::Url::revoke_object_url(&a.href()).expect("failed to revoke url");
115 }
116 #[cfg(not(target_arch = "wasm32"))]
117 {
118 if let Some(path) = tinyfiledialogs::save_file_dialog("Save", file_name) {
119 let file = std::fs::File::create(path)?;
120 let mut writer = std::io::BufWriter::new(file);
121 std::io::Write::write_all(&mut writer, data)?;
122 }
123 }
124 Ok(())
125}