1use std::future::Future;
2#[cfg(not(target_arch = "wasm32"))]
3use std::path::PathBuf;
4
5#[cfg(not(target_arch = "wasm32"))]
6use camino::Utf8PathBuf;
7use rfd::{AsyncFileDialog, FileHandle};
8use serde::Deserialize;
9
10use crate::async_util::perform_async_work;
11use crate::message::Message;
12use crate::wave_source::LoadOptions;
13use crate::SystemState;
14
15#[derive(Debug, Deserialize)]
16pub enum OpenMode {
17 Open,
18 Switch,
19}
20
21impl SystemState {
22 #[cfg(not(target_arch = "wasm32"))]
23 pub fn file_dialog_open<F>(
24 &mut self,
25 title: &'static str,
26 filter: (String, Vec<String>),
27 messages: F,
28 ) where
29 F: FnOnce(PathBuf) -> Vec<Message> + Send + 'static,
30 {
31 let sender = self.channels.msg_sender.clone();
32
33 perform_async_work(async move {
34 if let Some(file) = create_file_dialog(filter, title).pick_file().await {
35 for message in messages(file.path().to_path_buf()) {
36 sender.send(message).unwrap();
37 }
38 }
39 });
40 }
41
42 #[cfg(target_arch = "wasm32")]
43 pub fn file_dialog_open<F>(
44 &mut self,
45 title: &'static str,
46 filter: (String, Vec<String>),
47 messages: F,
48 ) where
49 F: FnOnce(Vec<u8>) -> Vec<Message> + 'static,
50 {
51 let sender = self.channels.msg_sender.clone();
52
53 perform_async_work(async move {
54 if let Some(file) = create_file_dialog(filter, title).pick_file().await {
55 for message in messages(file.read().await) {
56 sender.send(message).unwrap();
57 }
58 }
59 });
60 }
61
62 #[cfg(not(target_arch = "wasm32"))]
63 pub fn file_dialog_save<F, Fut>(
64 &mut self,
65 title: &'static str,
66 filter: (String, Vec<String>),
67 messages: F,
68 ) where
69 F: FnOnce(FileHandle) -> Fut + Send + 'static,
70 Fut: Future<Output = Vec<Message>> + Send + 'static,
71 {
72 let sender = self.channels.msg_sender.clone();
73
74 perform_async_work(async move {
75 if let Some(file) = create_file_dialog(filter, title).save_file().await {
76 let msgs = messages(file).await;
77 for message in msgs {
78 sender.send(message).unwrap();
79 }
80 }
81 });
82 }
83
84 #[cfg(target_arch = "wasm32")]
85 pub fn file_dialog_save<F, Fut>(
86 &mut self,
87 title: &'static str,
88 filter: (String, Vec<String>),
89 messages: F,
90 ) where
91 F: FnOnce(FileHandle) -> Fut + 'static,
92 Fut: Future<Output = Vec<Message>> + 'static,
93 {
94 let sender = self.channels.msg_sender.clone();
95
96 perform_async_work(async move {
97 if let Some(file) = create_file_dialog(filter, title).save_file().await {
98 let msgs = messages(file).await;
99 for message in msgs {
100 sender.send(message).unwrap();
101 }
102 }
103 });
104 }
105
106 pub fn open_file_dialog(&mut self, mode: OpenMode) {
107 let keep_unavailable = self.user.config.behavior.keep_during_reload;
108 let keep_variables = match mode {
109 OpenMode::Open => false,
110 OpenMode::Switch => true,
111 };
112
113 #[cfg(not(target_arch = "wasm32"))]
114 let message = move |file: PathBuf| {
115 vec![Message::LoadFile(
116 Utf8PathBuf::from_path_buf(file).unwrap(),
117 LoadOptions {
118 keep_variables,
119 keep_unavailable,
120 },
121 )]
122 };
123
124 #[cfg(target_arch = "wasm32")]
125 let message = move |file: Vec<u8>| {
126 vec![Message::LoadFromData(
127 file,
128 LoadOptions {
129 keep_variables,
130 keep_unavailable,
131 },
132 )]
133 };
134
135 self.file_dialog_open(
136 "Open waveform file",
137 (
138 "Waveform/Transaction-files (*.vcd, *.fst, *.ghw, *.ftr)".to_string(),
139 vec![
140 "vcd".to_string(),
141 "fst".to_string(),
142 "ghw".to_string(),
143 "ftr".to_string(),
144 ],
145 ),
146 message,
147 );
148 }
149
150 pub fn open_command_file_dialog(&mut self) {
151 #[cfg(not(target_arch = "wasm32"))]
152 let message = move |file: PathBuf| {
153 vec![Message::LoadCommandFile(
154 Utf8PathBuf::from_path_buf(file).unwrap(),
155 )]
156 };
157
158 #[cfg(target_arch = "wasm32")]
159 let message = move |file: Vec<u8>| vec![Message::LoadCommandFromData(file)];
160
161 self.file_dialog_open(
162 "Open command file",
163 (
164 "Command-file (*.sucl)".to_string(),
165 vec!["sucl".to_string()],
166 ),
167 message,
168 );
169 }
170
171 #[cfg(feature = "python")]
172 pub fn open_python_file_dialog(&mut self) {
173 self.file_dialog_open(
174 "Open Python translator file",
175 ("Python files (*.py)".to_string(), vec!["py".to_string()]),
176 |file| {
177 vec![Message::LoadPythonTranslator(
178 Utf8PathBuf::from_path_buf(file).unwrap(),
179 )]
180 },
181 );
182 }
183}
184
185fn create_file_dialog(filter: (String, Vec<String>), title: &'static str) -> AsyncFileDialog {
186 AsyncFileDialog::new()
187 .set_title(title)
188 .add_filter(filter.0, &filter.1)
189 .add_filter("All files", &["*"])
190}