libsurfer/
state_file_io.rs1use std::path::PathBuf;
2
3#[cfg(not(target_arch = "wasm32"))]
4use camino::Utf8PathBuf;
5use eyre::Context;
6use rfd::FileHandle;
7
8#[cfg(not(target_arch = "wasm32"))]
9use crate::async_util::perform_async_work;
10
11use crate::{
12 async_util::AsyncJob, message::Message, wave_source::STATE_FILE_EXTENSION, SystemState,
13};
14
15impl SystemState {
16 #[cfg(target_arch = "wasm32")]
17 pub fn load_state_file(&mut self, path: Option<PathBuf>) {
18 if path.is_some() {
19 return;
20 }
21 let message = move |bytes: Vec<u8>| match ron::de::from_bytes(&bytes)
22 .context("Failed loading state file")
23 {
24 Ok(s) => vec![Message::LoadState(s, path)],
25 Err(e) => {
26 log::error!("Failed to load state: {e:#?}");
27 vec![]
28 }
29 };
30 self.file_dialog_open(
31 "Load state",
32 (
33 format!("Surfer state files (*.{STATE_FILE_EXTENSION})"),
34 vec![STATE_FILE_EXTENSION.to_string()],
35 ),
36 message,
37 );
38 }
39
40 #[cfg(not(target_arch = "wasm32"))]
41 pub fn load_state_file(&mut self, path: Option<PathBuf>) {
42 let messages = move |path: PathBuf| {
43 let source = Utf8PathBuf::from_path_buf(path.clone()).unwrap();
44 if let Ok(bytes) = std::fs::read(source.clone()) {
45 match ron::de::from_bytes(&bytes)
46 .context(format!("Failed loading {}", source.file_name().unwrap()))
47 {
48 Ok(s) => vec![Message::LoadState(s, Some(path))],
49 Err(e) => {
50 log::error!("Failed to load state: {e:#?}");
51 vec![]
52 }
53 }
54 } else {
55 log::error!("Failed to load state file: {path:#?}");
56 vec![]
57 }
58 };
59 if let Some(path) = path {
60 let sender = self.channels.msg_sender.clone();
61 for message in messages(path) {
62 sender.send(message).unwrap();
63 }
64 } else {
65 self.file_dialog_open(
66 "Load state",
67 (
68 format!("Surfer state files (*.{STATE_FILE_EXTENSION})"),
69 vec![STATE_FILE_EXTENSION.to_string()],
70 ),
71 messages,
72 );
73 }
74 }
75
76 #[cfg(not(target_arch = "wasm32"))]
77 pub fn save_state_file(&mut self, path: Option<PathBuf>) {
78 let Some(encoded) = self.encode_state() else {
79 return;
80 };
81
82 let messages = async move |destination: FileHandle| {
83 destination
84 .write(encoded.as_bytes())
85 .await
86 .map_err(|e| log::error!("Failed to write state to {destination:#?} {e:#?}"))
87 .ok();
88 vec![
89 Message::SetStateFile(destination.path().into()),
90 Message::AsyncDone(AsyncJob::SaveState),
91 ]
92 };
93 if let Some(path) = path {
94 let sender = self.channels.msg_sender.clone();
95 perform_async_work(async move {
96 for message in messages(path.into()).await {
97 sender.send(message).unwrap();
98 }
99 });
100 } else {
101 self.file_dialog_save(
102 "Save state",
103 (
104 format!("Surfer state files (*.{STATE_FILE_EXTENSION})"),
105 ([STATE_FILE_EXTENSION.to_string()]).to_vec(),
106 ),
107 messages,
108 );
109 }
110 }
111
112 #[cfg(target_arch = "wasm32")]
113 pub fn save_state_file(&mut self, path: Option<PathBuf>) {
114 if path.is_some() {
115 return;
116 }
117 let Some(encoded) = self.encode_state() else {
118 return;
119 };
120 let messages = async move |destination: FileHandle| {
121 destination
122 .write(encoded.as_bytes())
123 .await
124 .map_err(|e| log::error!("Failed to write state to {destination:#?} {e:#?}"))
125 .ok();
126 vec![Message::AsyncDone(AsyncJob::SaveState)]
127 };
128 self.file_dialog_save(
129 "Save state",
130 (
131 format!("Surfer state files (*.{STATE_FILE_EXTENSION})"),
132 ([STATE_FILE_EXTENSION.to_string()]).to_vec(),
133 ),
134 messages,
135 );
136 }
137
138 fn encode_state(&self) -> Option<String> {
139 let opt = ron::Options::default();
140
141 opt.to_string_pretty(&self.user, ron::ser::PrettyConfig::default())
142 .context("Failed to encode state")
143 .map_err(|e| log::error!("Failed to encode state. {e:#?}"))
144 .ok()
145 }
146}