mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-10-16 22:19:26 -04:00
* prototype * `.normalise` helper + only `String` keys * implement it for 'search.paths' * redux devtools * fix * refactor backend * wip: upgrade to rspc fork * mega cursed * Upgrade Specta-related stuff * Upgrade Typescript * Cache debug page * bruh * Fix optimistic library setting * Cache clearing * better timeout * Fix tags page * bit of cleanup --------- Co-authored-by: Brendan Allan <brendonovich@outlook.com>
199 lines
5.2 KiB
Rust
199 lines
5.2 KiB
Rust
use std::{marker::PhantomData, sync::Arc};
|
|
|
|
use serde::{ser::SerializeMap, Serialize, Serializer};
|
|
use specta::{Any, DataType, NamedType, Type, TypeMap};
|
|
|
|
/// A type that can be used to return a group of `Reference<T>` and `CacheNode`'s
|
|
///
|
|
/// You don't need to use this, it's just a shortcut to avoid having to write out the full type everytime.
|
|
#[derive(Serialize, Type, Debug)]
|
|
pub struct NormalisedResults<T: Model + Type> {
|
|
pub items: Vec<Reference<T>>,
|
|
pub nodes: Vec<CacheNode>,
|
|
}
|
|
|
|
/// A type that can be used to return a group of `Reference<T>` and `CacheNode`'s
|
|
///
|
|
/// You don't need to use this, it's just a shortcut to avoid having to write out the full type everytime.
|
|
#[derive(Serialize, Type, Debug)]
|
|
pub struct NormalisedResult<T: Model + Type> {
|
|
pub item: Reference<T>,
|
|
pub nodes: Vec<CacheNode>,
|
|
}
|
|
|
|
impl<T: Model + Serialize + Type> NormalisedResult<T> {
|
|
pub fn from(item: T, id_fn: impl Fn(&T) -> String) -> Self {
|
|
let id = id_fn(&item);
|
|
Self {
|
|
item: Reference::new(id.clone()),
|
|
nodes: vec![CacheNode::new(id, item)],
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A type which can be stored in the cache.
|
|
pub trait Model {
|
|
/// Must return a unique identifier for this model within the cache.
|
|
fn name() -> &'static str;
|
|
}
|
|
|
|
/// A reference to a `CacheNode`.
|
|
///
|
|
/// This does not contain the actual data, but instead a reference to it.
|
|
/// This allows the CacheNode's to be switched out and the query recomputed without any backend communication.
|
|
///
|
|
/// If you use a `Reference` in a query, you *must* ensure the corresponding `CacheNode` is also in the query.
|
|
#[derive(Type, Debug, Clone, Hash, PartialEq, Eq)]
|
|
pub struct Reference<T> {
|
|
__type: &'static str,
|
|
__id: String,
|
|
#[specta(rename = "#type")]
|
|
ty: PhantomType<T>,
|
|
}
|
|
|
|
impl<T: Model + Type> Reference<T> {
|
|
pub fn new(key: String) -> Self {
|
|
Self {
|
|
__type: "", // This is just to fake the field for Specta
|
|
__id: key,
|
|
ty: PhantomType(PhantomData),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Model> Serialize for Reference<T> {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
let mut map = serializer.serialize_map(Some(2))?;
|
|
map.serialize_entry("__type", T::name())?;
|
|
map.serialize_entry("__id", &self.__id)?;
|
|
map.end()
|
|
}
|
|
}
|
|
|
|
/// A node in the cache.
|
|
/// This holds the data and is identified by it's type and id.
|
|
#[derive(Debug, Clone)] // TODO: `Hash, PartialEq, Eq`
|
|
pub struct CacheNode(
|
|
&'static str,
|
|
serde_json::Value,
|
|
Result<serde_json::Value, Arc<serde_json::Error>>,
|
|
);
|
|
|
|
impl CacheNode {
|
|
pub fn new<T: Model + Serialize + Type>(key: String, value: T) -> Self {
|
|
Self(
|
|
T::name(),
|
|
key.into(),
|
|
serde_json::to_value(value).map_err(Arc::new),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Type, Default)]
|
|
#[specta(rename = "CacheNode", remote = CacheNode)]
|
|
#[allow(unused)]
|
|
struct CacheNodeTy {
|
|
__type: String,
|
|
__id: String,
|
|
#[specta(rename = "#node")]
|
|
node: Any,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct NodeSerdeRepr<'a> {
|
|
__type: &'static str,
|
|
__id: &'a serde_json::Value,
|
|
#[serde(flatten)]
|
|
v: &'a serde_json::Value,
|
|
}
|
|
|
|
impl Serialize for CacheNode {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
NodeSerdeRepr {
|
|
__type: self.0,
|
|
__id: &self.1,
|
|
v: self.2.as_ref().map_err(|err| {
|
|
serde::ser::Error::custom(format!("Failed to serialise node: {}", err))
|
|
})?,
|
|
}
|
|
.serialize(serializer)
|
|
}
|
|
}
|
|
|
|
/// A helper for easily normalising data.
|
|
pub trait Normalise {
|
|
type Item: Model + Type;
|
|
|
|
fn normalise(
|
|
self,
|
|
id_fn: impl Fn(&Self::Item) -> String,
|
|
) -> (Vec<CacheNode>, Vec<Reference<Self::Item>>);
|
|
}
|
|
|
|
impl<T: Model + Serialize + Type> Normalise for Vec<T> {
|
|
type Item = T;
|
|
|
|
fn normalise(
|
|
self,
|
|
id_fn: impl Fn(&Self::Item) -> String,
|
|
) -> (Vec<CacheNode>, Vec<Reference<Self::Item>>) {
|
|
let mut nodes = Vec::with_capacity(self.len());
|
|
let mut references = Vec::with_capacity(self.len());
|
|
|
|
for item in self.into_iter() {
|
|
let id = id_fn(&item);
|
|
nodes.push(CacheNode::new(id.clone(), item));
|
|
references.push(Reference::new(id));
|
|
}
|
|
|
|
(nodes, references)
|
|
}
|
|
}
|
|
|
|
/// Basically `PhantomData`.
|
|
///
|
|
/// With Specta `PhantomData` is exported as `null`.
|
|
/// This will export as `T` but serve the same purpose as `PhantomData` (holding a type without it being instantiated).
|
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
pub struct PhantomType<T>(PhantomData<T>);
|
|
|
|
/// WARNING: This type is surgically updated within `Reference` in the final typedefs due it being impossible to properly implement.
|
|
/// Be careful changing it!
|
|
|
|
impl<T: Type> Type for PhantomType<T> {
|
|
fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> DataType {
|
|
T::inline(type_map, generics)
|
|
}
|
|
|
|
fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> specta::reference::Reference {
|
|
T::reference(type_map, generics)
|
|
}
|
|
|
|
fn definition(type_map: &mut TypeMap) -> DataType {
|
|
T::definition(type_map)
|
|
}
|
|
}
|
|
|
|
// This function is cursed.
|
|
pub fn patch_typedef(type_map: &mut TypeMap) {
|
|
#[derive(Type)]
|
|
#[specta(rename = "Reference")]
|
|
#[allow(unused)]
|
|
struct ReferenceTy<T> {
|
|
__type: &'static str,
|
|
__id: String,
|
|
#[specta(rename = "#type")]
|
|
ty: T,
|
|
}
|
|
|
|
let mut def = <Reference<()> as NamedType>::definition_named_data_type(type_map);
|
|
def.inner = ReferenceTy::<Any>::definition(type_map);
|
|
type_map.insert(<Reference<()> as NamedType>::SID, def)
|
|
}
|