From a68c49f2fd87991a5c9d5d56b91d0f1a12eab3a6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 2 Mar 2023 18:24:57 -0700 Subject: [PATCH] improved api --- patch-db-macro-internals/src/lib.rs | 348 +++++++++++++++++++++++++--- patch-db/src/lib.rs | 7 +- patch-db/src/model.rs | 300 ++++++++++++------------ patch-db/src/store.rs | 32 ++- patch-db/src/test.rs | 44 ++-- 5 files changed, 523 insertions(+), 208 deletions(-) diff --git a/patch-db-macro-internals/src/lib.rs b/patch-db-macro-internals/src/lib.rs index 4de78c3..e4c3bac 100644 --- a/patch-db-macro-internals/src/lib.rs +++ b/patch-db-macro-internals/src/lib.rs @@ -8,8 +8,9 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::{Comma, Paren, Pub}; use syn::{ - Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Ident, Lit, LitInt, LitStr, - MetaNameValue, Path, Type, VisRestricted, Visibility, + Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, GenericArgument, Ident, Lit, + LitInt, LitStr, Meta, MetaNameValue, Path, PathArguments, Type, TypePath, VisRestricted, + Visibility, }; pub fn build_model(item: &DeriveInput) -> TokenStream { @@ -126,12 +127,17 @@ impl ChildInfo { } struct Fns { + from_parts: TokenStream, impl_fns: TokenStream, + impl_ref_fns: TokenStream, impl_mut_fns: TokenStream, } -fn impl_fns(children: &[ChildInfo]) -> Fns { +fn impl_fns(model_ty: &Type, children: &[ChildInfo]) -> Fns { + let mut parts_args = TokenStream::new(); + let mut parts_assignments = TokenStream::new(); let mut impl_fns = TokenStream::new(); + let mut impl_ref_fns = TokenStream::new(); let mut impl_mut_fns = TokenStream::new(); for ChildInfo { vis, @@ -141,6 +147,7 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { } in children { let name_owned = Ident::new(&format!("into_{name}"), name.span()); + let name_ref = Ident::new(&format!("as_{name}"), name.span()); let name_mut = Ident::new(&format!("as_{name}_mut"), name.span()); let vis = match vis { Visibility::Inherited => Visibility::Restricted(VisRestricted { @@ -181,6 +188,17 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { } else { quote! { v } }; + let accessor_ref = if let Some(accessor) = accessor { + quote! { + { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #accessor.index_into(v).unwrap_or(&patch_db::value::NULL) + } + } + } else { + quote! { v } + }; let accessor_mut = if let Some(accessor) = accessor { quote! { { @@ -192,24 +210,128 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { } else { quote! { v } }; + let child_model_ty = replace_self(model_ty.clone(), &ty); + parts_args.extend(quote_spanned! { name.span() => + #name: #child_model_ty, + }); + parts_assignments.extend(quote_spanned! { name.span() => + *#accessor_mut = #name.into_value(); + }); impl_fns.extend(quote_spanned! { name.span() => - #vis fn #name_owned (self) -> patch_db::Model::<#ty> { + #vis fn #name_owned (self) -> #child_model_ty { + use patch_db::ModelExt; self.transmute(|v| #accessor_owned) } }); + impl_ref_fns.extend(quote_spanned! { name.span() => + #vis fn #name_ref (&self) -> &#child_model_ty { + use patch_db::ModelExt; + self.transmute_ref(|v| #accessor_ref) + } + }); impl_mut_fns.extend(quote_spanned! { name.span() => - #vis fn #name_mut (&mut self) -> &mut patch_db::Model::<#ty> { + #vis fn #name_mut (&mut self) -> &mut #child_model_ty { + use patch_db::ModelExt; self.transmute_mut(|v| #accessor_mut) } }); } Fns { + from_parts: quote! { + pub fn from_parts(#parts_args) -> Self { + use patch_db::ModelExt; + let mut res = patch_db::value::json!({}); + let v = &mut res; + #parts_assignments + Self::from_value(res) + } + }, impl_fns, + impl_ref_fns, impl_mut_fns, } } +fn replace_self(ty: Type, replace: &Type) -> Type { + match ty { + Type::Path(mut a) => Type::Path({ + a.path.segments = a + .path + .segments + .into_iter() + .map(|mut s| { + s.arguments = match s.arguments { + PathArguments::AngleBracketed(mut a) => PathArguments::AngleBracketed({ + a.args = a + .args + .into_iter() + .map(|a| match a { + GenericArgument::Type(Type::Path(a)) => { + GenericArgument::Type({ + if a.path.is_ident("Self") { + replace.clone() + } else { + Type::Path(a) + } + }) + } + a => a, + }) + .collect(); + a + }), + a => a, + }; + s + }) + .collect(); + a + }), + a => a, + } +} + fn build_model_struct(base: &DeriveInput, ast: &DataStruct) -> TokenStream { + let model_ty = match base + .attrs + .iter() + .filter(|attr| attr.path.is_ident("model")) + .filter_map(|attr| attr.parse_meta().ok()) + .find_map(|meta| { + if let Meta::NameValue(a) = meta { + Some(a) + } else { + None + } + }) + .ok_or_else(|| { + Error::new( + base.ident.span(), + "could not determine model type\n#[model = \"...\"] is required", + ) + }) + .and_then(|meta| { + if let Lit::Str(a) = meta.lit { + Ok(a) + } else { + Err(Error::new( + meta.lit.span(), + "syntax error: expected string literal", + )) + } + }) + .and_then(|s| syn::parse_str::(&s.value())) + { + Ok(a) => a, + Err(e) => return e.into_compile_error(), + }; + let model_ty_name = replace_self( + model_ty.clone(), + &Type::Path(TypePath { + qself: None, + path: base.ident.clone().into(), + }), + ); let serde_rename_all = base .attrs .iter() @@ -223,26 +345,73 @@ fn build_model_struct(base: &DeriveInput, ast: &DataStruct) -> TokenStream { let children = ChildInfo::from_fields(&serde_rename_all, &ast.fields); let name = &base.ident; let Fns { + from_parts, impl_fns, + impl_ref_fns, impl_mut_fns, - } = impl_fns(&children); + } = impl_fns(&model_ty, &children); quote! { - impl patch_db::HasModel for #name {} - impl patch_db::Model<#name> { + impl patch_db::HasModel for #name { + type Model = #model_ty; + } + impl #model_ty_name { + #from_parts #impl_fns + #impl_ref_fns #impl_mut_fns } } } fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { + let model_ty = match base + .attrs + .iter() + .filter(|attr| attr.path.is_ident("model")) + .filter_map(|attr| attr.parse_meta().ok()) + .find_map(|meta| { + if let Meta::NameValue(a) = meta { + Some(a) + } else { + None + } + }) + .ok_or_else(|| { + Error::new( + base.ident.span(), + "could not determine model type\n#[model = \"...\"] is required", + ) + }) + .and_then(|meta| { + if let Lit::Str(a) = meta.lit { + Ok(a) + } else { + Err(Error::new( + meta.lit.span(), + "syntax error: expected string literal", + )) + } + }) + .and_then(|s| syn::parse_str::(&s.value())) + { + Ok(a) => a, + Err(e) => return e.into_compile_error(), + }; + let model_ty_name = replace_self( + model_ty.clone(), + &Type::Path(TypePath { + qself: None, + path: base.ident.clone().into(), + }), + ); let mut match_name = None; + let mut match_name_ref = None; let mut match_name_mut = None; for arg in base .attrs .iter() .filter(|attr| attr.path.is_ident("model")) - .map(|attr| attr.parse_args::().unwrap()) + .filter_map(|attr| attr.parse_args::().ok()) { match arg { MetaNameValue { @@ -250,6 +419,11 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { lit: Lit::Str(s), .. } if path.is_ident("match") => match_name = Some(s.parse().unwrap()), + MetaNameValue { + path, + lit: Lit::Str(s), + .. + } if path.is_ident("match_ref") => match_name_ref = Some(s.parse().unwrap()), MetaNameValue { path, lit: Lit::Str(s), @@ -260,6 +434,8 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { } let match_name = match_name .unwrap_or_else(|| Ident::new(&format!("{}MatchModel", base.ident), Span::call_site())); + let match_name_ref = match_name_ref + .unwrap_or_else(|| Ident::new(&format!("{}MatchModelRef", base.ident), Span::call_site())); let match_name_mut = match_name_mut .unwrap_or_else(|| Ident::new(&format!("{}MatchModelMut", base.ident), Span::call_site())); let serde_rename_all = base @@ -304,10 +480,15 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { }) .collect(); let mut model_variants = TokenStream::new(); + let mut ref_model_variants = TokenStream::new(); let mut mut_model_variants = TokenStream::new(); - let (impl_new, impl_new_mut) = if let Some(Lit::Str(tag)) = serde_tag.remove("tag") { + let (impl_new, impl_unmatch, impl_new_ref, impl_new_mut) = if let Some(Lit::Str(tag)) = + serde_tag.remove("tag") + { if let Some(Lit::Str(content)) = serde_tag.remove("content") { let mut tag_variants = TokenStream::new(); + let mut tag_variants_unmatch = TokenStream::new(); + let mut tag_variants_ref = TokenStream::new(); let mut tag_variants_mut = TokenStream::new(); for variant in &ast.variants { let variant_name = &variant.ident; @@ -320,8 +501,22 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { #content.index_into_owned(v).unwrap_or_default() })), }); + tag_variants_unmatch.extend(quote_spanned! { variant_name.span() => + #match_name::#variant_name(v) => Self::from_value({ + let mut a = patch_db::value::json!({ #tag: #variant_accessor }); + a[#content] = v.into_value(); + a + }), + }); + tag_variants_ref.extend(quote_spanned! { variant_name.span() => + Some(#variant_accessor) => #match_name_ref::#variant_name(self.transmute_ref(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #content.index_into(v).unwrap_or(&patch_db::value::NULL) + })), + }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => - Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute(|v| { + Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute_mut(|v| { #[allow(unused_imports)] use patch_db::value::index::Index; #content.index_or_insert(v) @@ -330,13 +525,29 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { } ( quote! { - match self[#tag].as_str() { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { #tag_variants _ => #match_name::Error(self.into_inner()), } }, quote! { - match self[#tag].as_str() { + use patch_db::ModelExt; + match value { + #tag_variants_unmatch + #match_name::Error(v) => Self::from_value(v), + } + }, + quote! { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { + #tag_variants_ref + _ => #match_name_ref::Error(&*self), + } + }, + quote! { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { #tag_variants_mut _ => #match_name_mut::Error(&mut *self), } @@ -344,6 +555,8 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { ) } else { let mut tag_variants = TokenStream::new(); + let mut tag_variants_unmatch = TokenStream::new(); + let mut tag_variants_ref = TokenStream::new(); let mut tag_variants_mut = TokenStream::new(); for variant in &ast.variants { let variant_name = &variant.ident; @@ -356,8 +569,22 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { #variant_accessor.index_into_owned(v).unwrap_or_default() })), }); + tag_variants_unmatch.extend(quote_spanned! { variant_name.span() => + #match_name::#variant_name(v) => Self::from_value({ + let mut a = v.into_value(); + a[#tag] = patch_db::Value::String(std::sync::Arc::new(#variant_accessor.to_owned())); + a + }), + }); + tag_variants_ref.extend(quote_spanned! { variant_name.span() => + Some(#variant_accessor) => #match_name_ref::#variant_name(self.transmute_ref(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_into(v).unwrap_or(&patch_db::value::NULL) + })), + }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => - Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute(|v| { + Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute_mut(|v| { #[allow(unused_imports)] use patch_db::value::index::Index; #variant_accessor.index_or_insert(v) @@ -366,21 +593,39 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { } ( quote! { - match self[#tag].as_str() { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { #tag_variants - _ => #match_name::Error(self.into_inner()), + _ => #match_name::Error(self.into_value()), } }, quote! { - match self[#tag].as_str() { + use patch_db::ModelExt; + match value { + #tag_variants_unmatch + #match_name::Error(v) => Self::from_value(v), + } + }, + quote! { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { + #tag_variants_ref + _ => #match_name_ref::Error(self.as_value()), + } + }, + quote! { + use patch_db::ModelExt; + match self.as_value()[#tag].as_str() { #tag_variants_mut - _ => #match_name_mut::Error(&mut *self), + _ => #match_name_mut::Error(self.as_value_mut()), } }, ) } } else { let mut tag_variants = TokenStream::new(); + let mut tag_variants_unmatch = TokenStream::new(); + let mut tag_variants_ref = TokenStream::new(); let mut tag_variants_mut = TokenStream::new(); for variant in &ast.variants { let variant_name = &variant.ident; @@ -394,9 +639,25 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { })) } else }); + tag_variants_unmatch.extend(quote_spanned! { variant_name.span() => + #match_name::#variant_name(v) => Self::from_value({ + let mut a = patch_db::value::json!({}); + a[#variant_accessor] = v.into_value(); + a + }), + }); + tag_variants_ref.extend(quote_spanned! { variant_name.span() => + if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) { + #match_name_ref::#variant_name(self.transmute_ref(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_into(v).unwrap_or(&patch_db::value::NULL) + })) + } else + }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) { - #match_name_mut::#variant_name(self.transmute(|v| { + #match_name_mut::#variant_name(self.transmute_mut(|v| { #[allow(unused_imports)] use patch_db::value::index::Index; #variant_accessor.index_or_insert(v) @@ -406,11 +667,26 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { } ( quote! { + use patch_db::ModelExt; #tag_variants { #match_name::Error(self.into_inner()), } }, quote! { + use patch_db::ModelExt; + match value { + #tag_variants_unmatch + #match_name::Error(v) => Self::from_value(v), + } + }, + quote! { + use patch_db::ModelExt; + #tag_variants_ref { + #match_name_ref::Error(&*self), + } + }, + quote! { + use patch_db::ModelExt; #tag_variants_mut { #match_name_mut::Error(&mut *self), } @@ -429,22 +705,31 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { .into_compile_error() } }; + let model_variant_ty = replace_self(model_ty.clone(), &ty); model_variants.extend(quote_spanned! { name.span() => - #name(patch_db::Model<#ty>), + #name(#model_variant_ty), + }); + ref_model_variants.extend(quote_spanned! { name.span() => + #name(&'a #model_variant_ty), }); mut_model_variants.extend(quote_spanned! { name.span() => - #name(&'a mut patch_db::Model<#ty>), + #name(&'a mut #model_variant_ty), }); } let name = &base.ident; let vis = &base.vis; quote! { - impl patch_db::HasModel for #name {} + impl patch_db::HasModel for #name { + type Model = #model_ty; + } - impl patch_db::Model<#name> { - #vis fn matchable(&self) -> #match_name { + impl #model_ty_name { + #vis fn into_match(self) -> #match_name { #impl_new } + #vis fn from_match(value: #match_name) -> Self { + #impl_unmatch + } } #vis enum #match_name { @@ -452,8 +737,19 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { Error(patch_db::Value), } - impl patch_db::Model<#name> { - #vis fn matchable<'a>(&'a self) -> #match_name_mut<'a> { + impl #model_ty_name { + #vis fn as_match<'a>(&'a self) -> #match_name_ref<'a> { + #impl_new_ref + } + } + + #vis enum #match_name_ref<'a> { + #ref_model_variants + Error(&'a patch_db::Value), + } + + impl #model_ty_name { + #vis fn as_match_mut<'a>(&'a mut self) -> #match_name_mut<'a> { #impl_new_mut } } diff --git a/patch-db/src/lib.rs b/patch-db/src/lib.rs index 33b7882..c85d675 100644 --- a/patch-db/src/lib.rs +++ b/patch-db/src/lib.rs @@ -16,10 +16,11 @@ mod test; pub use imbl_value as value; pub use imbl_value::Value; -pub use model::{HasModel, Map, Model, ModelExt}; +pub use model::{HasModel, Model, ModelExt}; pub use patch::{DiffPatch, Dump, Revision}; pub use patch_db_macro::HasModel; pub use store::{PatchDb, Store}; +use tokio::sync::TryLockError; pub use {json_patch, json_ptr}; pub type Subscriber = tokio::sync::mpsc::UnboundedReceiver>; @@ -47,5 +48,7 @@ pub enum Error { #[error("Node Does Not Exist: {0}")] NodeDoesNotExist(JsonPointer), #[error("Provided Function Panicked! {0}")] - Panick(String), + Panic(String), + #[error("Would Block")] + WouldBlock(#[from] TryLockError), } diff --git a/patch-db/src/model.rs b/patch-db/src/model.rs index 6a9bc91..4fa7d33 100644 --- a/patch-db/src/model.rs +++ b/patch-db/src/model.rs @@ -1,193 +1,179 @@ -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; +use imbl_value::Value; -use imbl::OrdSet; -use imbl_value::{InternedString, Value}; -use serde::de::DeserializeOwned; -use serde::Serialize; - -pub trait HasModel {} - -/// &mut Model <=> &mut Value - -#[repr(transparent)] -#[derive(Debug)] -pub struct Model { - value: Value, - phantom: PhantomData, +pub trait HasModel: Sized { + type Model: Model; } -impl Clone for Model { - fn clone(&self) -> Self { - Self { - value: self.value.clone(), - phantom: PhantomData, + +mod sealed { + use super::*; + pub trait ModelMarker { + fn into_value(self) -> Value; + fn from_value(value: Value) -> Self; + fn as_value<'a>(&'a self) -> &'a Value; + fn value_as<'a>(value: &'a Value) -> &'a Self; + fn as_value_mut<'a>(&'a mut self) -> &'a mut Value; + fn value_as_mut<'a>(value: &'a mut Value) -> &'a mut Self; + } + impl ModelMarker for T + where + T: From + Into + Sized, + for<'a> &'a T: From<&'a Value> + Into<&'a Value>, + for<'a> &'a mut T: From<&'a mut Value> + Into<&'a mut Value>, + { + fn into_value(self) -> Value { + self.into() + } + fn from_value(value: Value) -> Self { + value.into() + } + fn as_value<'a>(&'a self) -> &'a Value { + self.into() + } + fn value_as<'a>(value: &'a Value) -> &'a Self { + value.into() + } + fn as_value_mut<'a>(&'a mut self) -> &'a mut Value { + self.into() + } + fn value_as_mut<'a>(value: &'a mut Value) -> &'a mut Self { + value.into() } } } -impl Deref for Model { - type Target = Value; - fn deref(&self) -> &Self::Target { - &self.value - } + +pub trait Model: sealed::ModelMarker + Sized { + type Model: Model; } -impl DerefMut for Model { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.value +pub trait ModelExt: Model { + fn into_value(self) -> Value { + ::into_value(self) } -} -impl Model { - pub fn new(value: Value) -> Self { - Self { - value, - phantom: PhantomData, - } + fn from_value(value: Value) -> Self { + ::from_value(value) } - pub fn into_inner(self) -> Value { - self.value + fn as_value<'a>(&'a self) -> &'a Value { + ::as_value(self) } - pub fn transmute(self, f: impl FnOnce(Value) -> Value) -> Model { - Model { - value: f(self.value), - phantom: PhantomData, - } + fn value_as<'a>(value: &'a Value) -> &'a Self { + ::value_as(value) } - pub fn transmute_mut<'a, U>( + fn as_value_mut<'a>(&'a mut self) -> &'a mut Value { + ::as_value_mut(self) + } + fn value_as_mut<'a>(value: &'a mut Value) -> &'a mut Self { + ::value_as_mut(value) + } + fn transmute(self, f: impl FnOnce(Value) -> Value) -> Self::Model { + Self::Model::::from_value(f(::into_value(self))) + } + fn transmute_ref<'a, U>( + &'a self, + f: impl for<'b> FnOnce(&'b Value) -> &'b Value, + ) -> &'a Self::Model { + Self::Model::::value_as(f(::as_value(self))) + } + fn transmute_mut<'a, U>( &'a mut self, - f: impl for<'c> FnOnce(&'c mut Value) -> &'c mut Value, - ) -> &'a mut Model { - unsafe { - std::mem::transmute::<&'a mut Value, &'a mut Model>(f(std::mem::transmute::< - &'a mut Model, - &'a mut Value, - >(self))) - } - } -} - -pub trait ModelExt: Deref + DerefMut { - type T: DeserializeOwned + Serialize; - fn set(&mut self, value: &Self::T) -> Result<(), imbl_value::Error> { - *self.deref_mut() = imbl_value::to_value(value)?; - Ok(()) - } - fn get(&self) -> Result { - imbl_value::from_value(self.deref().clone()) - } -} - -impl ModelExt for Model { - type T = T; -} - -impl Model> { - pub fn transpose(self) -> Option> { - if self.is_null() { - None - } else { - Some(self.transmute(|a| a)) - } - } -} - -pub trait Map: DeserializeOwned + Serialize { - type Key; - type Value; - type Error: From; -} - -impl Model -where - T::Key: DeserializeOwned + Ord + Clone, -{ - pub fn keys(&self) -> Result, T::Error> { - use serde::de::Error; - use serde::Deserialize; - match &**self { - Value::Object(o) => o - .keys() - .cloned() - .map(|k| { - T::Key::deserialize(imbl_value::de::InternedStringDeserializer::from(k)) - .map_err(|e| { - imbl_value::Error { - kind: imbl_value::ErrorKind::Deserialization, - source: e, - } - .into() - }) - }) - .collect(), - v => Err(imbl_value::Error { - source: imbl_value::ErrorSource::custom(format!("expected object found {v}")), - kind: imbl_value::ErrorKind::Deserialization, - } - .into()), - } - } -} -impl Model -where - T::Key: AsRef, -{ - pub fn idx(self, key: &T::Key) -> Option> { - match &*self { - Value::Object(o) if o.contains_key(key.as_ref()) => Some(self.transmute(|v| { - use imbl_value::index::Index; - key.as_ref().index_into_owned(v).unwrap() - })), - _ => None, - } - } -} -impl Model -where - T::Key: AsRef, - T::Value: Serialize, -{ - pub fn insert(&mut self, key: &T::Key, value: &T::Value) -> Result<(), T::Error> { - use serde::ser::Error; - let v = imbl_value::to_value(value)?; - match &mut **self { - Value::Object(o) => { - o.insert(InternedString::intern(key.as_ref()), v); - Ok(()) - } - _ => Err(imbl_value::Error { - source: imbl_value::ErrorSource::custom(format!("expected object found {v}")), - kind: imbl_value::ErrorKind::Serialization, - } - .into()), - } + f: impl for<'b> FnOnce(&'b mut Value) -> &'b mut Value, + ) -> &'a mut Self::Model { + Self::Model::::value_as_mut(f(::as_value_mut(self))) } } +impl> ModelExt for M {} #[cfg(test)] mod test { - use super::*; - use crate as patch_db; - use imbl_value::json; + use crate::model::sealed::ModelMarker; + use imbl_value::{from_value, json, to_value, Value}; + use serde::{de::DeserializeOwned, Serialize}; + use std::marker::PhantomData; + + /// &mut Model <=> &mut Value + #[repr(transparent)] + #[derive(Debug)] + pub struct Model { + value: Value, + phantom: PhantomData, + } + impl Model { + pub fn de(self) -> Result { + from_value(self.value) + } + } + impl Model { + pub fn ser(&mut self, value: &T) -> Result<(), imbl_value::Error> { + self.value = to_value(value)?; + Ok(()) + } + } + impl Clone for Model { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + phantom: PhantomData, + } + } + } + impl From for Model { + fn from(value: Value) -> Self { + Self { + value, + phantom: PhantomData, + } + } + } + impl From> for Value { + fn from(value: Model) -> Self { + value.value + } + } + impl<'a, T> From<&'a Value> for &'a Model { + fn from(value: &'a Value) -> Self { + unsafe { std::mem::transmute(value) } + } + } + impl<'a, T> From<&'a Model> for &'a Value { + fn from(value: &'a Model) -> Self { + unsafe { std::mem::transmute(value) } + } + } + impl<'a, T> From<&'a mut Value> for &mut Model { + fn from(value: &'a mut Value) -> Self { + unsafe { std::mem::transmute(value) } + } + } + impl<'a, T> From<&'a mut Model> for &mut Value { + fn from(value: &'a mut Model) -> Self { + unsafe { std::mem::transmute(value) } + } + } + impl patch_db::Model for Model { + type Model = Model; + } #[derive(crate::HasModel)] + #[model = "Model"] // #[macro_debug] struct Foo { a: Bar, } #[derive(crate::HasModel)] + #[model = "Model"] struct Bar { b: String, } fn mutate_fn(v: &mut Model) { let mut a = v.as_a_mut(); - a.as_b_mut().set(&"NotThis".into()).unwrap(); - a.as_b_mut().set(&"Replaced".into()).unwrap(); + a.as_b_mut().ser(&"NotThis".into()).unwrap(); + a.as_b_mut().ser(&"Replaced".into()).unwrap(); } #[test] fn test() { - let mut model = Model::::new(imbl_value::json!({ + let mut model = Model::::from(imbl_value::json!({ "a": { "b": "ReplaceMe" } @@ -195,8 +181,8 @@ mod test { mutate_fn(&mut model); mutate_fn(&mut model); assert_eq!( - &*model, - &imbl_value::json!({ + model.as_value(), + &json!({ "a": { "b": "Replaced" } diff --git a/patch-db/src/store.rs b/patch-db/src/store.rs index 6a70003..211d8cb 100644 --- a/patch-db/src/store.rs +++ b/patch-db/src/store.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use fd_lock_rs::FdLock; +use futures::FutureExt; use imbl_value::{InternedString, Value}; use json_patch::PatchError; use json_ptr::{JsonPointer, SegList}; @@ -360,7 +361,7 @@ impl PatchDb { let mut store = self.store.write().await; let old = store.persistent.clone(); let (new, res) = std::panic::catch_unwind(move || f(old)).map_err(|e| { - Error::Panick( + Error::Panic( e.downcast() .map(|a| *a) .unwrap_or_else(|_| "UNKNOWN".to_owned()), @@ -370,4 +371,33 @@ impl PatchDb { store.apply(diff).await?; Ok((new, res)) } + pub async fn run_idempotent(&self, f: F) -> Result<(Value, T), E> + where + F: Fn(Value) -> Fut + Send + Sync + UnwindSafe, + for<'a> &'a F: UnwindSafe, + Fut: std::future::Future> + UnwindSafe, + E: From, + { + let store = self.store.read().await; + let old = store.persistent.clone(); + drop(store); + loop { + let (new, res) = async { f(old.clone()).await } + .catch_unwind() + .await + .map_err(|e| { + Error::Panic( + e.downcast() + .map(|a| *a) + .unwrap_or_else(|_| "UNKNOWN".to_owned()), + ) + })??; + let mut store = self.store.write().await; + if &old == &store.persistent { + let diff = diff(&store.persistent, &new); + store.apply(diff).await?; + return Ok((new, res)); + } + } + } } diff --git a/patch-db/src/test.rs b/patch-db/src/test.rs index ff5bf71..8e515a4 100644 --- a/patch-db/src/test.rs +++ b/patch-db/src/test.rs @@ -1,7 +1,7 @@ use std::future::Future; use std::sync::Arc; -use imbl_value::Value; +use imbl_value::{json, Value}; use json_ptr::JsonPointer; use patch_db::{HasModel, PatchDb, Revision}; use proptest::prelude::*; @@ -15,14 +15,14 @@ async fn init_db(db_name: String) -> PatchDb { let db = PatchDb::open(db_name).await.unwrap(); db.put( &JsonPointer::<&'static str>::default(), - &Sample { - a: "test1".to_string(), - b: Child { - a: "test2".to_string(), - b: 1, - c: NewType(None), + &json!({ + "a": "test1", + "b": { + "a": "test2", + "b": 1, + "c": null, }, - }, + }), ) .await .unwrap(); @@ -71,19 +71,19 @@ proptest! { } } -#[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] -pub struct Sample { - a: String, - #[model] - b: Child, -} +// #[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] +// pub struct Sample { +// a: String, +// #[model] +// b: Child, +// } -#[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] -pub struct Child { - a: String, - b: usize, - c: NewType, -} +// #[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] +// pub struct Child { +// a: String, +// b: usize, +// c: NewType, +// } -#[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] -pub struct NewType(Option>); +// #[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)] +// pub struct NewType(Option>);