bevy_pkv 0.5.0

Persistent key value store for apps/games
Documentation
use crate::{StoreConfig, StoreImpl};

#[derive(Debug, Default)]
pub struct LocalStorageStore {
    prefix: String,
}

pub use LocalStorageStore as InnerStore;

#[derive(thiserror::Error, Debug)]
pub enum GetError {
    #[error("No value found for the given key")]
    NotFound,
    #[error("error deserializing json")]
    Json(#[from] serde_json::Error),
    #[error("JavaScript error from getItem")]
    GetItem(wasm_bindgen::JsValue),
}

#[derive(thiserror::Error, Debug)]
pub enum SetError {
    #[error("JavaScript error from setItem")]
    SetItem(wasm_bindgen::JsValue),
    #[error("Error serializing as json")]
    Json(#[from] serde_json::Error),
}

impl LocalStorageStore {
    fn storage(&self) -> web_sys::Storage {
        web_sys::window()
            .expect("No window")
            .local_storage()
            .expect("Failed to get local storage")
            .expect("No local storage")
    }

    pub(crate) fn new(config: &StoreConfig) -> Self {
        let StoreConfig {
            qualifier,
            organization,
            application,
        } = config;
        Self {
            prefix: match qualifier.as_deref() {
                Some(qualifier) => format!("{qualifier}.{organization}.{application}"),
                None => format!("{organization}.{application}"),
            },
        }
    }

    fn format_key(&self, key: &str) -> String {
        format!("{}{}", self.prefix, key)
    }
}

impl StoreImpl for LocalStorageStore {
    type GetError = GetError;
    type SetError = SetError;

    fn set_string(&mut self, key: &str, value: &str) -> Result<(), SetError> {
        let json = serde_json::to_string(value)?;
        let storage = self.storage();
        let key = self.format_key(key);
        storage.set_item(&key, &json).map_err(SetError::SetItem)?;
        Ok(())
    }

    fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, GetError> {
        let storage = self.storage();
        let key = self.format_key(key);
        let entry = storage.get_item(&key).map_err(GetError::GetItem)?;
        let json = entry.as_ref().ok_or(GetError::NotFound)?;
        let value: T = serde_json::from_str(json)?;
        Ok(value)
    }

    fn set<T: serde::Serialize>(&mut self, key: &str, value: &T) -> Result<(), SetError> {
        let json = serde_json::to_string(value)?;
        let storage = self.storage();
        let key = self.format_key(key);
        storage.set_item(&key, &json).map_err(SetError::SetItem)?;
        Ok(())
    }
}