use crate::prelude::*;
#[derive(Clone, Debug, Eq, PartialEq, Reflect)]
pub enum Storage {
#[cfg(not(target_family = "wasm"))]
Filesystem { path: PathBuf },
#[cfg(target_family = "wasm")]
LocalStorage { key: String },
#[cfg(target_family = "wasm")]
SessionStorage { key: String },
}
impl Storage {
pub fn initialize(&self) -> Result<(), PersistenceError> {
match self {
#[cfg(not(target_family = "wasm"))]
Storage::Filesystem { path } => {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
},
#[cfg(target_family = "wasm")]
Storage::LocalStorage { .. } => {},
#[cfg(target_family = "wasm")]
Storage::SessionStorage { .. } => {},
}
Ok(())
}
pub fn occupied(&self) -> bool {
match self {
#[cfg(not(target_family = "wasm"))]
Storage::Filesystem { path } => path.exists(),
#[cfg(target_family = "wasm")]
Storage::LocalStorage { key } => {
use gloo_storage::{
LocalStorage,
Storage,
};
matches!(LocalStorage::raw().get_item(key), Ok(Some(_)))
},
#[cfg(target_family = "wasm")]
Storage::SessionStorage { key } => {
use gloo_storage::{
SessionStorage,
Storage,
};
matches!(SessionStorage::raw().get_item(key), Ok(Some(_)))
},
}
}
pub fn read<R: Serialize + DeserializeOwned>(
&self,
name: &str,
format: StorageFormat,
) -> Result<R, PersistenceError> {
match self {
#[cfg(not(target_family = "wasm"))]
Storage::Filesystem { path } => {
let bytes = std::fs::read(path)?;
format.deserialize::<R>(name, &bytes)
},
#[cfg(target_family = "wasm")]
Storage::LocalStorage { key } => {
use gloo_storage::{
errors::StorageError,
LocalStorage,
Storage,
};
#[cfg(feature = "json")]
if format == StorageFormat::Json {
return Ok(LocalStorage::get::<R>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to parse {} as JSON\n\n{}", name, error);
}
error
})?);
}
#[cfg(all(feature = "json", feature = "pretty"))]
if format == StorageFormat::JsonPretty {
return Ok(LocalStorage::get::<R>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to parse {} as pretty JSON\n\n{}", name, error);
}
error
})?);
}
#[cfg(feature = "bincode")]
if format == StorageFormat::Bincode {
let bytes = LocalStorage::get::<Vec<u8>>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to get {} as a byte array\n\n{}", name, error);
}
error
})?;
return format.deserialize::<R>(name, &bytes);
}
let content = LocalStorage::get::<String>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to get {} as a string\n\n{}", name, error);
}
error
})?;
format.deserialize::<R>(name, content.as_bytes())
},
#[cfg(target_family = "wasm")]
Storage::SessionStorage { key } => {
use gloo_storage::{
errors::StorageError,
SessionStorage,
Storage,
};
#[cfg(feature = "json")]
if format == StorageFormat::Json {
return Ok(SessionStorage::get::<R>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to parse {} as JSON\n\n{}", name, error);
}
error
})?);
}
#[cfg(all(feature = "json", feature = "pretty"))]
if format == StorageFormat::JsonPretty {
return Ok(SessionStorage::get::<R>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to parse {} as pretty JSON\n\n{}", name, error);
}
error
})?);
}
#[cfg(feature = "bincode")]
if format == StorageFormat::Bincode {
let bytes = SessionStorage::get::<Vec<u8>>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to get {} as a byte array\n\n{}", name, error);
}
error
})?;
return format.deserialize::<R>(name, &bytes);
}
let content = SessionStorage::get::<String>(key).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to get {} as a string\n\n{}", name, error);
}
error
})?;
format.deserialize::<R>(name, content.as_bytes())
},
}
}
pub fn write<R: Serialize + DeserializeOwned>(
&self,
name: &str,
format: StorageFormat,
resource: &R,
) -> Result<(), PersistenceError> {
match self {
#[cfg(not(target_family = "wasm"))]
Storage::Filesystem { path } => {
let bytes = format.serialize(name, resource)?;
use std::io::Write;
std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(path)
.and_then(|mut file| file.write_all(&bytes))?;
},
#[cfg(target_family = "wasm")]
Storage::LocalStorage { key } => {
use gloo_storage::{
errors::StorageError,
LocalStorage,
Storage,
};
#[cfg(feature = "json")]
if format == StorageFormat::Json {
LocalStorage::set::<&R>(key, resource).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to serialize {} to JSON\n\n{}", name, error);
}
error
})?;
return Ok(());
}
#[cfg(all(feature = "json", feature = "pretty"))]
if format == StorageFormat::JsonPretty {
LocalStorage::set::<&R>(key, resource).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to serialize {} to pretty JSON\n\n{}", name, error);
}
error
})?;
return Ok(());
}
#[cfg(feature = "bincode")]
if format == StorageFormat::Bincode {
let bytes = format.serialize(name, resource)?;
LocalStorage::set::<&[u8]>(key, &bytes)?;
return Ok(());
}
let bytes = format.serialize(name, resource)?;
let string = std::str::from_utf8(&bytes).unwrap();
LocalStorage::set::<&str>(key, string)?;
},
#[cfg(target_family = "wasm")]
Storage::SessionStorage { key } => {
use gloo_storage::{
errors::StorageError,
SessionStorage,
Storage,
};
#[cfg(feature = "json")]
if format == StorageFormat::Json {
SessionStorage::set::<&R>(key, resource).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to serialize {} to JSON\n\n{}", name, error);
}
error
})?;
return Ok(());
}
#[cfg(all(feature = "json", feature = "pretty"))]
if format == StorageFormat::JsonPretty {
SessionStorage::set::<&R>(key, resource).map_err(|error| {
if let StorageError::SerdeError(error) = &error {
log::error!("failed to serialize {} to pretty JSON\n\n{}", name, error);
}
error
})?;
return Ok(());
}
#[cfg(feature = "bincode")]
if format == StorageFormat::Bincode {
let bytes = format.serialize(name, resource)?;
SessionStorage::set::<&[u8]>(key, &bytes)?;
return Ok(());
}
let bytes = format.serialize(name, resource)?;
let string = std::str::from_utf8(&bytes).unwrap();
SessionStorage::set::<&str>(key, string)?;
},
}
Ok(())
}
}
impl Display for Storage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(not(target_family = "wasm"))]
Storage::Filesystem { path } => {
if let Some(path) = path.to_str() {
write!(f, "{}", path)
} else {
write!(f, "{:?}", path)
}
},
#[cfg(target_family = "wasm")]
Storage::LocalStorage { key } => {
let separator = std::path::MAIN_SEPARATOR;
write!(f, "{}local{}{}", separator, separator, key)
},
#[cfg(target_family = "wasm")]
Storage::SessionStorage { key } => {
let separator = std::path::MAIN_SEPARATOR;
write!(f, "{}session{}{}", separator, separator, key)
},
}
}
}