1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use bevy::log::error;
use bones_framework::prelude::*;
use serde::{de::Visitor, Deserialize, Serialize};

#[cfg(target_arch = "wasm32")]
pub use wasm::StorageBackend;
#[cfg(target_arch = "wasm32")]
mod wasm {
    use super::*;
    pub struct StorageBackend {
        storage_key: String,
    }

    impl StorageBackend {
        pub fn new(qualifier: &str, organization: &str, application: &str) -> Self {
            Self {
                storage_key: format!("{qualifier}.{organization}.{application}.storage"),
            }
        }
    }

    impl StorageApi for StorageBackend {
        fn save(&mut self, data: Vec<SchemaBox>) {
            let mut buffer = Vec::new();
            let mut serializer = serde_yaml::Serializer::new(&mut buffer);
            LoadedStorage(data)
                .serialize(&mut serializer)
                .expect("Failed to serialize to storage file.");
            let data = String::from_utf8(buffer).unwrap();
            let window = web_sys::window().unwrap();
            let storage = window.local_storage().unwrap().unwrap();
            storage.set_item(&self.storage_key, &data).unwrap();
        }

        fn load(&mut self) -> Vec<SchemaBox> {
            let window = web_sys::window().unwrap();
            let storage = window.local_storage().unwrap().unwrap();
            let Some(data) = storage.get_item(&self.storage_key).unwrap() else {
                return default();
            };

            let Ok(loaded) = serde_yaml::from_str::<LoadedStorage>(&data) else {
                return default();
            };
            loaded.0
        }
    }
}

#[cfg(not(target_arch = "wasm32"))]
pub use native::StorageBackend;
#[cfg(not(target_arch = "wasm32"))]
mod native {
    use super::*;

    pub struct StorageBackend {
        storage_path: std::path::PathBuf,
    }

    impl StorageBackend {
        pub fn new(qualifier: &str, organization: &str, application: &str) -> Self {
            let project_dirs = directories::ProjectDirs::from(qualifier, organization, application)
                .expect("Identify system data dir path");
            Self {
                storage_path: project_dirs.data_dir().join("storage.yml"),
            }
        }
    }

    impl StorageApi for StorageBackend {
        fn save(&mut self, data: Vec<SchemaBox>) {
            let file = std::fs::OpenOptions::new()
                .write(true)
                .truncate(true)
                .create(true)
                .open(&self.storage_path)
                .expect("Failed to open storage file");
            let mut serializer = serde_yaml::Serializer::new(file);
            LoadedStorage(data)
                .serialize(&mut serializer)
                .expect("Failed to serialize to storage file.");
        }

        fn load(&mut self) -> Vec<SchemaBox> {
            if self.storage_path.exists() {
                let result: anyhow::Result<LoadedStorage> = (|| {
                    let file = std::fs::OpenOptions::new()
                        .read(true)
                        .open(&self.storage_path)
                        .context("Failed to open storage file")?;
                    let loaded: LoadedStorage = serde_yaml::from_reader(file)
                        .context("Failed to deserialize storage file")?;

                    anyhow::Result::Ok(loaded)
                })();
                match result {
                    Ok(loaded) => loaded.0,
                    Err(e) => {
                        error!(
                            "Error deserializing storage file, ignoring file, \
                        data will be overwritten when saved: {e:?}"
                        );
                        default()
                    }
                }
            } else {
                std::fs::create_dir_all(self.storage_path.parent().unwrap()).unwrap();
                default()
            }
        }
    }
}

struct LoadedStorage(Vec<SchemaBox>);
impl Serialize for LoadedStorage {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let data: HashMap<String, SchemaRef> = self
            .0
            .iter()
            .map(|x| (x.schema().full_name.to_string(), x.as_ref()))
            .collect();

        use serde::ser::SerializeMap;
        let mut map = serializer.serialize_map(Some(data.len()))?;

        for (key, value) in data {
            map.serialize_key(&key)?;
            map.serialize_value(&SchemaSerializer(value))?;
        }

        map.end()
    }
}
impl<'de> Deserialize<'de> for LoadedStorage {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_map(LoadedStorageVisitor).map(Self)
    }
}
struct LoadedStorageVisitor;
impl<'de> Visitor<'de> for LoadedStorageVisitor {
    type Value = Vec<SchemaBox>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "Mapping of string type names to type data.")
    }
    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        let mut data = Vec::new();
        while let Some(type_name) = map.next_key::<String>()? {
            let Some(schema) = SCHEMA_REGISTRY
                .schemas
                .iter()
                .find(|schema| schema.full_name.as_ref() == type_name)
            else {
                error!(
                    "\n\nCannot find schema registration for `{}` while loading persisted \
                        storage. This means you that you need to call \
                        `{}::schema()` to register your persisted storage type before \
                        creating the `BonesBevyRenderer` or that there is data from an old \
                        version of the app inside of the persistent storage file.\n\n",
                    type_name, type_name,
                );
                continue;
            };

            data.push(map.next_value_seed(SchemaDeserializer(schema))?);
        }

        Ok(data)
    }
}