bevy_settings/
store_fs.rs1use bevy_log::{debug, error, warn};
2use bevy_platform::dirs::preferences_dir;
3use bevy_tasks::IoTaskPool;
4use std::{fs, path::PathBuf};
5
6pub(crate) struct SettingsStore {
9 base_path: Option<PathBuf>,
10}
11
12impl SettingsStore {
13 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!("Settings path: {:?}", prefs_path);
22 Some(prefs_path)
23 } else {
24 warn!("Could not find user configuration directories");
25 None
26 },
27 }
28 }
29
30 pub(crate) fn save(&self, filename: &str, contents: toml::Table) {
36 if let Some(base_path) = &self.base_path {
37 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 settings directory: {:?}", e);
42 return;
43 }
44
45 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 settings file: {}", e);
49 }
50
51 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 settings file: {:?}", e);
55 }
56 }
57 }
58
59 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 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 settings directory: {:?}", e);
73 return;
74 }
75
76 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 settings file: {}", e);
80 }
81
82 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 settings file: {:?}", e);
86 }
87 });
88 });
89 }
90 }
91
92 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
107pub(crate) fn decode_toml_file(file: &PathBuf) -> Option<toml::Table> {
109 if file.exists() && file.is_file() {
110 let settings_str = match fs::read_to_string(file) {
111 Ok(settings_str) => settings_str,
112 Err(e) => {
113 error!("Error reading settings file: {}", e);
114 return None;
115 }
116 };
117
118 let table_value = match toml::from_str::<toml::Value>(&settings_str) {
119 Ok(table_value) => table_value,
120 Err(e) => {
121 error!("Error parsing settings file: {}", e);
122 return None;
123 }
124 };
125
126 match table_value {
127 toml::Value::Table(table) => Some(table),
128 _ => {
129 error!("Settings file must be a table");
130 None
131 }
132 }
133 } else {
134 None
136 }
137}