localsavefile/
localsavefilepersistent.rs

1use 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    // NOTE: Does not ensure path is valid/existing
68    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        // Clear file using existing handle
104        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        // Clone handle into writer
112        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}