Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_settings/src/store_fs.rs
30620 views
1
use bevy_log::{debug, error, warn};
2
use bevy_platform::dirs::preferences_dir;
3
use bevy_tasks::IoTaskPool;
4
use std::{fs, path::PathBuf};
5
6
/// Persistent storage which uses the local filesystem. Preferences will be located in the
7
/// OS-specific directory for user preferences.
8
pub(crate) struct PreferencesStore {
9
base_path: Option<PathBuf>,
10
}
11
12
impl PreferencesStore {
13
/// Construct a new filesystem preferences store.
14
///
15
/// # Arguments
16
/// * `app_name` - The name of the application. See [`crate::PreferencesPlugin`] for usage.
17
pub(crate) fn new(app_name: &str) -> Self {
18
Self {
19
base_path: if let Some(base_dir) = preferences_dir() {
20
let prefs_path = base_dir.join(app_name);
21
debug!("Preferences path: {:?}", prefs_path);
22
Some(prefs_path)
23
} else {
24
warn!("Could not find user configuration directories");
25
None
26
},
27
}
28
}
29
30
/// Save a [`toml::Table`] to disk.
31
///
32
/// # Arguments
33
/// * `filename` - the name of the file to be saved
34
/// * `contents` - the contents of the file
35
pub(crate) fn save(&self, filename: &str, contents: toml::Table) {
36
if let Some(base_path) = &self.base_path {
37
// Recursively create the preferences directory if it doesn't exist.
38
let mut dir_builder = fs::DirBuilder::new();
39
dir_builder.recursive(true);
40
if let Err(e) = dir_builder.create(base_path.clone()) {
41
warn!("Could not create preferences directory: {:?}", e);
42
return;
43
}
44
45
// Save preferences to temp file
46
let temp_path = base_path.join(format!("{filename}.toml.new"));
47
if let Err(e) = fs::write(&temp_path, contents.to_string()) {
48
error!("Error saving preferences file: {}", e);
49
}
50
51
// Replace old prefs file with new one.
52
let file_path = base_path.join(format!("{filename}.toml"));
53
if let Err(e) = fs::rename(&temp_path, file_path) {
54
warn!("Could not save preferences file: {:?}", e);
55
}
56
}
57
}
58
59
/// Save the contents of a [`toml::Table`] to disk in another thread.
60
///
61
/// # Arguments
62
/// * `filename` - the name of the file to be saved
63
/// * `contents` - the contents of the file
64
pub(crate) fn save_async(&self, filename: &str, contents: toml::Table) {
65
if let Some(base_path) = &self.base_path {
66
IoTaskPool::get().scope(|scope| {
67
scope.spawn(async {
68
// Recursively create the preferences directory if it doesn't exist.
69
let mut dir_builder = fs::DirBuilder::new();
70
dir_builder.recursive(true);
71
if let Err(e) = dir_builder.create(base_path.clone()) {
72
warn!("Could not create preferences directory: {:?}", e);
73
return;
74
}
75
76
// Save preferences to temp file
77
let temp_path = base_path.join(format!("{filename}.toml.new"));
78
if let Err(e) = fs::write(&temp_path, contents.to_string()) {
79
error!("Error saving preferences file: {}", e);
80
}
81
82
// Replace old prefs file with new one.
83
let file_path = base_path.join(format!("{filename}.toml"));
84
if let Err(e) = fs::rename(&temp_path, file_path) {
85
warn!("Could not save preferences file: {:?}", e);
86
}
87
});
88
});
89
}
90
}
91
92
/// Deserialize a [`toml::Table`] from disk. If the file does not exist, `None` will
93
/// be returned.
94
///
95
/// # Arguments
96
/// * `filename` - The name of the preferences file, without the file extension.
97
pub(crate) fn load(&self, filename: &str) -> Option<toml::Table> {
98
let Some(base_path) = &self.base_path else {
99
return None;
100
};
101
102
let file_path = base_path.join(format!("{filename}.toml"));
103
decode_toml_file(&file_path)
104
}
105
}
106
107
/// Load a preferences file from disk in TOML format.
108
pub(crate) fn decode_toml_file(file: &PathBuf) -> Option<toml::Table> {
109
if file.exists() && file.is_file() {
110
let prefs_str = match fs::read_to_string(file) {
111
Ok(prefs_str) => prefs_str,
112
Err(e) => {
113
error!("Error reading preferences file: {}", e);
114
return None;
115
}
116
};
117
118
let table_value = match toml::from_str::<toml::Value>(&prefs_str) {
119
Ok(table_value) => table_value,
120
Err(e) => {
121
error!("Error parsing preferences file: {}", e);
122
return None;
123
}
124
};
125
126
match table_value {
127
toml::Value::Table(table) => Some(table),
128
_ => {
129
error!("Preferences file must be a table");
130
None
131
}
132
}
133
} else {
134
// Preferences file does not exist yet.
135
None
136
}
137
}
138
139