localsavefile/
localsavefilepersistent.rs1use std::{
2 fs::{File, OpenOptions},
3 io::{self, BufReader, BufWriter, Seek, Write},
4 path::{Path, PathBuf},
5};
6
7use savefile::prelude::SavefileNoIntrospect;
8use tracing::{debug, warn};
9
10use crate::LocalSaveFileCommon;
11
12#[derive(Default, SavefileNoIntrospect, Debug)]
13pub struct LocalSaveFileMetaData {
14 #[savefile_ignore]
15 pub file: Option<File>,
16 #[savefile_ignore]
17 pub reader: Option<BufReader<File>>,
18 #[savefile_ignore]
19 pub path: Option<PathBuf>,
20}
21impl core::hash::Hash for LocalSaveFileMetaData {
22 fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
23}
24impl PartialEq for LocalSaveFileMetaData {
25 fn eq(&self, _other: &Self) -> bool {
26 true
27 }
28}
29impl Eq for LocalSaveFileMetaData {}
30impl PartialOrd for LocalSaveFileMetaData {
31 #[allow(clippy::non_canonical_partial_ord_impl)]
32 fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
33 Some(std::cmp::Ordering::Equal)
34 }
35}
36impl Ord for LocalSaveFileMetaData {
37 fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
38 std::cmp::Ordering::Equal
39 }
40}
41impl Clone for LocalSaveFileMetaData {
42 fn clone(&self) -> Self {
43 Self {
44 file: self.file.as_ref().and_then(|file| file.try_clone().ok()),
45 reader: None,
46 path: None,
47 }
48 }
49}
50
51pub trait LocalSaveFilePersistent
52where
53 Self: LocalSaveFileCommon,
54{
55 fn get_metadata_mut(&mut self) -> &mut LocalSaveFileMetaData;
56
57 fn close(&mut self) {
58 if self.get_metadata_mut().file.is_none() {
59 debug!("LocalSaveFile already closed");
60 return;
61 }
62 let metadata = self.get_metadata_mut();
63 metadata.file = None;
64 metadata.reader = None;
65 }
66
67 fn open<P>(&mut self, path: P) -> io::Result<()>
69 where
70 P: AsRef<Path>,
71 {
72 let metadata = self.get_metadata_mut();
73 let file = match &mut metadata.file {
74 Some(file) => {
75 debug!("LocalSaveFile already open with file");
76 file
77 }
78 None => {
79 metadata.file = Some(
80 OpenOptions::new()
81 .read(true)
82 .write(true)
83 .create(true)
84 .truncate(false)
85 .open(path)?,
86 );
87 metadata.reader = None;
88 metadata.file.as_mut().expect("Failed to get metadata file")
89 }
90 };
91
92 if metadata.reader.is_none() {
93 let reader = BufReader::new(file.try_clone()?);
94 metadata.reader = Some(reader);
95 }
96
97 Ok(())
98 }
99
100 fn save(&mut self) -> io::Result<()> {
101 self.open_default()?;
102 let metadata = self.get_metadata_mut();
103 let Some(file) = &mut metadata.file else {
105 return Err(std::io::Error::other("Failed to get file handle"));
106 };
107 file.flush()?;
108 file.rewind()?;
109 file.set_len(0)?;
110
111 let mut file = file.try_clone()?;
113 let mut writer = BufWriter::new(&mut file);
114
115 let result = savefile::save_compressed(&mut writer, Self::get_version(), self);
116 if let Err(err) = result {
117 Err(std::io::Error::other(err))
118 } else {
119 Ok(())
120 }
121 }
122
123 fn load(&mut self) -> io::Result<()> {
124 self.open_default()?;
125
126 let metadata = self.get_metadata_mut();
127 let Some(file) = metadata.file.as_mut() else {
128 return Err(io::Error::other("Failed to get file handle"));
129 };
130 file.flush()?;
131 file.rewind()?;
132
133 let Some(mut reader) = metadata.reader.as_mut() else {
134 return Err(std::io::Error::other("Failed to get reader"));
135 };
136
137 let result: Result<Self, savefile::SavefileError> =
138 savefile::load(&mut reader, Self::get_version());
139 match result {
140 Ok(res) => {
141 let metadata = metadata.clone();
142 *self = res;
143 *self.get_metadata_mut() = metadata;
144 Ok(())
145 }
146 Err(err) => Err(std::io::Error::other(err)),
147 }
148 }
149
150 fn open_default(&mut self) -> io::Result<()> {
151 let path = match &self.get_metadata_mut().path {
152 Some(p) => p.to_owned(),
153 None => Self::get_full_path()?,
154 };
155 self.open(path)
156 }
157
158 fn load_default() -> Self {
159 let mut def = Self::default();
160 if def.open_default().is_err() {
161 warn!("Failed to open default LocalSaveFile directory");
162 }
163 if let Err(result) = def.load() {
164 debug!("{:?}", result);
165 warn!(
166 "Failed to load on LocalSaveFile default, using default {:?}",
167 Self::get_struct_name()
168 );
169 }
170 def
171 }
172
173 fn load_file_or_default(file_path: &str) -> Self {
174 let mut def = Self::default();
175 if def.open_default().is_err() {
176 warn!("Failed to open default LocalSaveFile directory");
177 }
178 let result = def.load_file(file_path);
179 if result.is_err() {
180 debug!("{:?}", result);
181 warn!(
182 "Failed to load on LocalSaveFile, using path {:?}",
183 file_path
184 );
185 }
186 def
187 }
188}