bones_bevy_renderer/
storage.rs1use bevy::log::error;
2use bones_framework::prelude::*;
3use serde::{de::Visitor, Deserialize, Serialize};
4
5#[cfg(target_arch = "wasm32")]
6pub use wasm::StorageBackend;
7#[cfg(target_arch = "wasm32")]
8mod wasm {
9 use super::*;
10
11 pub struct StorageBackend {
13 storage_key: String,
14 }
15
16 impl StorageBackend {
17 pub fn new(qualifier: &str, organization: &str, application: &str) -> Self {
19 Self {
20 storage_key: format!("{qualifier}.{organization}.{application}.storage"),
21 }
22 }
23 }
24
25 impl StorageApi for StorageBackend {
26 fn save(&mut self, data: Vec<SchemaBox>) {
27 let mut buffer = Vec::new();
28 let mut serializer = serde_yaml::Serializer::new(&mut buffer);
29 LoadedStorage(data)
30 .serialize(&mut serializer)
31 .expect("Failed to serialize to storage file.");
32 let data = String::from_utf8(buffer).unwrap();
33 let window = web_sys::window().unwrap();
34 let storage = window.local_storage().unwrap().unwrap();
35 storage.set_item(&self.storage_key, &data).unwrap();
36 }
37
38 fn load(&mut self) -> Vec<SchemaBox> {
39 let window = web_sys::window().unwrap();
40 let storage = window.local_storage().unwrap().unwrap();
41 let Some(data) = storage.get_item(&self.storage_key).unwrap() else {
42 return default();
43 };
44
45 let Ok(loaded) = serde_yaml::from_str::<LoadedStorage>(&data) else {
46 return default();
47 };
48 loaded.0
49 }
50 }
51}
52
53#[cfg(not(target_arch = "wasm32"))]
54pub use native::StorageBackend;
55#[cfg(not(target_arch = "wasm32"))]
56mod native {
57 use super::*;
58
59 pub struct StorageBackend {
61 storage_path: std::path::PathBuf,
62 }
63
64 impl StorageBackend {
65 pub fn new(qualifier: &str, organization: &str, application: &str) -> Self {
67 let project_dirs = directories::ProjectDirs::from(qualifier, organization, application)
68 .expect("Identify system data dir path");
69 Self {
70 storage_path: project_dirs.data_dir().join("storage.yml"),
71 }
72 }
73 }
74
75 impl StorageApi for StorageBackend {
76 fn save(&mut self, data: Vec<SchemaBox>) {
77 let file = std::fs::OpenOptions::new()
78 .write(true)
79 .truncate(true)
80 .create(true)
81 .open(&self.storage_path)
82 .expect("Failed to open storage file");
83 let mut serializer = serde_yaml::Serializer::new(file);
84 LoadedStorage(data)
85 .serialize(&mut serializer)
86 .expect("Failed to serialize to storage file.");
87 }
88
89 fn load(&mut self) -> Vec<SchemaBox> {
90 if self.storage_path.exists() {
91 let result: anyhow::Result<LoadedStorage> = (|| {
92 let file = std::fs::OpenOptions::new()
93 .read(true)
94 .open(&self.storage_path)
95 .context("Failed to open storage file")?;
96 let loaded: LoadedStorage = serde_yaml::from_reader(file)
97 .context("Failed to deserialize storage file")?;
98
99 anyhow::Result::Ok(loaded)
100 })();
101 match result {
102 Ok(loaded) => loaded.0,
103 Err(e) => {
104 error!(
105 "Error deserializing storage file, ignoring file, \
106 data will be overwritten when saved: {e:?}"
107 );
108 default()
109 }
110 }
111 } else {
112 std::fs::create_dir_all(self.storage_path.parent().unwrap()).unwrap();
113 default()
114 }
115 }
116 }
117}
118
119struct LoadedStorage(Vec<SchemaBox>);
120impl Serialize for LoadedStorage {
121 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
122 where
123 S: serde::Serializer,
124 {
125 let data: HashMap<String, SchemaRef> = self
126 .0
127 .iter()
128 .map(|x| (x.schema().full_name.to_string(), x.as_ref()))
129 .collect();
130
131 use serde::ser::SerializeMap;
132 let mut map = serializer.serialize_map(Some(data.len()))?;
133
134 for (key, value) in data {
135 map.serialize_key(&key)?;
136 map.serialize_value(&SchemaSerializer(value))?;
137 }
138
139 map.end()
140 }
141}
142impl<'de> Deserialize<'de> for LoadedStorage {
143 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144 where
145 D: serde::Deserializer<'de>,
146 {
147 deserializer.deserialize_map(LoadedStorageVisitor).map(Self)
148 }
149}
150struct LoadedStorageVisitor;
151impl<'de> Visitor<'de> for LoadedStorageVisitor {
152 type Value = Vec<SchemaBox>;
153 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
154 write!(formatter, "Mapping of string type names to type data.")
155 }
156 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
157 where
158 A: serde::de::MapAccess<'de>,
159 {
160 let mut data = Vec::new();
161 while let Some(type_name) = map.next_key::<String>()? {
162 let Some(schema) = SCHEMA_REGISTRY
163 .schemas
164 .iter()
165 .find(|schema| schema.full_name.as_ref() == type_name)
166 else {
167 error!(
168 "\n\nCannot find schema registration for `{}` while loading persisted \
169 storage. This means you that you need to call \
170 `{}::schema()` to register your persisted storage type before \
171 creating the `BonesBevyRenderer` or that there is data from an old \
172 version of the app inside of the persistent storage file.\n\n",
173 type_name, type_name,
174 );
175 continue;
176 };
177
178 data.push(map.next_value_seed(SchemaDeserializer(schema))?);
179 }
180
181 Ok(data)
182 }
183}