finish macros for structs

This commit is contained in:
Aiden McClelland
2021-03-29 12:21:23 -06:00
parent 9d35accb38
commit a93adcd21c
4 changed files with 137 additions and 13 deletions

View File

@@ -60,7 +60,27 @@ fn build_model_struct(
let ident = field.ident.clone().unwrap();
child_fn_name.push(ident.clone());
let ty = &field.ty;
child_model.push(syn::parse2(quote! { patch_db::Model<#ty> }).unwrap()); // TODO: check attr
if let Some(child_model_name) = field
.attrs
.iter()
.filter(|attr| attr.path.is_ident("model"))
.filter_map(|attr| Some(attr.parse_args::<MetaNameValue>().unwrap()))
.filter(|nv| nv.path.is_ident("name"))
.find_map(|nv| match nv.lit {
Lit::Str(s) => Some(s),
_ => None,
})
{
let child_model_ty =
Ident::new(&child_model_name.value(), child_model_name.span());
child_model
.push(syn::parse2(quote! { #child_model_ty }).expect("invalid model name"));
} else if field.attrs.iter().any(|attr| attr.path.is_ident("model")) {
child_model
.push(syn::parse2(quote! { <#ty as patch_db::HasModel>::Model }).unwrap());
} else {
child_model.push(syn::parse2(quote! { patch_db::Model<#ty> }).unwrap());
}
let serde_rename = field
.attrs
.iter()
@@ -107,9 +127,91 @@ fn build_model_struct(
}
Fields::Unnamed(f) => {
if f.unnamed.len() == 1 {
let field = &f.unnamed[0];
let ty = &field.ty;
let inner_model: Type = if let Some(child_model_name) = field
.attrs
.iter()
.filter(|attr| attr.path.is_ident("model"))
.filter_map(|attr| Some(attr.parse_args::<MetaNameValue>().unwrap()))
.filter(|nv| nv.path.is_ident("name"))
.find_map(|nv| match nv.lit {
Lit::Str(s) => Some(s),
_ => None,
}) {
let child_model_ty =
Ident::new(&child_model_name.value(), child_model_name.span());
syn::parse2(quote! { #child_model_ty }).unwrap()
} else if field.attrs.iter().any(|attr| attr.path.is_ident("model")) {
syn::parse2(quote! { <#ty as patch_db::HasModel>::Model }).unwrap()
} else {
syn::parse2(quote! { patch_db::Model<#ty> }).unwrap()
};
return quote! {
#[derive(Debug, Clone)]
#model_vis struct #model_name(#inner_model);
impl core::ops::Deref for #model_name {
type Target = #inner_model;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl #model_name {
pub fn new(ptr: json_ptr::JsonPointer) -> Self {
#model_name(#inner_model::new(ptr))
}
}
impl From<patch_db::Model<#base_name>> for #model_name {
fn from(model: patch_db::Model<#base_name>) -> Self {
#model_name(#inner_model::from(model))
}
}
impl From<#inner_model> for #model_name {
fn from(model: #inner_model) -> Self {
#model_name(model)
}
}
impl patch_db::HasModel for #base_name {
type Model = #model_name;
}
};
} else if f.unnamed.len() > 1 {
for (i, field) in f.unnamed.iter().enumerate() {
child_fn_name.push(Ident::new(
&format!("idx_{}", i),
proc_macro2::Span::call_site(),
));
let ty = &field.ty;
if let Some(child_model_name) = field
.attrs
.iter()
.filter(|attr| attr.path.is_ident("model"))
.filter_map(|attr| Some(attr.parse_args::<MetaNameValue>().unwrap()))
.filter(|nv| nv.path.is_ident("name"))
.find_map(|nv| match nv.lit {
Lit::Str(s) => Some(s),
_ => None,
})
{
let child_model_ty =
Ident::new(&child_model_name.value(), child_model_name.span());
child_model.push(
syn::parse2(quote! { #child_model_ty }).expect("invalid model name"),
);
} else if field.attrs.iter().any(|attr| attr.path.is_ident("model")) {
child_model.push(
syn::parse2(quote! { <#ty as patch_db::HasModel>::Model }).unwrap(),
);
} else {
child_model.push(syn::parse2(quote! { patch_db::Model<#ty> }).unwrap());
}
// TODO: serde rename for tuple structs?
child_path.push(LitStr::new(
&format!("{}", i),
proc_macro2::Span::call_site(),
));
}
}
todo!()
}
Fields::Unit => (),
}
@@ -126,13 +228,17 @@ fn build_model_struct(
pub fn new(ptr: json_ptr::JsonPointer) -> Self {
#model_name(patch_db::Model::new(ptr))
}
// foreach element, create accessor fn
#(
pub fn #child_fn_name(&self) -> #child_model {
self.0.child(#child_path).into()
}
)*
}
impl From<patch_db::Model<#base_name>> for #model_name {
fn from(model: patch_db::Model<#base_name>) -> Self {
#model_name(model)
}
}
impl patch_db::HasModel for #base_name {
type Model = #model_name;
}

View File

@@ -20,6 +20,7 @@ serde_cbor = { path = "../cbor" }
thiserror = "1.0.23"
tokio = { version = "1.0.1", features = ["sync", "fs", "rt", "io-util", "macros"] }
patch-db-macro = { path = "../patch-db-macro" }
lazy_static = "1.4.0"
[dev-dependencies]
proptest = "1.0.0"

View File

@@ -1,16 +1,17 @@
use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Error as IOError;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::sync::Arc;
use std::{collections::HashMap, path::PathBuf};
use fd_lock_rs::FdLock;
use futures::future::{BoxFuture, FutureExt};
use json_patch::{AddOperation, Patch, PatchOperation, RemoveOperation, ReplaceOperation};
use json_ptr::{JsonPointer, SegList};
use qutex_2::{QrwLock, ReadGuard, WriteGuard};
use lazy_static::lazy_static;
use qutex_2::{Guard, QrwLock, Qutex, ReadGuard, WriteGuard};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use thiserror::Error;
@@ -186,28 +187,43 @@ impl DiffPatch {
}
}
lazy_static! {
static ref OPEN_STORES: Mutex<HashMap<PathBuf, Qutex<()>>> = Mutex::new(HashMap::new());
}
pub struct Store {
file: FdLock<File>,
_lock: Guard<()>,
cache_corrupted: Option<Arc<IOError>>,
data: Value,
revision: u64,
}
impl Store {
pub async fn open<P: AsRef<Path> + Send + 'static>(path: P) -> Result<Self, Error> {
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let path = tokio::fs::canonicalize(path).await?;
let _lock = {
let mut lock = OPEN_STORES.lock().await;
if let Some(open) = lock.get(&path) {
open.clone().lock().await.unwrap()
} else {
let tex = Qutex::new(());
lock.insert(path.clone(), tex.clone());
tex.lock().await.unwrap()
}
};
Ok(tokio::task::spawn_blocking(move || {
use std::io::Write;
let p = path.as_ref();
let bak = p.with_extension("bak");
let bak = path.with_extension("bak");
if bak.exists() {
std::fs::rename(&bak, p)?;
std::fs::rename(&bak, &path)?;
}
let mut f = FdLock::lock(
OpenOptions::new()
.create(true)
.read(true)
.append(true)
.open(p)?,
.open(&path)?,
fd_lock_rs::LockType::Exclusive,
true,
)?;
@@ -238,6 +254,7 @@ impl Store {
Ok::<_, Error>(Store {
file: f.map(File::from_std),
_lock,
cache_corrupted: None,
data,
revision,
@@ -326,7 +343,7 @@ pub struct PatchDb {
locker: Locker,
}
impl PatchDb {
pub async fn open<P: AsRef<Path> + Send + 'static>(path: P) -> Result<Self, Error> {
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let (subscriber, _) = tokio::sync::broadcast::channel(16); // TODO: make this unbounded
Ok(PatchDb {

View File

@@ -80,11 +80,11 @@ proptest! {
#[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)]
pub struct Sample {
a: String,
#[model(name = ChildModel)]
#[model(name = "ChildModel")]
b: Child,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[derive(Debug, serde::Deserialize, serde::Serialize, HasModel)]
pub struct Child {
a: String,
b: usize,