mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 08:43:27 +00:00
Stronger linter on sync generator
This commit is contained in:
parent
6cdaed47ec
commit
aa9a117a22
|
@ -28,6 +28,7 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
pub p2p_discovery: Option<P2PDiscoveryState>,
|
||||
pub p2p_remote_access: Option<bool>,
|
||||
pub p2p_manual_peers: Option<HashSet<String>>,
|
||||
#[cfg(feature = "ai")]
|
||||
pub image_labeler_version: Option<String>,
|
||||
}
|
||||
R.mutation(|node, args: ChangeNodeNameArgs| async move {
|
||||
|
|
|
@ -10,17 +10,19 @@ pub enum AttributeFieldValue<'a> {
|
|||
|
||||
#[allow(unused)]
|
||||
impl AttributeFieldValue<'_> {
|
||||
pub fn as_single(&self) -> Option<&str> {
|
||||
match self {
|
||||
AttributeFieldValue::Single(field) => Some(field),
|
||||
_ => None,
|
||||
pub const fn as_single(&self) -> Option<&str> {
|
||||
if let AttributeFieldValue::Single(field) = self {
|
||||
Some(field)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_list(&self) -> Option<&Vec<&str>> {
|
||||
match self {
|
||||
AttributeFieldValue::List(fields) => Some(fields),
|
||||
_ => None,
|
||||
pub const fn as_list(&self) -> Option<&Vec<&str>> {
|
||||
if let AttributeFieldValue::List(fields) = self {
|
||||
Some(fields)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,12 +38,14 @@ impl<'a> Attribute<'a> {
|
|||
parser::parse(input).map(|(_, a)| a).map_err(|_| ())
|
||||
}
|
||||
|
||||
pub fn field(&self, name: &str) -> Option<&AttributeFieldValue> {
|
||||
self.fields.iter().find(|(n, _)| *n == name).map(|(_, v)| v)
|
||||
pub fn field(&self, name: &str) -> Option<&AttributeFieldValue<'_>> {
|
||||
self.fields
|
||||
.iter()
|
||||
.find_map(|(n, v)| (*n == name).then_some(v))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn model_attributes(model: ModelWalker) -> Vec<Attribute> {
|
||||
pub fn model_attributes(model: ModelWalker<'_>) -> Vec<Attribute<'_>> {
|
||||
model
|
||||
.ast_model()
|
||||
.documentation()
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::*,
|
||||
character::complete::*,
|
||||
combinator::*,
|
||||
bytes::complete::{is_not, tag},
|
||||
character::complete::{alpha1, char, multispace0},
|
||||
combinator::{map, opt},
|
||||
error::{ErrorKind, ParseError},
|
||||
multi::*,
|
||||
sequence::*,
|
||||
multi::separated_list1,
|
||||
sequence::{delimited, separated_pair},
|
||||
AsChar, IResult, InputTakeAtPosition,
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ fn parens(input: &str) -> IResult<&str, &str> {
|
|||
delimited(char('('), is_not(")"), char(')'))(input)
|
||||
}
|
||||
|
||||
fn single_value<T, E: ParseError<T>>(i: T) -> IResult<T, T, E>
|
||||
fn single_value<T, E: ParseError<T>>(i: &T) -> IResult<T, T, E>
|
||||
where
|
||||
T: InputTakeAtPosition,
|
||||
<T as InputTakeAtPosition>::Item: AsChar,
|
||||
|
@ -41,19 +41,19 @@ where
|
|||
fn list_value(input: &str) -> IResult<&str, Vec<&str>> {
|
||||
delimited(
|
||||
char('['),
|
||||
separated_list1(char(','), remove_ws(single_value)),
|
||||
separated_list1(char(','), remove_ws(|a| single_value(&a))),
|
||||
char(']'),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn attribute_field_value(input: &str) -> IResult<&str, AttributeFieldValue> {
|
||||
fn attribute_field_value(input: &str) -> IResult<&str, AttributeFieldValue<'_>> {
|
||||
remove_ws(alt((
|
||||
map(list_value, AttributeFieldValue::List),
|
||||
map(single_value, AttributeFieldValue::Single),
|
||||
map(|a| list_value(a), AttributeFieldValue::List),
|
||||
map(|a| single_value(&a), AttributeFieldValue::Single),
|
||||
)))(input)
|
||||
}
|
||||
|
||||
fn attribute_field(input: &str) -> IResult<&str, (&str, AttributeFieldValue)> {
|
||||
fn attribute_field(input: &str) -> IResult<&str, (&str, AttributeFieldValue<'_>)> {
|
||||
remove_ws(separated_pair(
|
||||
remove_ws(is_not(":")),
|
||||
char(':'),
|
||||
|
@ -61,11 +61,11 @@ fn attribute_field(input: &str) -> IResult<&str, (&str, AttributeFieldValue)> {
|
|||
))(input)
|
||||
}
|
||||
|
||||
fn attribute_fields(input: &str) -> IResult<&str, Vec<(&str, AttributeFieldValue)>> {
|
||||
fn attribute_fields(input: &str) -> IResult<&str, Vec<(&str, AttributeFieldValue<'_>)>> {
|
||||
separated_list1(char(','), attribute_field)(input)
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> IResult<&str, Attribute> {
|
||||
pub fn parse(input: &str) -> IResult<&str, Attribute<'_>> {
|
||||
let (input, _) = remove_ws(tag("@"))(input)?;
|
||||
let (input, name) = alpha1(input)?;
|
||||
let (input, values_str) = opt(remove_ws(parens))(input)?;
|
||||
|
@ -86,7 +86,7 @@ mod test {
|
|||
fn marker() {
|
||||
let s = "@local";
|
||||
|
||||
let (remaining, attribute) = super::parse(s).unwrap();
|
||||
let (remaining, attribute) = parse(s).unwrap();
|
||||
|
||||
assert_eq!(remaining, "");
|
||||
assert_eq!(attribute.name, "local");
|
||||
|
@ -97,7 +97,7 @@ mod test {
|
|||
fn single() {
|
||||
let s = "@local(foo: bar)";
|
||||
|
||||
let (remaining, attribute) = super::parse(s).unwrap();
|
||||
let (remaining, attribute) = parse(s).unwrap();
|
||||
|
||||
assert_eq!(remaining, "");
|
||||
assert_eq!(attribute.name, "local");
|
||||
|
@ -113,7 +113,7 @@ mod test {
|
|||
fn list() {
|
||||
let s = "@local(foo: [bar, baz])";
|
||||
|
||||
let (remaining, attribute) = match super::parse(s) {
|
||||
let (remaining, attribute) = match parse(s) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("{}", e),
|
||||
};
|
||||
|
@ -136,7 +136,7 @@ mod test {
|
|||
fn multiple() {
|
||||
let s = "@local(foo: bar, baz: qux)";
|
||||
|
||||
let (remaining, attribute) = super::parse(s).unwrap();
|
||||
let (remaining, attribute) = parse(s).unwrap();
|
||||
|
||||
assert_eq!(remaining, "");
|
||||
assert_eq!(attribute.name, "local");
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
mod attribute;
|
||||
mod model;
|
||||
mod sync_data;
|
||||
|
||||
use attribute::*;
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::pedantic,
|
||||
clippy::correctness,
|
||||
clippy::perf,
|
||||
clippy::style,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::nursery,
|
||||
clippy::unwrap_used,
|
||||
unused_qualifications,
|
||||
rust_2018_idioms,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_allocation,
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::dbg_macro,
|
||||
clippy::deprecated_cfg_attr,
|
||||
clippy::separated_literal_suffix,
|
||||
deprecated
|
||||
)]
|
||||
#![forbid(deprecated_in_future)]
|
||||
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
|
||||
|
||||
use prisma_client_rust_sdk::{
|
||||
prelude::*,
|
||||
|
@ -11,6 +34,12 @@ use prisma_client_rust_sdk::{
|
|||
},
|
||||
};
|
||||
|
||||
mod attribute;
|
||||
mod model;
|
||||
mod sync_data;
|
||||
|
||||
use attribute::{model_attributes, Attribute, AttributeFieldValue};
|
||||
|
||||
#[derive(Debug, serde::Serialize, thiserror::Error)]
|
||||
enum Error {}
|
||||
|
||||
|
@ -38,7 +67,7 @@ pub enum ModelSyncType<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ModelSyncType<'a> {
|
||||
fn from_attribute(attr: Attribute, model: ModelWalker<'a>) -> Option<Self> {
|
||||
fn from_attribute(attr: &Attribute<'_>, model: ModelWalker<'a>) -> Option<Self> {
|
||||
Some(match attr.name {
|
||||
"local" | "shared" => {
|
||||
let id = attr
|
||||
|
@ -69,14 +98,15 @@ impl<'a> ModelSyncType<'a> {
|
|||
AttributeFieldValue::List(_) => None,
|
||||
})
|
||||
.and_then(|name| {
|
||||
match model
|
||||
if let RefinedFieldWalker::Relation(r) = model
|
||||
.fields()
|
||||
.find(|f| f.name() == name)
|
||||
.unwrap_or_else(|| panic!("'{name}' field not found"))
|
||||
.refine()
|
||||
{
|
||||
RefinedFieldWalker::Relation(r) => Some(r),
|
||||
_ => None,
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| panic!("'{name}' must be a relation field"))
|
||||
|
@ -96,11 +126,10 @@ impl<'a> ModelSyncType<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn sync_id(&self) -> Vec<FieldWalker> {
|
||||
fn sync_id(&self) -> Vec<FieldWalker<'_>> {
|
||||
match self {
|
||||
// Self::Owned { id } => id.clone(),
|
||||
Self::Local { id, .. } => vec![*id],
|
||||
Self::Shared { id, .. } => vec![*id],
|
||||
Self::Local { id, .. } | Self::Shared { id, .. } => vec![*id],
|
||||
Self::Relation { group, item, .. } => vec![(*group).into(), (*item).into()],
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +156,7 @@ impl PrismaGenerator for SDSyncGenerator {
|
|||
|
||||
type Error = Error;
|
||||
|
||||
fn generate(self, args: GenerateArgs) -> Result<Module, Self::Error> {
|
||||
fn generate(self, args: GenerateArgs<'_>) -> Result<Module, Self::Error> {
|
||||
let db = &args.schema.db;
|
||||
|
||||
let models_with_sync_types = db
|
||||
|
@ -136,13 +165,13 @@ impl PrismaGenerator for SDSyncGenerator {
|
|||
.map(|(model, attributes)| {
|
||||
let sync_type = attributes
|
||||
.into_iter()
|
||||
.find_map(|a| ModelSyncType::from_attribute(a, model));
|
||||
.find_map(|a| ModelSyncType::from_attribute(&a, model));
|
||||
|
||||
(model, sync_type)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let model_sync_data = sync_data::r#enum(models_with_sync_types.clone());
|
||||
let model_sync_data = sync_data::enumerate(&models_with_sync_types);
|
||||
|
||||
let mut module = Module::new(
|
||||
"root",
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use prisma_client_rust_sdk::{prelude::*, prisma::prisma_models::walkers::RefinedFieldWalker};
|
||||
use prisma_models::{ast::ModelId, walkers::Walker};
|
||||
|
||||
use crate::{ModelSyncType, ModelWithSyncType};
|
||||
|
||||
pub fn module((model, sync_type): ModelWithSyncType) -> Module {
|
||||
pub fn module((model, sync_type): ModelWithSyncType<'_>) -> Module {
|
||||
let model_name_snake = snake_ident(model.name());
|
||||
|
||||
let sync_id = sync_type.as_ref().map(|sync_type| {
|
||||
let fields = sync_type.sync_id();
|
||||
let fields = fields.iter().flat_map(|field| {
|
||||
let fields = fields.iter().map(|field| {
|
||||
let name_snake = snake_ident(field.name());
|
||||
|
||||
let typ = match field.refine() {
|
||||
|
@ -18,10 +19,99 @@ pub fn module((model, sync_type): ModelWithSyncType) -> Module {
|
|||
}
|
||||
};
|
||||
|
||||
Some(quote!(pub #name_snake: #typ))
|
||||
quote!(pub #name_snake: #typ)
|
||||
});
|
||||
|
||||
let model_stuff = match sync_type {
|
||||
let model_stuff = parse_model(sync_type, &model_name_snake);
|
||||
|
||||
quote! {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||
pub struct SyncId {
|
||||
#(#fields),*
|
||||
}
|
||||
|
||||
impl sd_sync::SyncId for SyncId {
|
||||
type Model = #model_name_snake::Types;
|
||||
}
|
||||
|
||||
#model_stuff
|
||||
}
|
||||
});
|
||||
|
||||
let set_param_impl = {
|
||||
let field_matches = model.fields().filter_map(|field| {
|
||||
let field_name_snake = snake_ident(field.name());
|
||||
|
||||
match field.refine() {
|
||||
RefinedFieldWalker::Scalar(scalar_field) => {
|
||||
(!scalar_field.is_in_required_relation()).then(|| {
|
||||
quote! {
|
||||
#model_name_snake::#field_name_snake::set(::rmpv::ext::from_value(val).unwrap()),
|
||||
}
|
||||
})
|
||||
}
|
||||
RefinedFieldWalker::Relation(relation_field) => {
|
||||
let relation_model_name_snake =
|
||||
snake_ident(relation_field.related_model().name());
|
||||
|
||||
relation_field.referenced_fields().map_or_else(
|
||||
|| None,
|
||||
|i| {
|
||||
if i.count() == 1 {
|
||||
Some(quote! {{
|
||||
let val: std::collections::HashMap<String, rmpv::Value> = ::rmpv::ext::from_value(val).unwrap();
|
||||
let val = val.into_iter().next().unwrap();
|
||||
|
||||
#model_name_snake::#field_name_snake::connect(
|
||||
#relation_model_name_snake::UniqueWhereParam::deserialize(&val.0, val.1).unwrap()
|
||||
)
|
||||
}})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
.map(|body| quote!(#model_name_snake::#field_name_snake::NAME => #body))
|
||||
});
|
||||
|
||||
if field_matches.clone().count() == 0 {
|
||||
quote!()
|
||||
} else {
|
||||
quote! {
|
||||
impl #model_name_snake::SetParam {
|
||||
pub fn deserialize(field: &str, val: ::rmpv::Value) -> Option<Self> {
|
||||
Some(match field {
|
||||
#(#field_matches)*
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let unique_param_impl = process_unique_params(model, &model_name_snake);
|
||||
|
||||
Module::new(
|
||||
model.name(),
|
||||
quote! {
|
||||
use super::prisma::*;
|
||||
use prisma_client_rust::scalar_types::*;
|
||||
|
||||
#sync_id
|
||||
|
||||
#set_param_impl
|
||||
|
||||
#unique_param_impl
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_model(sync_type: &ModelSyncType<'_>, model_name_snake: &Ident) -> Option<TokenStream> {
|
||||
match sync_type {
|
||||
ModelSyncType::Relation {
|
||||
item,
|
||||
group,
|
||||
|
@ -68,80 +158,15 @@ pub fn module((model, sync_type): ModelWithSyncType) -> Module {
|
|||
type SyncId = SyncId;
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
ModelSyncType::Local { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||
pub struct SyncId {
|
||||
#(#fields),*
|
||||
}
|
||||
|
||||
impl sd_sync::SyncId for SyncId {
|
||||
type Model = #model_name_snake::Types;
|
||||
}
|
||||
|
||||
#model_stuff
|
||||
}
|
||||
});
|
||||
|
||||
let set_param_impl = {
|
||||
let field_matches = model.fields().filter_map(|field| {
|
||||
let field_name_snake = snake_ident(field.name());
|
||||
|
||||
match field.refine() {
|
||||
RefinedFieldWalker::Scalar(scalar_field) => {
|
||||
(!scalar_field.is_in_required_relation()).then(|| {
|
||||
quote! {
|
||||
#model_name_snake::#field_name_snake::set(::rmpv::ext::from_value(val).unwrap()),
|
||||
}
|
||||
})
|
||||
}
|
||||
RefinedFieldWalker::Relation(relation_field) => {
|
||||
let relation_model_name_snake =
|
||||
snake_ident(relation_field.related_model().name());
|
||||
|
||||
match relation_field.referenced_fields() {
|
||||
Some(i) => {
|
||||
if i.count() == 1 {
|
||||
Some(quote! {{
|
||||
let val: std::collections::HashMap<String, rmpv::Value> = ::rmpv::ext::from_value(val).unwrap();
|
||||
let val = val.into_iter().next().unwrap();
|
||||
|
||||
#model_name_snake::#field_name_snake::connect(
|
||||
#relation_model_name_snake::UniqueWhereParam::deserialize(&val.0, val.1).unwrap()
|
||||
)
|
||||
}})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
.map(|body| quote!(#model_name_snake::#field_name_snake::NAME => #body))
|
||||
});
|
||||
|
||||
match field_matches.clone().count() {
|
||||
0 => quote!(),
|
||||
_ => quote! {
|
||||
impl #model_name_snake::SetParam {
|
||||
pub fn deserialize(field: &str, val: ::rmpv::Value) -> Option<Self> {
|
||||
Some(match field {
|
||||
#(#field_matches)*
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let unique_param_impl = {
|
||||
#[inline]
|
||||
fn process_unique_params(model: Walker<'_, ModelId>, model_name_snake: &Ident) -> TokenStream {
|
||||
let field_matches = model
|
||||
.unique_criterias()
|
||||
.flat_map(|criteria| match &criteria.fields().next() {
|
||||
.filter_map(|criteria| match &criteria.fields().next() {
|
||||
Some(field) if criteria.fields().len() == 1 => {
|
||||
let field_name_snake = snake_ident(field.name());
|
||||
|
||||
|
@ -155,9 +180,10 @@ pub fn module((model, sync_type): ModelWithSyncType) -> Module {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match field_matches.len() {
|
||||
0 => quote!(),
|
||||
_ => quote! {
|
||||
if field_matches.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
quote! {
|
||||
impl #model_name_snake::UniqueWhereParam {
|
||||
pub fn deserialize(field: &str, val: ::rmpv::Value) -> Option<Self> {
|
||||
Some(match field {
|
||||
|
@ -166,21 +192,6 @@ pub fn module((model, sync_type): ModelWithSyncType) -> Module {
|
|||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Module::new(
|
||||
model.name(),
|
||||
quote! {
|
||||
use super::prisma::*;
|
||||
use prisma_client_rust::scalar_types::*;
|
||||
|
||||
#sync_id
|
||||
|
||||
#set_param_impl
|
||||
|
||||
#unique_param_impl
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ use prisma_client_rust_sdk::{
|
|||
prelude::*,
|
||||
prisma::prisma_models::walkers::{RefinedFieldWalker, RelationFieldWalker},
|
||||
};
|
||||
use prisma_models::walkers::{FieldWalker, ScalarFieldWalker};
|
||||
|
||||
use crate::{ModelSyncType, ModelWithSyncType};
|
||||
|
||||
pub fn r#enum(models: Vec<ModelWithSyncType>) -> TokenStream {
|
||||
pub fn enumerate(models: &[ModelWithSyncType<'_>]) -> TokenStream {
|
||||
let (variants, matches): (Vec<_>, Vec<_>) = models
|
||||
.iter()
|
||||
.filter_map(|(model, sync_type)| {
|
||||
|
@ -38,13 +39,173 @@ pub fn r#enum(models: Vec<ModelWithSyncType>) -> TokenStream {
|
|||
|
||||
let match_arms = match sync_type.as_ref()? {
|
||||
ModelSyncType::Shared { id, model_id } => {
|
||||
handle_crdt_ops_shared(id, *model_id, &model_name_snake)
|
||||
}
|
||||
ModelSyncType::Relation { item, group, .. } => {
|
||||
handle_crdt_ops_relation(models, item, group, &model_name_snake)
|
||||
}
|
||||
ModelSyncType::Local { .. } => return None,
|
||||
};
|
||||
|
||||
Some(quote! {
|
||||
Self::#model_name_pascal(id, data) => {
|
||||
#match_arms
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub enum ModelSyncData {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl ModelSyncData {
|
||||
pub fn from_op(op: sd_sync::CRDTOperation) -> Option<Self> {
|
||||
Some(match op.model {
|
||||
#(#matches),*,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn exec(self, db: &prisma::PrismaClient) -> prisma_client_rust::Result<()> {
|
||||
match self {
|
||||
#(#exec_matches),*
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_crdt_ops_relation(
|
||||
models: &[ModelWithSyncType<'_>],
|
||||
item: &RelationFieldWalker<'_>,
|
||||
group: &RelationFieldWalker<'_>,
|
||||
model_name_snake: &Ident,
|
||||
) -> TokenStream {
|
||||
let compound_id = format_ident!(
|
||||
"{}",
|
||||
group
|
||||
.fields()
|
||||
.expect("missing group fields")
|
||||
.chain(item.fields().expect("missing item fields"))
|
||||
.map(ScalarFieldWalker::name)
|
||||
.collect::<Vec<_>>()
|
||||
.join("_")
|
||||
);
|
||||
|
||||
let db_batch_items = {
|
||||
let batch_item = |item: &RelationFieldWalker<'_>| {
|
||||
let item_model_sync_id_field_name_snake = models
|
||||
.iter()
|
||||
.find(|m| m.0.name() == item.related_model().name())
|
||||
.and_then(|(_m, sync)| sync.as_ref())
|
||||
.map(|sync| snake_ident(sync.sync_id()[0].name()))
|
||||
.expect("missing sync id field name for relation");
|
||||
let item_model_name_snake = snake_ident(item.related_model().name());
|
||||
let item_field_name_snake = snake_ident(item.name());
|
||||
|
||||
quote! {
|
||||
db.#item_model_name_snake()
|
||||
.find_unique(
|
||||
prisma::#item_model_name_snake::#item_model_sync_id_field_name_snake::equals(
|
||||
id.#item_field_name_snake.#item_model_sync_id_field_name_snake.clone()
|
||||
)
|
||||
)
|
||||
.select(prisma::#item_model_name_snake::select!({ id }))
|
||||
}
|
||||
};
|
||||
|
||||
[batch_item(group), batch_item(item)]
|
||||
};
|
||||
|
||||
let create_items = {
|
||||
let create_item = |item: &RelationFieldWalker<'_>, var: TokenStream| {
|
||||
let item_model_name_snake = snake_ident(item.related_model().name());
|
||||
|
||||
quote!(
|
||||
prisma::#item_model_name_snake::id::equals(#var.id)
|
||||
)
|
||||
};
|
||||
|
||||
[
|
||||
create_item(item, quote!(item)),
|
||||
create_item(group, quote!(group)),
|
||||
]
|
||||
};
|
||||
|
||||
quote! {
|
||||
let (Some(group), Some(item)) =
|
||||
(#(#db_batch_items.exec().await?),*) else {
|
||||
panic!("item and group not found!");
|
||||
};
|
||||
|
||||
let id = prisma::#model_name_snake::#compound_id(group.id, item.id);
|
||||
|
||||
match data {
|
||||
sd_sync::CRDTOperationData::Create(_) => {
|
||||
db.#model_name_snake()
|
||||
.upsert(
|
||||
id,
|
||||
prisma::#model_name_snake::create(
|
||||
#(#create_items),*,
|
||||
vec![]
|
||||
),
|
||||
vec![],
|
||||
)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
sd_sync::CRDTOperationData::Update { field, value } => {
|
||||
let data = vec![prisma::#model_name_snake::SetParam::deserialize(&field, value).unwrap()];
|
||||
|
||||
db.#model_name_snake()
|
||||
.upsert(
|
||||
id,
|
||||
prisma::#model_name_snake::create(
|
||||
#(#create_items),*,
|
||||
data.clone(),
|
||||
),
|
||||
data,
|
||||
)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
sd_sync::CRDTOperationData::Delete => {
|
||||
db.#model_name_snake()
|
||||
.delete(id)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle_crdt_ops_shared(
|
||||
id: &FieldWalker<'_>,
|
||||
model_id: u16,
|
||||
model_name_snake: &Ident,
|
||||
) -> TokenStream {
|
||||
let (get_id, equals_value, id_name_snake, create_id) = match id.refine() {
|
||||
RefinedFieldWalker::Relation(rel) => {
|
||||
let scalar_field = rel.fields().unwrap().next().unwrap();
|
||||
let scalar_field = rel
|
||||
.fields()
|
||||
.expect("missing fields")
|
||||
.next()
|
||||
.expect("empty fields");
|
||||
let id_name_snake = snake_ident(scalar_field.name());
|
||||
let field_name_snake = snake_ident(rel.name());
|
||||
let opposite_model_name_snake =
|
||||
snake_ident(rel.opposite_relation_field().unwrap().model().name());
|
||||
let opposite_model_name_snake = snake_ident(
|
||||
rel.opposite_relation_field()
|
||||
.expect("missing opposite relation field")
|
||||
.model()
|
||||
.name(),
|
||||
);
|
||||
|
||||
let relation_equals_condition = quote!(prisma::#opposite_model_name_snake::pub_id::equals(
|
||||
id.#field_name_snake.pub_id.clone()
|
||||
|
@ -122,138 +283,4 @@ pub fn r#enum(models: Vec<ModelWithSyncType>) -> TokenStream {
|
|||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
ModelSyncType::Relation { item, group, .. } => {
|
||||
let compound_id = format_ident!(
|
||||
"{}",
|
||||
group
|
||||
.fields()
|
||||
.unwrap()
|
||||
.chain(item.fields().unwrap())
|
||||
.map(|f| f.name())
|
||||
.collect::<Vec<_>>()
|
||||
.join("_")
|
||||
);
|
||||
|
||||
let db_batch_items = {
|
||||
let batch_item = |item: &RelationFieldWalker| {
|
||||
let item_model_sync_id_field_name_snake = models
|
||||
.iter()
|
||||
.find(|m| m.0.name() == item.related_model().name())
|
||||
.and_then(|(_m, sync)| sync.as_ref())
|
||||
.map(|sync| snake_ident(sync.sync_id()[0].name()))
|
||||
.unwrap();
|
||||
let item_model_name_snake = snake_ident(item.related_model().name());
|
||||
let item_field_name_snake = snake_ident(item.name());
|
||||
|
||||
quote! {
|
||||
db.#item_model_name_snake()
|
||||
.find_unique(
|
||||
prisma::#item_model_name_snake::#item_model_sync_id_field_name_snake::equals(
|
||||
id.#item_field_name_snake.#item_model_sync_id_field_name_snake.clone()
|
||||
)
|
||||
)
|
||||
.select(prisma::#item_model_name_snake::select!({ id }))
|
||||
}
|
||||
};
|
||||
|
||||
[batch_item(group), batch_item(item)]
|
||||
};
|
||||
|
||||
let create_items = {
|
||||
let create_item = |item: &RelationFieldWalker, var: TokenStream| {
|
||||
let item_model_name_snake = snake_ident(item.related_model().name());
|
||||
|
||||
quote!(
|
||||
prisma::#item_model_name_snake::id::equals(#var.id)
|
||||
)
|
||||
};
|
||||
|
||||
[
|
||||
create_item(item, quote!(item)),
|
||||
create_item(group, quote!(group)),
|
||||
]
|
||||
};
|
||||
|
||||
quote! {
|
||||
let (Some(group), Some(item)) =
|
||||
(#(#db_batch_items.exec().await?),*) else {
|
||||
panic!("item and group not found!");
|
||||
};
|
||||
|
||||
let id = prisma::#model_name_snake::#compound_id(group.id, item.id);
|
||||
|
||||
match data {
|
||||
sd_sync::CRDTOperationData::Create(_) => {
|
||||
db.#model_name_snake()
|
||||
.upsert(
|
||||
id,
|
||||
prisma::#model_name_snake::create(
|
||||
#(#create_items),*,
|
||||
vec![]
|
||||
),
|
||||
vec![],
|
||||
)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
sd_sync::CRDTOperationData::Update { field, value } => {
|
||||
let data = vec![prisma::#model_name_snake::SetParam::deserialize(&field, value).unwrap()];
|
||||
|
||||
db.#model_name_snake()
|
||||
.upsert(
|
||||
id,
|
||||
prisma::#model_name_snake::create(
|
||||
#(#create_items),*,
|
||||
data.clone(),
|
||||
),
|
||||
data,
|
||||
)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
sd_sync::CRDTOperationData::Delete => {
|
||||
db.#model_name_snake()
|
||||
.delete(id)
|
||||
.exec()
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(quote! {
|
||||
Self::#model_name_pascal(id, data) => {
|
||||
#match_arms
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub enum ModelSyncData {
|
||||
#(#variants),*
|
||||
}
|
||||
|
||||
impl ModelSyncData {
|
||||
pub fn from_op(op: sd_sync::CRDTOperation) -> Option<Self> {
|
||||
Some(match op.model {
|
||||
#(#matches),*,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn exec(self, db: &prisma::PrismaClient) -> prisma_client_rust::Result<()> {
|
||||
match self {
|
||||
#(#exec_matches),*
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ export type CameraData = { device_make: string | null; device_model: string | nu
|
|||
|
||||
export type CasId = string
|
||||
|
||||
export type ChangeNodeNameArgs = { name: string | null; p2p_port: Port | null; p2p_disabled: boolean | null; p2p_ipv6_disabled: boolean | null; p2p_relay_disabled: boolean | null; p2p_discovery: P2PDiscoveryState | null; p2p_remote_access: boolean | null; p2p_manual_peers: string[] | null; image_labeler_version: string | null }
|
||||
export type ChangeNodeNameArgs = { name: string | null; p2p_port: Port | null; p2p_disabled: boolean | null; p2p_ipv6_disabled: boolean | null; p2p_relay_disabled: boolean | null; p2p_discovery: P2PDiscoveryState | null; p2p_remote_access: boolean | null; p2p_manual_peers: string[] | null }
|
||||
|
||||
export type Chapter = { id: number; start: [number, number]; end: [number, number]; time_base_den: number; time_base_num: number; metadata: Metadata }
|
||||
|
||||
|
|
Loading…
Reference in a new issue