From f30d90adf0863960e9175c7f3a9b672285e62d33 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 27 Feb 2023 17:02:29 -0700 Subject: [PATCH] fix models again --- patch-db-macro-internals/src/lib.rs | 514 ++++++++-------------------- patch-db/src/lib.rs | 4 +- patch-db/src/model.rs | 309 +++++++++-------- 3 files changed, 306 insertions(+), 521 deletions(-) diff --git a/patch-db-macro-internals/src/lib.rs b/patch-db-macro-internals/src/lib.rs index 3f62b9e..4de78c3 100644 --- a/patch-db-macro-internals/src/lib.rs +++ b/patch-db-macro-internals/src/lib.rs @@ -8,30 +8,14 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::{Comma, Paren, Pub}; use syn::{ - Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, GenericArgument, Ident, Lit, - LitInt, LitStr, MetaNameValue, Path, PathArguments, Type, VisPublic, VisRestricted, Visibility, + Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Ident, Lit, LitInt, LitStr, + MetaNameValue, Path, Type, VisRestricted, Visibility, }; pub fn build_model(item: &DeriveInput) -> TokenStream { - let mut model_name = None; - for arg in item - .attrs - .iter() - .filter(|attr| attr.path.is_ident("model")) - .map(|attr| attr.parse_args::().unwrap()) - { - match arg { - MetaNameValue { - path, - lit: Lit::Str(s), - .. - } if path.is_ident("name") => model_name = Some(s.parse().unwrap()), - _ => (), - } - } let res = match &item.data { - Data::Struct(struct_ast) => build_model_struct(item, struct_ast, model_name), - Data::Enum(enum_ast) => build_model_enum(item, enum_ast, model_name), + Data::Struct(struct_ast) => build_model_struct(item, struct_ast), + Data::Enum(enum_ast) => build_model_enum(item, enum_ast), _ => panic!("Models can only be created for Structs and Enums"), }; if let Some(dbg) = item.attrs.iter().find(|a| a.path.is_ident("macro_debug")) { @@ -83,7 +67,6 @@ struct ChildInfo { name: Ident, accessor: Option, ty: Type, - has_model: bool, } impl ChildInfo { fn from_fields(serde_rename_all: &Option, fields: &Fields) -> Vec { @@ -93,7 +76,6 @@ impl ChildInfo { for field in &f.named { let ident = field.ident.clone().unwrap(); let ty = field.ty.clone(); - let has_model = field.attrs.iter().any(|attr| attr.path.is_ident("model")); let accessor = if field .attrs .iter() @@ -114,7 +96,6 @@ impl ChildInfo { name: ident, accessor, ty, - has_model, }) } } @@ -122,7 +103,6 @@ impl ChildInfo { for (i, field) in f.unnamed.iter().enumerate() { let ident = Ident::new(&format!("idx_{i}"), field.span()); let ty = field.ty.clone(); - let has_model = field.attrs.iter().any(|attr| attr.path.is_ident("model")); let accessor = if f.unnamed.len() > 1 { Some(Lit::Int(LitInt::new( &format!("{}", i), @@ -136,7 +116,6 @@ impl ChildInfo { name: ident, accessor, ty, - has_model, }) } } @@ -146,27 +125,6 @@ impl ChildInfo { } } -fn separate_option(ty: &Type) -> (bool, &Type) { - match ty { - Type::Path(p) => { - if let Some(s) = p.path.segments.first() { - if s.ident == "Option" { - if let PathArguments::AngleBracketed(a) = &s.arguments { - if a.args.len() == 1 { - if let GenericArgument::Type(a) = &a.args[0] { - return (true, a); - } - } - } - } - } - } - _ => (), - } - - (false, ty) -} - struct Fns { impl_fns: TokenStream, impl_mut_fns: TokenStream, @@ -180,9 +138,10 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { name, accessor, ty, - has_model, } in children { + let name_owned = Ident::new(&format!("into_{name}"), name.span()); + let name_mut = Ident::new(&format!("as_{name}_mut"), name.span()); let vis = match vis { Visibility::Inherited => Visibility::Restricted(VisRestricted { pub_token: Pub::default(), @@ -211,54 +170,38 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { } a => a.clone(), }; - let (optional, ty) = separate_option(ty); - let (model_ty, model_mut_ty) = if *has_model { - ( - quote_spanned! { name.span() => - <#ty as patch_db::HasModel>::Model - }, - quote_spanned! { name.span() => - <<#ty as patch_db::HasModel>::Model as patch_db::Model>::Mut<'a> - }, - ) + let accessor_owned = if let Some(accessor) = accessor { + quote! { + { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #accessor.index_into_owned(v).unwrap_or_default() + } + } } else { - ( - quote_spanned! { name.span() => - patch_db::GenericModel::<#ty> - }, - quote_spanned! { name.span() => - patch_db::GenericModelMut::<'a, #ty> - }, - ) + quote! { v } }; - let accessor = if let Some(accessor) = accessor { - quote! { [#accessor] } + let accessor_mut = if let Some(accessor) = accessor { + quote! { + { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #accessor.index_or_insert(v) + } + } } else { - quote! {} + quote! { v } }; - if optional { - impl_fns.extend(quote_spanned! { name.span() => - #vis fn #name (&self) -> patch_db::OptionModel<#model_ty> { - as patch_db::Model>::new((**self) #accessor .clone()) - } - }); - impl_mut_fns.extend(quote_spanned! { name.span() => - #vis fn #name (&'a mut self) -> patch_db::OptionModelMut<'a, #model_mut_ty> { - as patch_db::ModelMut<'a>>::new(&mut (**self) #accessor) - } - }); - } else { - impl_fns.extend(quote_spanned! { name.span() => - #vis fn #name (&self) -> #model_ty { - <#model_ty as patch_db::Model>::new((**self) #accessor .clone()) - } - }); - impl_mut_fns.extend(quote_spanned! { name.span() => - #vis fn #name (&'a mut self) -> #model_mut_ty { - <#model_mut_ty as patch_db::ModelMut<'a>>::new(&mut (**self) #accessor) - } - }); - } + impl_fns.extend(quote_spanned! { name.span() => + #vis fn #name_owned (self) -> patch_db::Model::<#ty> { + self.transmute(|v| #accessor_owned) + } + }); + impl_mut_fns.extend(quote_spanned! { name.span() => + #vis fn #name_mut (&mut self) -> &mut patch_db::Model::<#ty> { + self.transmute_mut(|v| #accessor_mut) + } + }); } Fns { impl_fns, @@ -266,17 +209,7 @@ fn impl_fns(children: &[ChildInfo]) -> Fns { } } -fn build_model_struct( - base: &DeriveInput, - ast: &DataStruct, - module_name: Option, -) -> TokenStream { - let module_name = module_name.unwrap_or_else(|| { - Ident::new( - &format!("{}_model", heck::AsSnakeCase(base.ident.to_string())), - proc_macro2::Span::call_site(), - ) - }); +fn build_model_struct(base: &DeriveInput, ast: &DataStruct) -> TokenStream { let serde_rename_all = base .attrs .iter() @@ -289,82 +222,46 @@ fn build_model_struct( }); let children = ChildInfo::from_fields(&serde_rename_all, &ast.fields); let name = &base.ident; - let vis = &base.vis; let Fns { impl_fns, impl_mut_fns, } = impl_fns(&children); quote! { - impl patch_db::HasModel for #name { - type Model = #module_name::Model; - } - #vis mod #module_name { - use super::*; - - #[derive(Debug)] - pub struct Model(patch_db::Value); - impl patch_db::Model for Model { - type T = #name; - type Mut<'a> = ModelMut<'a>; - fn new(value: patch_db::Value) -> Self { - Self(value) - } - fn into_inner(self) -> patch_db::Value { - self.0 - } - } - impl ::core::ops::Deref for Model { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl ::core::ops::DerefMut for Model { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - impl Model { - #impl_fns - } - - #[derive(Debug)] - pub struct ModelMut<'a>(&'a mut patch_db::Value); - impl<'a> patch_db::ModelMut<'a> for ModelMut<'a> { - type T = #name; - fn new(value: &'a mut patch_db::Value) -> Self { - Self(value) - } - fn into_inner(self) -> &'a mut patch_db::Value { - self.0 - } - } - impl<'a> ::core::ops::Deref for ModelMut<'a> { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - &*self.0 - } - } - impl<'a> ::core::ops::DerefMut for ModelMut<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } - } - impl<'a> ModelMut<'a> { - #impl_mut_fns - } + impl patch_db::HasModel for #name {} + impl patch_db::Model<#name> { + #impl_fns + #impl_mut_fns } } } -fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option) -> TokenStream { - let module_name = module_name.unwrap_or_else(|| { - Ident::new( - &format!("{}_model", heck::AsSnakeCase(base.ident.to_string())), - proc_macro2::Span::call_site(), - ) - }); - let mut children = Vec::new(); +fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream { + let mut match_name = 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()) + { + match arg { + MetaNameValue { + path, + 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_mut") => match_name_mut = Some(s.parse().unwrap()), + _ => (), + } + } + let match_name = match_name + .unwrap_or_else(|| Ident::new(&format!("{}MatchModel", 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 .attrs .iter() @@ -408,55 +305,40 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option - Some(#variant_accessor) => Model::#variant_name(#variant_model(value[#content].clone())), + Some(#variant_accessor) => #match_name::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #content.index_into_owned(v).unwrap_or_default() + })), }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => - Some(#variant_accessor) => ModelMut::#variant_name(#variant_model_mut(&mut value[#content])), + Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #content.index_or_insert(v) + })), }); } ( quote! { - match value[#tag].as_str() { + match self[#tag].as_str() { #tag_variants - _ => Model::Error(value), + _ => #match_name::Error(self.into_inner()), } }, quote! { - match value[#tag].as_str() { + match self[#tag].as_str() { #tag_variants_mut - _ => ModelMut::Error(value), + _ => #match_name_mut::Error(&mut *self), } }, ) @@ -465,30 +347,34 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option - Some(#variant_accessor) => Model::#variant_name(#variant_model(value)), + Some(#variant_accessor) => #match_name::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_into_owned(v).unwrap_or_default() + })), }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => - Some(#variant_accessor) => ModelMut::#variant_name(#variant_model_mut(value)), + Some(#variant_accessor) => #match_name_mut::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_or_insert(v) + })), }); } ( quote! { - match value[#tag].as_str() { + match self[#tag].as_str() { #tag_variants - _ => Model::Error(value), + _ => #match_name::Error(self.into_inner()), } }, quote! { - match value[#tag].as_str() { + match self[#tag].as_str() { #tag_variants_mut - _ => ModelMut::Error(value), + _ => #match_name_mut::Error(&mut *self), } }, ) @@ -498,213 +384,83 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) { - Model::#variant_name(#variant_model(value[#variant_accessor].clone())) + #match_name::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_into_owned(v).unwrap_or_default() + })) } else }); tag_variants_mut.extend(quote_spanned! { variant_name.span() => if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) { - ModelMut::#variant_name(#variant_model_mut(&mut value[#variant_accessor])) + #match_name_mut::#variant_name(self.transmute(|v| { + #[allow(unused_imports)] + use patch_db::value::index::Index; + #variant_accessor.index_or_insert(v) + })) } else }); } ( quote! { #tag_variants { - Model::Error(value), + #match_name::Error(self.into_inner()), } }, quote! { #tag_variants_mut { - ModelMut::Error(value), + #match_name_mut::Error(&mut *self), } }, ) }; for variant in &ast.variants { let name = &variant.ident; - let model_name = Ident::new(&format!("{}Model", variant.ident), variant.ident.span()); - let model_name_mut = - Ident::new(&format!("{}ModelMut", variant.ident), variant.ident.span()); - let serde_rename_all = variant - .attrs - .iter() - .filter(|attr| attr.path.is_ident("serde")) - .filter_map(|attr| attr.parse_args::().ok()) - .filter(|nv| nv.path.is_ident("rename_all")) - .find_map(|nv| match nv.lit { - Lit::Str(s) => Some(s.value()), - _ => None, - }); + let ty = match &variant.fields { + Fields::Unnamed(a) if a.unnamed.len() == 1 => &a.unnamed.first().unwrap().ty, + a => { + return Error::new( + a.span(), + "Can only derive HasModel for enums with newtype variants", + ) + .into_compile_error() + } + }; model_variants.extend(quote_spanned! { name.span() => - #name(#model_name), + #name(patch_db::Model<#ty>), }); mut_model_variants.extend(quote_spanned! { name.span() => - #name(#model_name_mut<'a>), - }); - let children: Vec<_> = ChildInfo::from_fields(&serde_rename_all, &variant.fields) - .into_iter() - .map(|c| ChildInfo { - vis: Visibility::Public(VisPublic { - pub_token: Pub::default(), - }), - ..c - }) - .collect(); - let Fns { - impl_fns, - impl_mut_fns, - } = impl_fns(&children); - decl_model_variants.extend(quote_spanned! { name.span() => - #[derive(Debug)] - pub struct #model_name(patch_db::Value); - impl ::core::ops::Deref for #model_name { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl ::core::ops::DerefMut for #model_name { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - impl #model_name { - #impl_fns - } - - #[derive(Debug)] - pub struct #model_name_mut<'a>(&'a mut patch_db::Value); - impl<'a> ::core::ops::Deref for #model_name_mut<'a> { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - &*self.0 - } - } - impl<'a> ::core::ops::DerefMut for #model_name_mut<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } - } - impl<'a> #model_name_mut<'a> { - #impl_mut_fns - } - }); - into_inner.extend(quote_spanned! { name.span() => - Model::#name(a) => a.0, - }); - deref.extend(quote_spanned! { name.span() => - Model::#name(a) => &a.0, - }); - deref_mut.extend(quote_spanned! { name.span() => - Model::#name(a) => &mut a.0, - }); - mut_into_inner.extend(quote_spanned! { name.span() => - ModelMut::#name(a) => a.0, - }); - mut_deref.extend(quote_spanned! { name.span() => - ModelMut::#name(a) => &*a, - }); - mut_deref_mut.extend(quote_spanned! { name.span() => - ModelMut::#name(a) => &mut *a, + #name(&'a mut patch_db::Model<#ty>), }); } let name = &base.ident; let vis = &base.vis; - let Fns { - impl_fns, - impl_mut_fns, - } = impl_fns(&children); quote! { - impl patch_db::HasModel for #name { - type Model = #module_name::Model; + impl patch_db::HasModel for #name {} + + impl patch_db::Model<#name> { + #vis fn matchable(&self) -> #match_name { + #impl_new + } } - #vis mod #module_name { - use super::*; - #[derive(Debug)] - pub enum Model { - #model_variants - Error(patch_db::Value), - } - impl patch_db::Model for Model { - type T = #name; - type Mut<'a> = ModelMut<'a>; - fn new(value: patch_db::Value) -> Self { - #impl_new - } - fn into_inner(self) -> patch_db::Value { - match self { - #into_inner - Model::Error(a) => a, - } - } - } - impl ::core::ops::Deref for Model { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - match self { - #deref - Model::Error(a) => &a, - } - } - } - impl ::core::ops::DerefMut for Model { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - #deref_mut - Model::Error(a) => &mut a, - } - } - } - impl Model { - #impl_fns - } + #vis enum #match_name { + #model_variants + Error(patch_db::Value), + } - #[derive(Debug)] - pub enum ModelMut<'a> { - #mut_model_variants - Error(&'a mut patch_db::Value), - } - impl<'a> patch_db::ModelMut<'a> for ModelMut<'a> { - type T = #name; - fn new(value: &'a mut patch_db::Value) -> Self { - #impl_new_mut - } - fn into_inner(self) -> &'a mut patch_db::Value { - match self { - #mut_into_inner - ModelMut::Error(a) => a, - } - } - } - impl<'a> ::core::ops::Deref for ModelMut<'a> { - type Target = patch_db::Value; - fn deref(&self) -> &Self::Target { - match self { - #mut_deref - ModelMut::Error(a) => &*a, - } - } - } - impl<'a> ::core::ops::DerefMut for ModelMut<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - #mut_deref_mut - ModelMut::Error(a) => a, - } - } - } - impl<'a> ModelMut<'a> { - #impl_mut_fns + impl patch_db::Model<#name> { + #vis fn matchable<'a>(&'a self) -> #match_name_mut<'a> { + #impl_new_mut } + } - #decl_model_variants + #vis enum #match_name_mut<'a> { + #mut_model_variants + Error(&'a mut patch_db::Value), } } } diff --git a/patch-db/src/lib.rs b/patch-db/src/lib.rs index 9586093..d5070c0 100644 --- a/patch-db/src/lib.rs +++ b/patch-db/src/lib.rs @@ -16,9 +16,7 @@ mod test; pub use imbl_value as value; pub use imbl_value::Value; -pub use model::{ - GenericModel, GenericModelMut, HasModel, Model, ModelMut, OptionModel, OptionModelMut, -}; +pub use model::{HasModel, Model, ModelExt}; pub use patch::{DiffPatch, Dump, Revision}; pub use patch_db_macro::HasModel; pub use store::{PatchDb, Store}; diff --git a/patch-db/src/model.rs b/patch-db/src/model.rs index a72298b..6a9bc91 100644 --- a/patch-db/src/model.rs +++ b/patch-db/src/model.rs @@ -1,24 +1,71 @@ 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 { - type Model: Model; +pub trait HasModel {} + +/// &mut Model <=> &mut Value + +#[repr(transparent)] +#[derive(Debug)] +pub struct Model { + value: Value, + phantom: PhantomData, +} +impl Clone for Model { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + phantom: PhantomData, + } + } +} +impl Deref for Model { + type Target = Value; + fn deref(&self) -> &Self::Target { + &self.value + } +} +impl DerefMut for Model { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} +impl Model { + pub fn new(value: Value) -> Self { + Self { + value, + phantom: PhantomData, + } + } + pub fn into_inner(self) -> Value { + self.value + } + pub fn transmute(self, f: impl FnOnce(Value) -> Value) -> Model { + Model { + value: f(self.value), + phantom: PhantomData, + } + } + pub 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 Model: Deref + DerefMut { +pub trait ModelExt: Deref + DerefMut { type T: DeserializeOwned + Serialize; - type Mut<'a>: ModelMut<'a> - where - Self: 'a; - fn new(value: Value) -> Self; - fn into_inner(self) -> Value; - fn as_mut<'a>(&'a mut self) -> Self::Mut<'a> { - Self::Mut::new(&mut *self) - } fn set(&mut self, value: &Self::T) -> Result<(), imbl_value::Error> { *self.deref_mut() = imbl_value::to_value(value)?; Ok(()) @@ -28,148 +75,132 @@ pub trait Model: Deref + DerefMut { } } -pub trait ModelMut<'a>: Deref + DerefMut { - type T: DeserializeOwned + Serialize; - fn new(value: &'a mut Value) -> Self; - fn into_inner(self) -> &'a mut Value; - 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()) - } -} - -pub struct GenericModel { - value: Value, - phantom: PhantomData, -} -impl Deref for GenericModel { - type Target = Value; - fn deref(&self) -> &Self::Target { - &self.value - } -} -impl DerefMut for GenericModel { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.value - } -} -impl Model for GenericModel { +impl ModelExt for Model { type T = T; - type Mut<'a> = GenericModelMut<'a, T> where T: 'a; - fn new(value: Value) -> Self { - Self { - value, - phantom: PhantomData, - } - } - fn into_inner(self) -> Value { - self.value - } } -pub struct GenericModelMut<'a, T> { - value: &'a mut Value, - phantom: PhantomData, -} -impl<'a, T> Deref for GenericModelMut<'a, T> { - type Target = Value; - fn deref(&self) -> &Self::Target { - &*self.value - } -} -impl<'a, T> DerefMut for GenericModelMut<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.value - } -} -impl<'a, T: DeserializeOwned + Serialize> ModelMut<'a> for GenericModelMut<'a, T> { - type T = T; - fn new(value: &'a mut Value) -> Self { - Self { - value, - phantom: PhantomData, - } - } - fn into_inner(self) -> &'a mut Value { - self.value - } -} - -pub struct OptionModel { - value: Value, - phantom: PhantomData, -} - -impl OptionModel { - pub fn check(self) -> Option { +impl Model> { + pub fn transpose(self) -> Option> { if self.is_null() { None } else { - Some(T::new(self.value)) + Some(self.transmute(|a| a)) } } } -impl Deref for OptionModel { - type Target = Value; - fn deref(&self) -> &Self::Target { - &self.value - } -} -impl DerefMut for OptionModel { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.value - } -} -impl Model for OptionModel { - type T = Option; - type Mut<'a> = OptionModelMut<'a, T::Mut<'a>> where T: 'a; - fn new(value: Value) -> Self { - Self { - value, - phantom: PhantomData, - } - } - fn into_inner(self) -> Value { - self.value - } -} -pub struct OptionModelMut<'a, T: ModelMut<'a>> { - value: &'a mut Value, - phantom: PhantomData, +pub trait Map: DeserializeOwned + Serialize { + type Key; + type Value; + type Error: From; } -impl<'a, T: ModelMut<'a>> OptionModelMut<'a, T> { - pub fn check(self) -> Option { - if self.is_null() { - None - } else { - Some(T::new(self.value)) + +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<'a, T: ModelMut<'a>> Deref for OptionModelMut<'a, T> { - type Target = Value; - fn deref(&self) -> &Self::Target { - &*self.value - } -} -impl<'a, T: ModelMut<'a>> DerefMut for OptionModelMut<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.value - } -} -impl<'a, T: ModelMut<'a>> ModelMut<'a> for OptionModelMut<'a, T> { - type T = Option; - fn new(value: &'a mut Value) -> Self { - Self { - value, - phantom: PhantomData, +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, } } - fn into_inner(self) -> &'a mut Value { - self.value +} +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()), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate as patch_db; + use imbl_value::json; + + #[derive(crate::HasModel)] + // #[macro_debug] + struct Foo { + a: Bar, + } + + #[derive(crate::HasModel)] + 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(); + } + + #[test] + fn test() { + let mut model = Model::::new(imbl_value::json!({ + "a": { + "b": "ReplaceMe" + } + })); + mutate_fn(&mut model); + mutate_fn(&mut model); + assert_eq!( + &*model, + &imbl_value::json!({ + "a": { + "b": "Replaced" + } + }) + ) } }