mirror of
https://github.com/Start9Labs/patch-db.git
synced 2026-03-30 20:04:47 +00:00
fix models again
This commit is contained in:
@@ -8,30 +8,14 @@ use syn::punctuated::Punctuated;
|
|||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::{Comma, Paren, Pub};
|
use syn::token::{Comma, Paren, Pub};
|
||||||
use syn::{
|
use syn::{
|
||||||
Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, GenericArgument, Ident, Lit,
|
Attribute, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Ident, Lit, LitInt, LitStr,
|
||||||
LitInt, LitStr, MetaNameValue, Path, PathArguments, Type, VisPublic, VisRestricted, Visibility,
|
MetaNameValue, Path, Type, VisRestricted, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn build_model(item: &DeriveInput) -> TokenStream {
|
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::<MetaNameValue>().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 {
|
let res = match &item.data {
|
||||||
Data::Struct(struct_ast) => build_model_struct(item, struct_ast, model_name),
|
Data::Struct(struct_ast) => build_model_struct(item, struct_ast),
|
||||||
Data::Enum(enum_ast) => build_model_enum(item, enum_ast, model_name),
|
Data::Enum(enum_ast) => build_model_enum(item, enum_ast),
|
||||||
_ => panic!("Models can only be created for Structs and Enums"),
|
_ => 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")) {
|
if let Some(dbg) = item.attrs.iter().find(|a| a.path.is_ident("macro_debug")) {
|
||||||
@@ -83,7 +67,6 @@ struct ChildInfo {
|
|||||||
name: Ident,
|
name: Ident,
|
||||||
accessor: Option<Lit>,
|
accessor: Option<Lit>,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
has_model: bool,
|
|
||||||
}
|
}
|
||||||
impl ChildInfo {
|
impl ChildInfo {
|
||||||
fn from_fields(serde_rename_all: &Option<String>, fields: &Fields) -> Vec<Self> {
|
fn from_fields(serde_rename_all: &Option<String>, fields: &Fields) -> Vec<Self> {
|
||||||
@@ -93,7 +76,6 @@ impl ChildInfo {
|
|||||||
for field in &f.named {
|
for field in &f.named {
|
||||||
let ident = field.ident.clone().unwrap();
|
let ident = field.ident.clone().unwrap();
|
||||||
let ty = field.ty.clone();
|
let ty = field.ty.clone();
|
||||||
let has_model = field.attrs.iter().any(|attr| attr.path.is_ident("model"));
|
|
||||||
let accessor = if field
|
let accessor = if field
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -114,7 +96,6 @@ impl ChildInfo {
|
|||||||
name: ident,
|
name: ident,
|
||||||
accessor,
|
accessor,
|
||||||
ty,
|
ty,
|
||||||
has_model,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +103,6 @@ impl ChildInfo {
|
|||||||
for (i, field) in f.unnamed.iter().enumerate() {
|
for (i, field) in f.unnamed.iter().enumerate() {
|
||||||
let ident = Ident::new(&format!("idx_{i}"), field.span());
|
let ident = Ident::new(&format!("idx_{i}"), field.span());
|
||||||
let ty = field.ty.clone();
|
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 {
|
let accessor = if f.unnamed.len() > 1 {
|
||||||
Some(Lit::Int(LitInt::new(
|
Some(Lit::Int(LitInt::new(
|
||||||
&format!("{}", i),
|
&format!("{}", i),
|
||||||
@@ -136,7 +116,6 @@ impl ChildInfo {
|
|||||||
name: ident,
|
name: ident,
|
||||||
accessor,
|
accessor,
|
||||||
ty,
|
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 {
|
struct Fns {
|
||||||
impl_fns: TokenStream,
|
impl_fns: TokenStream,
|
||||||
impl_mut_fns: TokenStream,
|
impl_mut_fns: TokenStream,
|
||||||
@@ -180,9 +138,10 @@ fn impl_fns(children: &[ChildInfo]) -> Fns {
|
|||||||
name,
|
name,
|
||||||
accessor,
|
accessor,
|
||||||
ty,
|
ty,
|
||||||
has_model,
|
|
||||||
} in children
|
} 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 {
|
let vis = match vis {
|
||||||
Visibility::Inherited => Visibility::Restricted(VisRestricted {
|
Visibility::Inherited => Visibility::Restricted(VisRestricted {
|
||||||
pub_token: Pub::default(),
|
pub_token: Pub::default(),
|
||||||
@@ -211,54 +170,38 @@ fn impl_fns(children: &[ChildInfo]) -> Fns {
|
|||||||
}
|
}
|
||||||
a => a.clone(),
|
a => a.clone(),
|
||||||
};
|
};
|
||||||
let (optional, ty) = separate_option(ty);
|
let accessor_owned = if let Some(accessor) = accessor {
|
||||||
let (model_ty, model_mut_ty) = if *has_model {
|
quote! {
|
||||||
(
|
{
|
||||||
quote_spanned! { name.span() =>
|
#[allow(unused_imports)]
|
||||||
<#ty as patch_db::HasModel>::Model
|
use patch_db::value::index::Index;
|
||||||
},
|
#accessor.index_into_owned(v).unwrap_or_default()
|
||||||
quote_spanned! { name.span() =>
|
}
|
||||||
<<#ty as patch_db::HasModel>::Model as patch_db::Model>::Mut<'a>
|
}
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(
|
quote! { v }
|
||||||
quote_spanned! { name.span() =>
|
|
||||||
patch_db::GenericModel::<#ty>
|
|
||||||
},
|
|
||||||
quote_spanned! { name.span() =>
|
|
||||||
patch_db::GenericModelMut::<'a, #ty>
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let accessor = if let Some(accessor) = accessor {
|
let accessor_mut = if let Some(accessor) = accessor {
|
||||||
quote! { [#accessor] }
|
quote! {
|
||||||
|
{
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use patch_db::value::index::Index;
|
||||||
|
#accessor.index_or_insert(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {}
|
quote! { v }
|
||||||
};
|
};
|
||||||
if optional {
|
impl_fns.extend(quote_spanned! { name.span() =>
|
||||||
impl_fns.extend(quote_spanned! { name.span() =>
|
#vis fn #name_owned (self) -> patch_db::Model::<#ty> {
|
||||||
#vis fn #name (&self) -> patch_db::OptionModel<#model_ty> {
|
self.transmute(|v| #accessor_owned)
|
||||||
<patch_db::OptionModel::<#model_ty> as patch_db::Model>::new((**self) #accessor .clone())
|
}
|
||||||
}
|
});
|
||||||
});
|
impl_mut_fns.extend(quote_spanned! { name.span() =>
|
||||||
impl_mut_fns.extend(quote_spanned! { name.span() =>
|
#vis fn #name_mut (&mut self) -> &mut patch_db::Model::<#ty> {
|
||||||
#vis fn #name (&'a mut self) -> patch_db::OptionModelMut<'a, #model_mut_ty> {
|
self.transmute_mut(|v| #accessor_mut)
|
||||||
<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)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Fns {
|
Fns {
|
||||||
impl_fns,
|
impl_fns,
|
||||||
@@ -266,17 +209,7 @@ fn impl_fns(children: &[ChildInfo]) -> Fns {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_model_struct(
|
fn build_model_struct(base: &DeriveInput, ast: &DataStruct) -> TokenStream {
|
||||||
base: &DeriveInput,
|
|
||||||
ast: &DataStruct,
|
|
||||||
module_name: Option<Ident>,
|
|
||||||
) -> 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 serde_rename_all = base
|
let serde_rename_all = base
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -289,82 +222,46 @@ fn build_model_struct(
|
|||||||
});
|
});
|
||||||
let children = ChildInfo::from_fields(&serde_rename_all, &ast.fields);
|
let children = ChildInfo::from_fields(&serde_rename_all, &ast.fields);
|
||||||
let name = &base.ident;
|
let name = &base.ident;
|
||||||
let vis = &base.vis;
|
|
||||||
let Fns {
|
let Fns {
|
||||||
impl_fns,
|
impl_fns,
|
||||||
impl_mut_fns,
|
impl_mut_fns,
|
||||||
} = impl_fns(&children);
|
} = impl_fns(&children);
|
||||||
quote! {
|
quote! {
|
||||||
impl patch_db::HasModel for #name {
|
impl patch_db::HasModel for #name {}
|
||||||
type Model = #module_name::Model;
|
impl patch_db::Model<#name> {
|
||||||
}
|
#impl_fns
|
||||||
#vis mod #module_name {
|
#impl_mut_fns
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option<Ident>) -> TokenStream {
|
fn build_model_enum(base: &DeriveInput, ast: &DataEnum) -> TokenStream {
|
||||||
let module_name = module_name.unwrap_or_else(|| {
|
let mut match_name = None;
|
||||||
Ident::new(
|
let mut match_name_mut = None;
|
||||||
&format!("{}_model", heck::AsSnakeCase(base.ident.to_string())),
|
for arg in base
|
||||||
proc_macro2::Span::call_site(),
|
.attrs
|
||||||
)
|
.iter()
|
||||||
});
|
.filter(|attr| attr.path.is_ident("model"))
|
||||||
let mut children = Vec::new();
|
.map(|attr| attr.parse_args::<MetaNameValue>().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
|
let serde_rename_all = base
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -408,55 +305,40 @@ fn build_model_enum(base: &DeriveInput, ast: &DataEnum, module_name: Option<Iden
|
|||||||
.collect();
|
.collect();
|
||||||
let mut model_variants = TokenStream::new();
|
let mut model_variants = TokenStream::new();
|
||||||
let mut mut_model_variants = TokenStream::new();
|
let mut mut_model_variants = TokenStream::new();
|
||||||
let mut decl_model_variants = TokenStream::new();
|
|
||||||
let mut into_inner = TokenStream::new();
|
|
||||||
let mut deref = TokenStream::new();
|
|
||||||
let mut deref_mut = TokenStream::new();
|
|
||||||
let mut mut_into_inner = TokenStream::new();
|
|
||||||
let mut mut_deref = TokenStream::new();
|
|
||||||
let mut mut_deref_mut = TokenStream::new();
|
|
||||||
let (impl_new, impl_new_mut) = if let Some(Lit::Str(tag)) = serde_tag.remove("tag") {
|
let (impl_new, impl_new_mut) = if let Some(Lit::Str(tag)) = serde_tag.remove("tag") {
|
||||||
children.push(ChildInfo {
|
|
||||||
vis: Visibility::Public(VisPublic {
|
|
||||||
pub_token: Pub::default(),
|
|
||||||
}),
|
|
||||||
name: Ident::new("tag", tag.span()),
|
|
||||||
accessor: Some(Lit::Str(tag.clone())),
|
|
||||||
ty: Type::Path(syn::TypePath {
|
|
||||||
qself: None,
|
|
||||||
path: Path::from(Ident::new("String", tag.span())),
|
|
||||||
}),
|
|
||||||
has_model: false,
|
|
||||||
});
|
|
||||||
if let Some(Lit::Str(content)) = serde_tag.remove("content") {
|
if let Some(Lit::Str(content)) = serde_tag.remove("content") {
|
||||||
let mut tag_variants = TokenStream::new();
|
let mut tag_variants = TokenStream::new();
|
||||||
let mut tag_variants_mut = TokenStream::new();
|
let mut tag_variants_mut = TokenStream::new();
|
||||||
for variant in &ast.variants {
|
for variant in &ast.variants {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
let variant_model =
|
|
||||||
Ident::new(&format!("{}Model", variant_name), variant_name.span());
|
|
||||||
let variant_model_mut =
|
|
||||||
Ident::new(&format!("{}ModelMut", variant_name), variant_name.span());
|
|
||||||
let variant_accessor =
|
let variant_accessor =
|
||||||
get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
||||||
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
||||||
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() =>
|
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! {
|
quote! {
|
||||||
match value[#tag].as_str() {
|
match self[#tag].as_str() {
|
||||||
#tag_variants
|
#tag_variants
|
||||||
_ => Model::Error(value),
|
_ => #match_name::Error(self.into_inner()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
match value[#tag].as_str() {
|
match self[#tag].as_str() {
|
||||||
#tag_variants_mut
|
#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<Iden
|
|||||||
let mut tag_variants_mut = TokenStream::new();
|
let mut tag_variants_mut = TokenStream::new();
|
||||||
for variant in &ast.variants {
|
for variant in &ast.variants {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
let variant_model =
|
|
||||||
Ident::new(&format!("{}Model", variant_name), variant_name.span());
|
|
||||||
let variant_model_mut =
|
|
||||||
Ident::new(&format!("{}ModelMut", variant_name), variant_name.span());
|
|
||||||
let variant_accessor =
|
let variant_accessor =
|
||||||
get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
||||||
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
||||||
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() =>
|
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! {
|
quote! {
|
||||||
match value[#tag].as_str() {
|
match self[#tag].as_str() {
|
||||||
#tag_variants
|
#tag_variants
|
||||||
_ => Model::Error(value),
|
_ => #match_name::Error(self.into_inner()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
match value[#tag].as_str() {
|
match self[#tag].as_str() {
|
||||||
#tag_variants_mut
|
#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<Iden
|
|||||||
let mut tag_variants_mut = TokenStream::new();
|
let mut tag_variants_mut = TokenStream::new();
|
||||||
for variant in &ast.variants {
|
for variant in &ast.variants {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
let variant_model = Ident::new(&format!("{}Model", variant_name), variant_name.span());
|
|
||||||
let variant_model_mut =
|
|
||||||
Ident::new(&format!("{}ModelMut", variant_name), variant_name.span());
|
|
||||||
let variant_accessor = get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
let variant_accessor = get_accessor(&serde_rename_all, &variant.attrs, variant_name);
|
||||||
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
tag_variants.extend(quote_spanned! { variant_name.span() =>
|
||||||
if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) {
|
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
|
} else
|
||||||
});
|
});
|
||||||
tag_variants_mut.extend(quote_spanned! { variant_name.span() =>
|
tag_variants_mut.extend(quote_spanned! { variant_name.span() =>
|
||||||
if value.as_object().map(|o| o.contains_key(#variant_accessor)).unwrap_or(false) {
|
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
|
} else
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
quote! {
|
quote! {
|
||||||
#tag_variants {
|
#tag_variants {
|
||||||
Model::Error(value),
|
#match_name::Error(self.into_inner()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
#tag_variants_mut {
|
#tag_variants_mut {
|
||||||
ModelMut::Error(value),
|
#match_name_mut::Error(&mut *self),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
for variant in &ast.variants {
|
for variant in &ast.variants {
|
||||||
let name = &variant.ident;
|
let name = &variant.ident;
|
||||||
let model_name = Ident::new(&format!("{}Model", variant.ident), variant.ident.span());
|
let ty = match &variant.fields {
|
||||||
let model_name_mut =
|
Fields::Unnamed(a) if a.unnamed.len() == 1 => &a.unnamed.first().unwrap().ty,
|
||||||
Ident::new(&format!("{}ModelMut", variant.ident), variant.ident.span());
|
a => {
|
||||||
let serde_rename_all = variant
|
return Error::new(
|
||||||
.attrs
|
a.span(),
|
||||||
.iter()
|
"Can only derive HasModel for enums with newtype variants",
|
||||||
.filter(|attr| attr.path.is_ident("serde"))
|
)
|
||||||
.filter_map(|attr| attr.parse_args::<MetaNameValue>().ok())
|
.into_compile_error()
|
||||||
.filter(|nv| nv.path.is_ident("rename_all"))
|
}
|
||||||
.find_map(|nv| match nv.lit {
|
};
|
||||||
Lit::Str(s) => Some(s.value()),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
model_variants.extend(quote_spanned! { name.span() =>
|
model_variants.extend(quote_spanned! { name.span() =>
|
||||||
#name(#model_name),
|
#name(patch_db::Model<#ty>),
|
||||||
});
|
});
|
||||||
mut_model_variants.extend(quote_spanned! { name.span() =>
|
mut_model_variants.extend(quote_spanned! { name.span() =>
|
||||||
#name(#model_name_mut<'a>),
|
#name(&'a mut patch_db::Model<#ty>),
|
||||||
});
|
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let name = &base.ident;
|
let name = &base.ident;
|
||||||
let vis = &base.vis;
|
let vis = &base.vis;
|
||||||
let Fns {
|
|
||||||
impl_fns,
|
|
||||||
impl_mut_fns,
|
|
||||||
} = impl_fns(&children);
|
|
||||||
quote! {
|
quote! {
|
||||||
impl patch_db::HasModel for #name {
|
impl patch_db::HasModel for #name {}
|
||||||
type Model = #module_name::Model;
|
|
||||||
|
impl patch_db::Model<#name> {
|
||||||
|
#vis fn matchable(&self) -> #match_name {
|
||||||
|
#impl_new
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#vis mod #module_name {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#vis enum #match_name {
|
||||||
pub enum Model {
|
#model_variants
|
||||||
#model_variants
|
Error(patch_db::Value),
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl patch_db::Model<#name> {
|
||||||
pub enum ModelMut<'a> {
|
#vis fn matchable<'a>(&'a self) -> #match_name_mut<'a> {
|
||||||
#mut_model_variants
|
#impl_new_mut
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#decl_model_variants
|
#vis enum #match_name_mut<'a> {
|
||||||
|
#mut_model_variants
|
||||||
|
Error(&'a mut patch_db::Value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ mod test;
|
|||||||
|
|
||||||
pub use imbl_value as value;
|
pub use imbl_value as value;
|
||||||
pub use imbl_value::Value;
|
pub use imbl_value::Value;
|
||||||
pub use model::{
|
pub use model::{HasModel, Model, ModelExt};
|
||||||
GenericModel, GenericModelMut, HasModel, Model, ModelMut, OptionModel, OptionModelMut,
|
|
||||||
};
|
|
||||||
pub use patch::{DiffPatch, Dump, Revision};
|
pub use patch::{DiffPatch, Dump, Revision};
|
||||||
pub use patch_db_macro::HasModel;
|
pub use patch_db_macro::HasModel;
|
||||||
pub use store::{PatchDb, Store};
|
pub use store::{PatchDb, Store};
|
||||||
|
|||||||
@@ -1,24 +1,71 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use imbl_value::Value;
|
use imbl::OrdSet;
|
||||||
|
use imbl_value::{InternedString, Value};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
pub trait HasModel {
|
pub trait HasModel {}
|
||||||
type Model: Model;
|
|
||||||
|
/// &mut Model<T> <=> &mut Value
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Model<T> {
|
||||||
|
value: Value,
|
||||||
|
phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
impl<T> Clone for Model<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
value: self.value.clone(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Deref for Model<T> {
|
||||||
|
type Target = Value;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> DerefMut for Model<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Model<T> {
|
||||||
|
pub fn new(value: Value) -> Self {
|
||||||
|
Self {
|
||||||
|
value,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn into_inner(self) -> Value {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
pub fn transmute<U>(self, f: impl FnOnce(Value) -> Value) -> Model<U> {
|
||||||
|
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<U> {
|
||||||
|
unsafe {
|
||||||
|
std::mem::transmute::<&'a mut Value, &'a mut Model<U>>(f(std::mem::transmute::<
|
||||||
|
&'a mut Model<T>,
|
||||||
|
&'a mut Value,
|
||||||
|
>(self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Model: Deref<Target = Value> + DerefMut {
|
pub trait ModelExt: Deref<Target = Value> + DerefMut {
|
||||||
type T: DeserializeOwned + Serialize;
|
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> {
|
fn set(&mut self, value: &Self::T) -> Result<(), imbl_value::Error> {
|
||||||
*self.deref_mut() = imbl_value::to_value(value)?;
|
*self.deref_mut() = imbl_value::to_value(value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -28,148 +75,132 @@ pub trait Model: Deref<Target = Value> + DerefMut {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ModelMut<'a>: Deref<Target = Value> + DerefMut {
|
impl<T: DeserializeOwned + Serialize> ModelExt for Model<T> {
|
||||||
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<Self::T, imbl_value::Error> {
|
|
||||||
imbl_value::from_value(self.deref().clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GenericModel<T> {
|
|
||||||
value: Value,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
impl<T> Deref for GenericModel<T> {
|
|
||||||
type Target = Value;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DerefMut for GenericModel<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: DeserializeOwned + Serialize> Model for GenericModel<T> {
|
|
||||||
type T = T;
|
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> {
|
impl<T> Model<Option<T>> {
|
||||||
value: &'a mut Value,
|
pub fn transpose(self) -> Option<Model<T>> {
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
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<T: Model> {
|
|
||||||
value: Value,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Model> OptionModel<T> {
|
|
||||||
pub fn check(self) -> Option<T> {
|
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(T::new(self.value))
|
Some(self.transmute(|a| a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Model> Deref for OptionModel<T> {
|
|
||||||
type Target = Value;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Model> DerefMut for OptionModel<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Model> Model for OptionModel<T> {
|
|
||||||
type T = Option<T::T>;
|
|
||||||
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>> {
|
pub trait Map: DeserializeOwned + Serialize {
|
||||||
value: &'a mut Value,
|
type Key;
|
||||||
phantom: PhantomData<T>,
|
type Value;
|
||||||
|
type Error: From<imbl_value::Error>;
|
||||||
}
|
}
|
||||||
impl<'a, T: ModelMut<'a>> OptionModelMut<'a, T> {
|
|
||||||
pub fn check(self) -> Option<T> {
|
impl<T: Map> Model<T>
|
||||||
if self.is_null() {
|
where
|
||||||
None
|
T::Key: DeserializeOwned + Ord + Clone,
|
||||||
} else {
|
{
|
||||||
Some(T::new(self.value))
|
pub fn keys(&self) -> Result<OrdSet<T::Key>, 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> {
|
impl<T: Map> Model<T>
|
||||||
type Target = Value;
|
where
|
||||||
fn deref(&self) -> &Self::Target {
|
T::Key: AsRef<str>,
|
||||||
&*self.value
|
{
|
||||||
}
|
pub fn idx(self, key: &T::Key) -> Option<Model<T::Value>> {
|
||||||
}
|
match &*self {
|
||||||
impl<'a, T: ModelMut<'a>> DerefMut for OptionModelMut<'a, T> {
|
Value::Object(o) if o.contains_key(key.as_ref()) => Some(self.transmute(|v| {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
use imbl_value::index::Index;
|
||||||
self.value
|
key.as_ref().index_into_owned(v).unwrap()
|
||||||
}
|
})),
|
||||||
}
|
_ => None,
|
||||||
impl<'a, T: ModelMut<'a>> ModelMut<'a> for OptionModelMut<'a, T> {
|
|
||||||
type T = Option<T::T>;
|
|
||||||
fn new(value: &'a mut Value) -> Self {
|
|
||||||
Self {
|
|
||||||
value,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn into_inner(self) -> &'a mut Value {
|
}
|
||||||
self.value
|
impl<T: Map> Model<T>
|
||||||
|
where
|
||||||
|
T::Key: AsRef<str>,
|
||||||
|
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<Foo>) {
|
||||||
|
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::<Foo>::new(imbl_value::json!({
|
||||||
|
"a": {
|
||||||
|
"b": "ReplaceMe"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
mutate_fn(&mut model);
|
||||||
|
mutate_fn(&mut model);
|
||||||
|
assert_eq!(
|
||||||
|
&*model,
|
||||||
|
&imbl_value::json!({
|
||||||
|
"a": {
|
||||||
|
"b": "Replaced"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user