From d1b35423a89ed54ccd972954f8d6893df9b0f55b Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 3 Jan 2023 16:15:41 +0800 Subject: [PATCH] add extended traits --- Cargo.toml | 2 +- cli/Cargo.toml | 2 +- cli/src/generator/client.rs | 8 +- cli/src/generator/models/include_select.rs | 2 +- cli/src/generator/models/mod.rs | 22 ++-- integration-tests/Cargo.toml | 2 +- integration-tests/tests/count.rs | 76 +++--------- integration-tests/tests/lib.rs | 2 +- integration-tests/tests/mock.rs | 4 +- prisma-cli/Cargo.toml | 2 +- src/actions.rs | 98 +-------------- src/client.rs | 29 +++-- src/lib.rs | 2 + src/mock.rs | 8 +- src/queries/batch.rs | 31 ++--- src/queries/count.rs | 88 +++++++------- src/queries/create.rs | 73 +++++------ src/queries/create_many.rs | 67 +++------- src/queries/delete.rs | 65 ++++------ src/queries/delete_many.rs | 71 +++++------ src/queries/error.rs | 39 ++++++ src/queries/execute_raw.rs | 28 ++--- src/queries/find_first.rs | 84 +++++++------ src/queries/find_many.rs | 94 +++++++------- src/queries/find_unique.rs | 60 ++++----- src/queries/include.rs | 20 +-- src/queries/mod.rs | 80 +++++------- src/queries/query.rs | 135 +++++++++++++++++++++ src/queries/query_raw.rs | 48 ++++---- src/queries/select.rs | 8 +- src/queries/update.rs | 71 +++++------ src/queries/update_many.rs | 67 ++++------ src/queries/upsert.rs | 63 ++++------ 33 files changed, 671 insertions(+), 780 deletions(-) create mode 100644 src/queries/error.rs create mode 100644 src/queries/query.rs diff --git a/Cargo.toml b/Cargo.toml index 7aeb1c9c..30ed79fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rspc = ["dep:rspc"] sqlite-create-many = ["psl/sqlite-create-many"] migrations = ["migration-core", "include_dir", "tempdir", "tokio/fs", "tracing"] mocking = ["tokio"] -mutation-callbacks = [] +# mutation-callbacks = [] mysql = ["query-core/mysql", "migration-core/mysql"] sqlite = ["query-core/sqlite", "migration-core/sqlite"] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b29438a9..552e1c24 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,7 +10,7 @@ default = ["mysql", "sqlite", "mssql", "postgresql", "mongodb"] rspc = [] migrations = [] sqlite-create-many = ["prisma-client-rust-sdk/sqlite-create-many"] -mutation-callbacks = [] +# mutation-callbacks = [] mocking = [] mysql = ["prisma-client-rust-sdk/mysql"] diff --git a/cli/src/generator/client.rs b/cli/src/generator/client.rs index 68dce121..bb94387b 100644 --- a/cli/src/generator/client.rs +++ b/cli/src/generator/client.rs @@ -55,8 +55,8 @@ pub fn generate(args: &GenerateArgs) -> TokenStream { let mock_ctor = cfg!(feature = "mocking").then(|| { quote! { - pub async fn _mock() -> (Self, #pcr::MockStore) { - let (internals, store) = #pcr::PrismaClientInternals::new_mock(#pcr::ActionNotifier::new()).await; + pub fn _mock() -> (Self, #pcr::MockStore) { + let (internals, store) = #pcr::PrismaClientInternals::new_mock(#pcr::ActionNotifier::new()); (Self(internals), store) } @@ -111,7 +111,7 @@ pub fn generate(args: &GenerateArgs) -> TokenStream { #mock_ctor - pub fn _query_raw(&self, query: #pcr::Raw) -> #pcr::QueryRaw { + pub fn _query_raw(&self, query: #pcr::Raw) -> #pcr::QueryRaw { #pcr::QueryRaw::new( &self.0, query, @@ -127,7 +127,7 @@ pub fn generate(args: &GenerateArgs) -> TokenStream { ) } - pub async fn _batch, Marker>(&self, queries: T) -> #pcr::Result { + pub async fn _batch<'a, T: #pcr::BatchContainer<'a, Marker>, Marker>(&self, queries: T) -> #pcr::Result<>::ReturnType> { #pcr::batch(queries, &self.0).await } diff --git a/cli/src/generator/models/include_select.rs b/cli/src/generator/models/include_select.rs index 91234b14..4ea51595 100644 --- a/cli/src/generator/models/include_select.rs +++ b/cli/src/generator/models/include_select.rs @@ -421,7 +421,7 @@ fn model_macro<'a>( let selection_struct = quote! { pub struct Selection(Vec<::prisma_client_rust::Selection>); - impl ::prisma_client_rust::#variant_ident::#selection_type for Selection { + impl ::prisma_client_rust::#selection_type for Selection { type Data = Data; type ModelData = $crate::#module_path::#model_name_snake::Data; diff --git a/cli/src/generator/models/mod.rs b/cli/src/generator/models/mod.rs index 00675911..21ca1751 100644 --- a/cli/src/generator/models/mod.rs +++ b/cli/src/generator/models/mod.rs @@ -291,17 +291,17 @@ pub fn generate(args: &GenerateArgs, module_path: TokenStream) -> Vec>; pub type ManyArgs = ::prisma_client_rust::ManyArgs>; - pub type Count<'a> = ::prisma_client_rust::Count<'a, Actions<'static>>; - pub type Create<'a> = ::prisma_client_rust::Create<'a, Actions<'static>>; - pub type CreateMany<'a> = ::prisma_client_rust::CreateMany<'a, Actions<'static>>; - pub type FindUnique<'a> = ::prisma_client_rust::FindUnique<'a, Actions<'static>>; - pub type FindMany<'a> = ::prisma_client_rust::FindMany<'a, Actions<'static>>; - pub type FindFirst<'a> = ::prisma_client_rust::FindFirst<'a, Actions<'static>>; - pub type Update<'a> = ::prisma_client_rust::Update<'a, Actions<'static>>; - pub type UpdateMany<'a> = ::prisma_client_rust::UpdateMany<'a, Actions<'static>>; - pub type Upsert<'a> = ::prisma_client_rust::Upsert<'a, Actions<'static>>; - pub type Delete<'a> = ::prisma_client_rust::Delete<'a, Actions<'static>>; - pub type DeleteMany<'a> = ::prisma_client_rust::DeleteMany<'a, Actions<'static>>; + pub type Count<'a> = ::prisma_client_rust::Count<'a, Actions<'a>>; + pub type Create<'a> = ::prisma_client_rust::Create<'a, Actions<'a>>; + pub type CreateMany<'a> = ::prisma_client_rust::CreateMany<'a, Actions<'a>>; + pub type FindUnique<'a> = ::prisma_client_rust::FindUnique<'a, Actions<'a>>; + pub type FindMany<'a> = ::prisma_client_rust::FindMany<'a, Actions<'a>>; + pub type FindFirst<'a> = ::prisma_client_rust::FindFirst<'a, Actions<'a>>; + pub type Update<'a> = ::prisma_client_rust::Update<'a, Actions<'a>>; + pub type UpdateMany<'a> = ::prisma_client_rust::UpdateMany<'a, Actions<'a>>; + pub type Upsert<'a> = ::prisma_client_rust::Upsert<'a, Actions<'a>>; + pub type Delete<'a> = ::prisma_client_rust::Delete<'a, Actions<'a>>; + pub type DeleteMany<'a> = ::prisma_client_rust::DeleteMany<'a, Actions<'a>>; #actions_struct } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 002e424b..5c474b2b 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -18,6 +18,6 @@ prisma-client-rust = { path = "..", features = [ "sqlite-create-many", "sqlite", "migrations", - "mutation-callbacks", + # "mutation-callbacks", "mocking", ], default-features = false } diff --git a/integration-tests/tests/count.rs b/integration-tests/tests/count.rs index 58a6f217..fc3f35a4 100644 --- a/integration-tests/tests/count.rs +++ b/integration-tests/tests/count.rs @@ -1,19 +1,29 @@ use crate::db::*; use crate::utils::*; -#[tokio::test] -async fn basic() -> TestResult { - let client = client().await; - +async fn create_posts(client: &PrismaClient) -> TestResult { client .post() - .create("Hi from Prisma!".to_string(), true, vec![]) + .create_many(vec![ + post::create("Hi from Prisma!".to_string(), true, vec![]), + post::create("Hi from Prisma!".to_string(), true, vec![]), + post::create("Hi from Prisma!".to_string(), false, vec![]), + ]) .exec() .await?; + Ok(()) +} + +#[tokio::test] +async fn basic() -> TestResult { + let client = client().await; + + create_posts(&client).await?; + let count = client.post().count(vec![]).exec().await?; - assert_eq!(count, 1); + assert_eq!(count, 3); cleanup(client).await } @@ -37,23 +47,7 @@ async fn no_results() -> TestResult { async fn where_() -> TestResult { let client = client().await; - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), false, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; + create_posts(&client).await?; let published_count = client .post() @@ -76,23 +70,7 @@ async fn where_() -> TestResult { async fn take() -> TestResult { let client = client().await; - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; + create_posts(&client).await?; let count = client.post().count(vec![]).take(1).exec().await?; @@ -105,23 +83,7 @@ async fn take() -> TestResult { async fn skip() -> TestResult { let client = client().await; - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; - - client - .post() - .create("Hi from Prisma!".to_string(), true, vec![]) - .exec() - .await?; + create_posts(&client).await?; let count = client.post().count(vec![]).skip(1).exec().await?; diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs index 9d07d5c1..9c0890ea 100644 --- a/integration-tests/tests/lib.rs +++ b/integration-tests/tests/lib.rs @@ -19,7 +19,7 @@ async fn aaaa_run_migrations() -> TestResult { } mod batch; -mod callbacks; +// mod callbacks; mod count; mod create; mod create_many; diff --git a/integration-tests/tests/mock.rs b/integration-tests/tests/mock.rs index a839c679..5c7e14ee 100644 --- a/integration-tests/tests/mock.rs +++ b/integration-tests/tests/mock.rs @@ -3,7 +3,7 @@ use crate::utils::*; #[tokio::test] async fn returns() -> TestResult { - let (client, mock) = PrismaClient::_mock().await; + let (client, mock) = PrismaClient::_mock(); user::select!(basic_user { id name }); @@ -31,7 +31,7 @@ async fn returns() -> TestResult { #[tokio::test] async fn returns_many() -> TestResult { - let (client, mock) = PrismaClient::_mock().await; + let (client, mock) = PrismaClient::_mock(); user::select!(basic_user { id name }); diff --git a/prisma-cli/Cargo.toml b/prisma-cli/Cargo.toml index c770470c..8a350192 100644 --- a/prisma-cli/Cargo.toml +++ b/prisma-cli/Cargo.toml @@ -11,6 +11,6 @@ prisma-client-rust-cli = { features = [ "sqlite-create-many", "sqlite", "migrations", - "mutation-callbacks", + # "mutation-callbacks", "mocking" ], default_features = false, path = "../cli" } diff --git a/src/actions.rs b/src/actions.rs index 006446ac..d9776399 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,106 +1,12 @@ -use prisma_models::PrismaValue; -use query_core::{Selection, SelectionArgument}; -use serde::de::DeserializeOwned; - -use crate::SerializedWhereInput; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ModelQueryType { - FindUnique, - FindFirst, - FindMany, - Count, -} - -impl ModelQueryType { - pub fn name(&self) -> &'static str { - match self { - Self::FindUnique => "findUnique", - Self::FindFirst => "findFirst", - Self::FindMany => "findMany", - Self::Count => "aggregate", - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ModelMutationType { - Create, - CreateMany, - Update, - UpdateMany, - Delete, - DeleteMany, - Upsert, -} - -impl ModelMutationType { - pub fn name(&self) -> &'static str { - match self { - Self::Create => "createOne", - Self::CreateMany => "createMany", - Self::Update => "updateOne", - Self::UpdateMany => "updateMany", - Self::Delete => "deleteOne", - Self::DeleteMany => "deleteMany", - Self::Upsert => "upsertOne", - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ModelActionType { - Query(ModelQueryType), - Mutation(ModelMutationType), -} - -impl ModelActionType { - pub fn name(&self) -> &'static str { - match self { - Self::Query(q) => q.name(), - Self::Mutation(q) => q.name(), - } - } -} - -pub trait ModelActions { - type Data: DeserializeOwned; - type Where: WhereInput; - type Set: Into<(String, PrismaValue)>; - type With: Into; - type OrderBy: Into<(String, PrismaValue)>; - type Cursor: Into; - - const MODEL: &'static str; - - fn scalar_selections() -> Vec; -} +use crate::{ModelWriteOperation, SerializedWhereInput}; pub trait WhereInput { fn serialize(self) -> SerializedWhereInput; } -pub trait ModelAction { - type Actions: ModelActions; - - const TYPE: ModelActionType; - - fn base_selection( - arguments: impl IntoIterator, - nested_selections: impl IntoIterator, - ) -> Selection { - Selection::new( - format!("{}{}", Self::TYPE.name(), Self::Actions::MODEL), - Some("result".to_string()), - arguments.into_iter().collect::>(), - nested_selections.into_iter().collect::>(), - ) - } -} - #[derive(Debug, PartialEq, Eq)] pub struct ModelMutationCallbackData { - pub action: ModelMutationType, + pub action: ModelWriteOperation, pub model: &'static str, } diff --git a/src/client.rs b/src/client.rs index 46f172ec..77b928f7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,15 +1,13 @@ -use std::sync::Arc; - +use crate::queries::ModelActions; +use crate::{ActionNotifier, ModelQuery}; use diagnostics::Diagnostics; use query_core::{schema_builder, CoreError, Operation}; use schema::QuerySchema; use serde::de::{DeserializeOwned, IntoDeserializer}; +use std::sync::Arc; use thiserror::Error; -use crate::{ - prisma_value, ActionNotifier, MockStore, ModelAction, ModelActionType, ModelActions, - ModelMutationCallbackData, QueryError, Result, -}; +use crate::{prisma_value, ModelMutationCallbackData, ModelOperation, QueryError, Result}; pub type Executor = Box; @@ -19,7 +17,8 @@ pub(crate) enum ExecutionEngine { query_schema: Arc, url: String, }, - Mock(MockStore), + #[cfg(feature = "mocking")] + Mock(crate::MockStore), } impl ExecutionEngine { @@ -39,6 +38,7 @@ impl ExecutionEngine { Ok(serde_value::to_value(data)?) } + #[cfg(feature = "mocking")] Self::Mock(store) => Ok(store.get_op(&op).await.expect("Mock data not found")), } } @@ -70,6 +70,7 @@ impl ExecutionEngine { }) .collect()) } + #[cfg(feature = "mocking")] Self::Mock(store) => { let mut ret = vec![]; @@ -100,12 +101,12 @@ impl PrismaClientInternals { Ok(ret) } - pub fn notify_model_mutation(&self) + pub fn notify_model_mutation<'a, Action>(&self) where - Action: ModelAction, + Action: ModelQuery<'a>, { match Action::TYPE { - ModelActionType::Mutation(action) => { + ModelOperation::Write(action) => { for callback in &self.action_notifier.model_mutation_callbacks { (callback)(ModelMutationCallbackData { model: Action::Actions::MODEL, @@ -113,7 +114,7 @@ impl PrismaClientInternals { }) } } - ModelActionType::Query(_) => { + ModelOperation::Read(_) => { println!("notify_model_mutation only acceps mutations, not queries!") } } @@ -174,8 +175,9 @@ impl PrismaClientInternals { }) } - pub async fn new_mock(action_notifier: ActionNotifier) -> (Self, MockStore) { - let mock_store = MockStore::new(); + #[cfg(feature = "mocking")] + pub fn new_mock(action_notifier: ActionNotifier) -> (Self, crate::MockStore) { + let mock_store = crate::MockStore::new(); ( Self { @@ -188,6 +190,7 @@ impl PrismaClientInternals { pub fn url(&self) -> &str { match &self.engine { + #[cfg(feature = "mocking")] ExecutionEngine::Mock(_) => "mock", ExecutionEngine::Real { url, .. } => url, } diff --git a/src/lib.rs b/src/lib.rs index 706e7f6f..d9ce62fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod actions; mod client; #[cfg(feature = "migrations")] pub mod migrations; +#[cfg(feature = "mocking")] mod mock; pub mod operator; mod prisma_value; @@ -30,6 +31,7 @@ pub use user_facing_errors as prisma_errors; pub use actions::*; pub use client::*; +#[cfg(feature = "mocking")] pub use mock::*; pub use operator::Operator; pub use queries::*; diff --git a/src/mock.rs b/src/mock.rs index 18c37ba3..db7a32a3 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -6,7 +6,7 @@ use serde::Serialize; use serde_value::Value; use tokio::sync::Mutex; -use crate::BatchQuery; +use crate::Query; #[derive(Default, Clone)] pub struct MockStore { @@ -29,11 +29,11 @@ impl MockStore { mutex.lock().await.push((sel, expected)) } - pub async fn expect(&self, query: Q, expected: Q::RawType) + pub async fn expect<'a, Q: Query<'a>>(&self, query: Q, expected: Q::RawType) where - ::RawType: Serialize, + >::RawType: Serialize, { - self.add_op(query.graphql(), serde_value::to_value(expected).unwrap()) + self.add_op(query.graphql().0, serde_value::to_value(expected).unwrap()) .await; } diff --git a/src/queries/batch.rs b/src/queries/batch.rs index 20443c15..60a2d974 100644 --- a/src/queries/batch.rs +++ b/src/queries/batch.rs @@ -6,12 +6,12 @@ use serde::{ Deserialize, }; -use crate::PrismaClientInternals; +use crate::{PrismaClientInternals, Query}; -pub async fn batch, Marker>( +pub async fn batch<'a, T: BatchContainer<'a, Marker>, Marker>( container: T, client: &PrismaClientInternals, -) -> super::Result { +) -> super::Result<>::ReturnType> { let data = client .engine .execute_all(container.graphql()) @@ -23,20 +23,8 @@ pub async fn batch, Marker>( Ok(T::convert(data)) } -/// A query that can be used within a transaction -pub trait BatchQuery { - type RawType: DeserializeOwned; - type ReturnType; - - fn graphql(self) -> Operation; - - /// Function for converting between raw database data and the type expected by the user. - /// Necessary for things like raw queries - fn convert(raw: Self::RawType) -> Self::ReturnType; -} - /// A container that can hold queries to batch into a transaction -pub trait BatchContainer { +pub trait BatchContainer<'a, Marker> { type RawType: DeserializeOwned; type ReturnType; @@ -44,12 +32,15 @@ pub trait BatchContainer { fn convert(raw: VecDeque) -> Self::ReturnType; } -impl> BatchContainer<()> for I { +impl<'a, T: Query<'a>, I: IntoIterator> BatchContainer<'a, ()> for I { type RawType = T::RawType; type ReturnType = Vec; fn graphql(self) -> Vec { - self.into_iter().map(BatchQuery::graphql).collect() + self.into_iter() + .map(Query::graphql) + .map(|(o, _)| o) + .collect() } fn convert(raw: VecDeque) -> Self::ReturnType { @@ -62,14 +53,14 @@ pub enum TupleMarker {} macro_rules! impl_tuple { ($($generic:ident),+) => { #[allow(warnings)] - impl<$($generic: BatchQuery),+> BatchContainer for ($($generic),+) { + impl<'a, $($generic: Query<'a>),+> BatchContainer<'a, TupleMarker> for ($($generic),+) { type RawType = serde_json::Value; type ReturnType = ($($generic::ReturnType),+); fn graphql(self) -> Vec<$crate::query_core::Operation> { let ($($generic),+) = self; - vec![$($generic.graphql()),+] + vec![$($generic.graphql().0),+] } fn convert(mut raw: VecDeque) -> Self::ReturnType { diff --git a/src/queries/count.rs b/src/queries/count.rs index 0a4abce6..facbf498 100644 --- a/src/queries/count.rs +++ b/src/queries/count.rs @@ -3,14 +3,11 @@ use query_core::{Operation, Selection}; use serde::Deserialize; use crate::{ - merge_fields, BatchQuery, ModelAction, ModelActionType, ModelActions, ModelQueryType, - PrismaClientInternals, SerializedWhereInput, WhereInput, + merge_fields, ModelActions, ModelOperation, ModelQuery, ModelReadOperation, OrderByQuery, + PaginatedQuery, PrismaClientInternals, Query, SerializedWhereInput, WhereInput, WhereQuery, }; -pub struct Count<'a, Actions> -where - Actions: ModelActions, -{ +pub struct Count<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_params: Vec, pub order_by_params: Vec, @@ -19,19 +16,7 @@ where pub take: Option, } -impl<'a, Actions> ModelAction for Count<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Query(ModelQueryType::Count); -} - -impl<'a, Actions> Count<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> Count<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, where_params: Vec) -> Self { Self { client, @@ -63,7 +48,26 @@ where self } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +#[derive(Deserialize)] +pub struct CountAggregateResult { + _count: CountResult, +} + +#[derive(Deserialize)] +pub struct CountResult { + _all: i64, +} + +impl<'a, Actions: ModelActions> Query<'a> for Count<'a, Actions> { + type RawType = CountAggregateResult; + type ReturnType = i64; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { ( Operation::Read(Self::base_selection( [ @@ -125,39 +129,39 @@ where ) } - pub(crate) fn convert(data: CountAggregateResult) -> i64 { - data._count._all + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw._count._all } +} - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); +impl<'a, Actions: ModelActions> ModelQuery<'a> for Count<'a, Actions> { + type Actions = Actions; - client.execute(op).await.map(Self::convert) - } + const TYPE: ModelOperation = ModelOperation::Read(ModelReadOperation::Count); } -#[derive(Deserialize)] -pub struct CountAggregateResult { - _count: CountResult, +impl<'a, Actions: ModelActions> WhereQuery<'a> for Count<'a, Actions> { + fn add_where(&mut self, param: Actions::Where) { + self.where_params.push(param); + } } -#[derive(Deserialize)] -pub struct CountResult { - _all: i64, +impl<'a, Actions: ModelActions> OrderByQuery<'a> for Count<'a, Actions> { + fn add_order_by(&mut self, param: Actions::OrderBy) { + self.order_by_params.push(param); + } } -impl<'a, Actions> BatchQuery for Count<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = CountAggregateResult; - type ReturnType = i64; +impl<'a, Actions: ModelActions> PaginatedQuery<'a> for Count<'a, Actions> { + fn add_cursor(&mut self, param: Actions::Cursor) { + self.cursor_params.push(param); + } - fn graphql(self) -> Operation { - self.exec_operation().0 + fn set_skip(&mut self, skip: i64) { + self.skip = Some(skip); } - fn convert(raw: Self::RawType) -> Self::ReturnType { - Self::convert(raw) + fn set_take(&mut self, take: i64) { + self.take = Some(take); } } diff --git a/src/queries/create.rs b/src/queries/create.rs index d6454e82..39422a64 100644 --- a/src/queries/create.rs +++ b/src/queries/create.rs @@ -2,35 +2,17 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use crate::{ - include::{Include, IncludeType}, - merge_fields, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelMutationType, - PrismaClientInternals, + merge_fields, Include, IncludeType, ModelActions, ModelOperation, ModelQuery, + ModelWriteOperation, PrismaClientInternals, Query, Select, SelectType, SetQuery, WithQuery, }; -pub struct Create<'a, Actions> -where - Actions: ModelActions, -{ +pub struct Create<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub set_params: Vec, pub with_params: Vec, } -impl<'a, Actions> ModelAction for Create<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::Create); -} - -impl<'a, Actions> Create<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> Create<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, set_params: Vec) -> Self { Self { client, @@ -81,7 +63,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for Create<'a, Actions> { + type RawType = Actions::Data; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -92,30 +83,28 @@ where ) } - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); - - Ok(res) + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw } } -impl<'a, Actions> BatchQuery for Create<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Actions::Data; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> ModelQuery<'a> for Create<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::Create); +} + +impl<'a, Actions: ModelActions> SetQuery<'a> for Create<'a, Actions> { + fn add_set(&mut self, param: Actions::Set) { + self.set_params.push(param); } +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> WithQuery<'a> for Create<'a, Actions> { + fn add_with( + &mut self, + param: impl Into<<>::Actions as ModelActions>::With>, + ) { + self.with_params.push(param.into()); } } diff --git a/src/queries/create_many.rs b/src/queries/create_many.rs index d218c6c9..10783a16 100644 --- a/src/queries/create_many.rs +++ b/src/queries/create_many.rs @@ -1,33 +1,18 @@ use prisma_models::PrismaValue; -use query_core::{Operation, QueryValue, Selection}; +use query_core::{Operation, Selection}; use crate::{ - merge_fields, BatchQuery, BatchResult, ModelAction, ModelActionType, ModelActions, - ModelMutationType, PrismaClientInternals, + merge_fields, BatchResult, ModelActions, ModelOperation, ModelQuery, ModelWriteOperation, + PrismaClientInternals, Query, }; -pub struct CreateMany<'a, Actions> -where - Actions: ModelActions, -{ +pub struct CreateMany<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub set_params: Vec>, pub skip_duplicates: bool, } -impl<'a, Actions> ModelAction for CreateMany<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::CreateMany); -} - -impl<'a, Actions> CreateMany<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> CreateMany<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, set_params: Vec>) -> Self { Self { client, @@ -66,14 +51,23 @@ where #[cfg(not(any(feature = "mongodb", feature = "mssql")))] ( "skipDuplicates".to_string(), - QueryValue::Boolean(_skip_duplicates), + PrismaValue::Boolean(_skip_duplicates).into(), ), ], nested_selections, ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for CreateMany<'a, Actions> { + type RawType = BatchResult; + type ReturnType = i64; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { ( Operation::Write(Self::to_selection( self.set_params, @@ -84,34 +78,13 @@ where ) } - pub(crate) fn convert(raw: BatchResult) -> i64 { + fn convert(raw: Self::RawType) -> Self::ReturnType { raw.count } - - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await.map(Self::convert)?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); - - Ok(res) - } } -impl<'a, Actions> BatchQuery for CreateMany<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = BatchResult; - type ReturnType = i64; - - fn graphql(self) -> Operation { - self.exec_operation().0 - } +impl<'a, Actions: ModelActions> ModelQuery<'a> for CreateMany<'a, Actions> { + type Actions = Actions; - fn convert(raw: Self::RawType) -> Self::ReturnType { - Self::convert(raw) - } + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::CreateMany); } diff --git a/src/queries/delete.rs b/src/queries/delete.rs index 5d18ed0a..6bb3301b 100644 --- a/src/queries/delete.rs +++ b/src/queries/delete.rs @@ -2,34 +2,17 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use crate::{ - include::{Include, IncludeType}, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelMutationType, - PrismaClientInternals, WhereInput, + Include, IncludeType, ModelActions, ModelOperation, ModelQuery, ModelWriteOperation, + PrismaClientInternals, Query, Select, SelectType, WhereInput, WithQuery, }; -pub struct Delete<'a, Actions> -where - Actions: ModelActions, -{ +pub struct Delete<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_param: Actions::Where, pub with_params: Vec, } -impl<'a, Actions> ModelAction for Delete<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::Delete); -} - -impl<'a, Actions> Delete<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> Delete<'a, Actions> { pub fn new( client: &'a PrismaClientInternals, where_param: Actions::Where, @@ -80,7 +63,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for Delete<'a, Actions> { + type RawType = Actions::Data; + type ReturnType = Actions::Data; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -91,30 +83,19 @@ where ) } - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); - - Ok(res) + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw } } -impl<'a, Actions> BatchQuery for Delete<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Actions::Data; - type ReturnType = Actions::Data; +impl<'a, Actions: ModelActions> ModelQuery<'a> for Delete<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 - } + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::Delete); +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> WithQuery<'a> for Delete<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); } } diff --git a/src/queries/delete_many.rs b/src/queries/delete_many.rs index 127c3326..4a45939b 100644 --- a/src/queries/delete_many.rs +++ b/src/queries/delete_many.rs @@ -1,32 +1,17 @@ use query_core::Operation; use crate::{ - merge_fields, BatchQuery, BatchResult, ModelAction, ModelActionType, ModelActions, - ModelMutationType, PrismaClientInternals, WhereInput, + merge_fields, BatchResult, ModelActions, ModelOperation, ModelQuery, ModelWriteOperation, + PrismaClientInternals, Query, WhereInput, WhereQuery, }; use prisma_models::PrismaValue; -pub struct DeleteMany<'a, Actions> -where - Actions: ModelActions, -{ +pub struct DeleteMany<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_params: Vec, } -impl<'a, Actions> ModelAction for DeleteMany<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::DeleteMany); -} - -impl<'a, Actions> DeleteMany<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> DeleteMany<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, where_params: Vec) -> Self { Self { client, @@ -34,7 +19,20 @@ where } } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub(crate) fn convert(raw: BatchResult) -> i64 { + raw.count + } + + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for DeleteMany<'a, Actions> { + type RawType = BatchResult; + type ReturnType = i64; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { ( Operation::Write(Self::base_selection( [(!self.where_params.is_empty()).then(|| { @@ -58,34 +56,19 @@ where ) } - pub(crate) fn convert(raw: BatchResult) -> i64 { - raw.count - } - - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await.map(Self::convert)?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); - - Ok(res) + fn convert(raw: Self::RawType) -> Self::ReturnType { + Self::convert(raw) } } -impl<'a, Actions> BatchQuery for DeleteMany<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = BatchResult; - type ReturnType = i64; +impl<'a, Actions: ModelActions> ModelQuery<'a> for DeleteMany<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 - } + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::DeleteMany); +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - Self::convert(raw) +impl<'a, Actions: ModelActions> WhereQuery<'a> for DeleteMany<'a, Actions> { + fn add_where(&mut self, param: Actions::Where) { + self.where_params.push(param); } } diff --git a/src/queries/error.rs b/src/queries/error.rs new file mode 100644 index 00000000..d28c7b86 --- /dev/null +++ b/src/queries/error.rs @@ -0,0 +1,39 @@ +use thiserror::Error; +use user_facing_errors::UserFacingError; + +#[derive(Debug, Error)] +pub enum QueryError { + #[error("Error executing query: {} - {}", .0.as_known().map(|k| k.error_code.to_string()).unwrap_or("Unknown".to_string()), .0.message())] + Execute(user_facing_errors::Error), + + #[error("Error serializing query result: {0}")] + Serialize(#[from] serde_value::SerializerError), + + #[error("Error deserializing query result into return type: {0}")] + Deserialize(#[from] serde_value::DeserializerError), +} + +impl QueryError { + pub fn is_prisma_error(&self) -> bool { + match self { + Self::Execute(error) => error + .as_known() + .map(|e| e.error_code == ::ERROR_CODE) + .unwrap_or(false), + _ => false, + } + } +} + +pub type Result = std::result::Result; + +#[cfg(feature = "rspc")] +impl From for rspc::Error { + fn from(err: QueryError) -> Self { + rspc::Error::with_cause( + rspc::ErrorCode::InternalServerError, + "Internal server error occurred while completing database operation!".into(), + err, + ) + } +} diff --git a/src/queries/execute_raw.rs b/src/queries/execute_raw.rs index 1e3bb495..baa210e9 100644 --- a/src/queries/execute_raw.rs +++ b/src/queries/execute_raw.rs @@ -2,7 +2,7 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use serde_json::Value; -use crate::{raw::Raw, BatchQuery, PrismaClientInternals}; +use crate::{raw::Raw, PrismaClientInternals, Query}; pub struct ExecuteRaw<'a> { client: &'a PrismaClientInternals, @@ -21,7 +21,16 @@ impl<'a> ExecuteRaw<'a> { } } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a> Query<'a> for ExecuteRaw<'a> { + type RawType = i64; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { ( Operation::Write(Selection::new( "executeRaw".to_string(), @@ -39,21 +48,6 @@ impl<'a> ExecuteRaw<'a> { ) } - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - client.execute(op).await - } -} - -impl<'a> BatchQuery for ExecuteRaw<'a> { - type RawType = i64; - type ReturnType = Self::RawType; - - fn graphql(self) -> Operation { - self.exec_operation().0 - } - fn convert(raw: Self::RawType) -> Self::ReturnType { raw } diff --git a/src/queries/find_first.rs b/src/queries/find_first.rs index d1effc8d..b45b775f 100644 --- a/src/queries/find_first.rs +++ b/src/queries/find_first.rs @@ -2,19 +2,14 @@ use prisma_models::PrismaValue; use query_core::{Operation, QueryValue, Selection}; use crate::{ - include::{Include, IncludeType}, - merge_fields, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelQueryType, PrismaClientInternals, - WhereInput, + merge_fields, Include, IncludeType, ModelActions, ModelOperation, ModelQuery, + ModelReadOperation, OrderByQuery, PaginatedQuery, PrismaClientInternals, Query, Select, + SelectType, WhereInput, WhereQuery, WithQuery, }; use super::SerializedWhereInput; -pub struct FindFirst<'a, Actions> -where - Actions: ModelActions, -{ +pub struct FindFirst<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_params: Vec, pub with_params: Vec, @@ -24,19 +19,7 @@ where pub take: Option, } -impl<'a, Actions> ModelAction for FindFirst<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Query(ModelQueryType::FindFirst); -} - -impl<'a, Actions> FindFirst<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> FindFirst<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, where_params: Vec) -> Self { Self { client, @@ -167,7 +150,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result> { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for FindFirst<'a, Actions> { + type RawType = Option; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -185,25 +177,45 @@ where ) } - pub async fn exec(self) -> super::Result> { - let (op, client) = self.exec_operation(); + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw + } +} + +impl<'a, Actions: ModelActions> ModelQuery<'a> for FindFirst<'a, Actions> { + type Actions = Actions; - client.execute(op).await + const TYPE: ModelOperation = ModelOperation::Read(ModelReadOperation::FindFirst); +} + +impl<'a, Actions: ModelActions> WhereQuery<'a> for FindFirst<'a, Actions> { + fn add_where(&mut self, param: Actions::Where) { + self.where_params.push(param); } } -impl<'a, Actions> BatchQuery for FindFirst<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Actions::Data; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> WithQuery<'a> for FindFirst<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); + } +} - fn graphql(self) -> Operation { - self.exec_operation().0 +impl<'a, Actions: ModelActions> OrderByQuery<'a> for FindFirst<'a, Actions> { + fn add_order_by(&mut self, param: Actions::OrderBy) { + self.order_by_params.push(param); } +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> PaginatedQuery<'a> for FindFirst<'a, Actions> { + fn add_cursor(&mut self, param: Actions::Cursor) { + self.cursor_params.push(param); + } + + fn set_skip(&mut self, skip: i64) { + self.skip = Some(skip); + } + + fn set_take(&mut self, take: i64) { + self.take = Some(take); } } diff --git a/src/queries/find_many.rs b/src/queries/find_many.rs index 86e844f5..345f2879 100644 --- a/src/queries/find_many.rs +++ b/src/queries/find_many.rs @@ -2,19 +2,14 @@ use prisma_models::PrismaValue; use query_core::{Operation, QueryValue, Selection}; use crate::{ - actions::ModelActions, - include::{Include, IncludeType}, - merge_fields, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelQueryType, PrismaClientInternals, WhereInput, + merge_fields, Include, IncludeType, ModelActions, ModelOperation, ModelQuery, + ModelReadOperation, OrderByQuery, PaginatedQuery, PrismaClientInternals, Query, Select, + SelectType, WhereInput, WhereQuery, WithQuery, }; use super::SerializedWhereInput; -pub struct FindMany<'a, Actions> -where - Actions: ModelActions, -{ +pub struct FindMany<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_params: Vec, pub with_params: Vec, @@ -24,19 +19,7 @@ where pub take: Option, } -impl<'a, Actions> ModelAction for FindMany<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Query(ModelQueryType::FindMany); -} - -impl<'a, Actions> FindMany<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> FindMany<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, where_params: Vec) -> Self { Self { client, @@ -167,7 +150,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result> { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for FindMany<'a, Actions> { + type RawType = Vec; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -185,34 +177,51 @@ where ) } - pub async fn exec(self) -> super::Result> { - let (op, client) = self.exec_operation(); + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw + } +} + +impl<'a, Actions: ModelActions> ModelQuery<'a> for FindMany<'a, Actions> { + type Actions = Actions; - client.execute(op).await + const TYPE: ModelOperation = ModelOperation::Read(ModelReadOperation::FindMany); +} + +impl<'a, Actions: ModelActions> WhereQuery<'a> for FindMany<'a, Actions> { + fn add_where(&mut self, param: Actions::Where) { + self.where_params.push(param); } } -impl<'a, Actions> BatchQuery for FindMany<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Vec; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> WithQuery<'a> for FindMany<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); + } +} + +impl<'a, Actions: ModelActions> OrderByQuery<'a> for FindMany<'a, Actions> { + fn add_order_by(&mut self, param: Actions::OrderBy) { + self.order_by_params.push(param); + } +} - fn graphql(self) -> Operation { - self.exec_operation().0 +impl<'a, Actions: ModelActions> PaginatedQuery<'a> for FindMany<'a, Actions> { + fn add_cursor(&mut self, param: Actions::Cursor) { + self.cursor_params.push(param); } - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw + fn set_skip(&mut self, skip: i64) { + self.skip = Some(skip); + } + + fn set_take(&mut self, take: i64) { + self.take = Some(take); } } #[derive(Clone)] -pub struct ManyArgs -where - Actions: ModelActions, -{ +pub struct ManyArgs { pub where_params: Vec, pub with_params: Vec, pub order_by_params: Vec, @@ -221,10 +230,7 @@ where pub take: Option, } -impl ManyArgs -where - Actions: ModelActions, -{ +impl ManyArgs { pub fn new(where_params: Vec) -> Self { Self { where_params, diff --git a/src/queries/find_unique.rs b/src/queries/find_unique.rs index fdeff8e3..97930103 100644 --- a/src/queries/find_unique.rs +++ b/src/queries/find_unique.rs @@ -4,35 +4,18 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use crate::{ - include::{Include, IncludeType}, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelQueryType, PrismaClientInternals, - WhereInput, + Include, IncludeType, ModelActions, ModelOperation, ModelQuery, ModelReadOperation, + PrismaClientInternals, Query, Select, SelectType, WhereInput, WithQuery, }; -pub struct FindUnique<'a, Actions> -where - Actions: ModelActions, -{ +pub struct FindUnique<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_param: Actions::Where, pub with_params: Vec, _data: PhantomData<(Actions::Set, Actions::Data)>, } -impl<'a, Actions> ModelAction for FindUnique<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Query(ModelQueryType::FindUnique); -} - -impl<'a, Actions> FindUnique<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> FindUnique<'a, Actions> { pub fn new(client: &'a PrismaClientInternals, where_param: Actions::Where) -> Self { Self { client, @@ -83,7 +66,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result> { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for FindUnique<'a, Actions> { + type RawType = Option; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -94,26 +86,20 @@ where ) } - pub async fn exec(self) -> super::Result> { - let (op, client) = self.exec_operation(); - - client.execute(op).await + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw } } -impl<'a, Actions> BatchQuery for FindUnique<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Option; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> ModelQuery<'a> for FindUnique<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 - } + const TYPE: ModelOperation = ModelOperation::Read(ModelReadOperation::FindUnique); +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> WithQuery<'a> for FindUnique<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); } } diff --git a/src/queries/include.rs b/src/queries/include.rs index bb242480..4ef5fc32 100644 --- a/src/queries/include.rs +++ b/src/queries/include.rs @@ -1,25 +1,25 @@ +use query_core::{Operation, Selection}; use std::marker::PhantomData; -use query_core::{Operation, Selection}; -use serde::de::DeserializeOwned; +use crate::{PrismaClientInternals, Query}; -use crate::{BatchQuery, PrismaClientInternals}; +use super::query; pub trait IncludeType { // TODO: ModelActions - type Data: DeserializeOwned; + type Data: query::Data; type ModelData; fn to_selections(self) -> Vec; } -pub struct Include<'a, Data: DeserializeOwned> { +pub struct Include<'a, Data> { operation: Operation, client: &'a PrismaClientInternals, _data: PhantomData, } -impl<'a, Data: DeserializeOwned> Include<'a, Data> { +impl<'a, Data: query::Data> Include<'a, Data> { pub fn new(client: &'a PrismaClientInternals, operation: Operation) -> Self { Self { client, @@ -29,16 +29,16 @@ impl<'a, Data: DeserializeOwned> Include<'a, Data> { } pub async fn exec(self) -> super::Result { - self.client.execute(self.operation).await + super::exec(self).await } } -impl<'a, Data: DeserializeOwned> BatchQuery for Include<'a, Data> { +impl<'a, Data: query::Data> Query<'a> for Include<'a, Data> { type RawType = Data; type ReturnType = Self::RawType; - fn graphql(self) -> Operation { - self.operation + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { + (self.operation, self.client) } fn convert(raw: Self::RawType) -> Self::ReturnType { diff --git a/src/queries/mod.rs b/src/queries/mod.rs index 99499470..12658d3b 100644 --- a/src/queries/mod.rs +++ b/src/queries/mod.rs @@ -1,19 +1,21 @@ -pub mod batch; -pub mod count; -pub mod create; -pub mod create_many; -pub mod delete; -pub mod delete_many; -pub mod execute_raw; -pub mod find_first; -pub mod find_many; -pub mod find_unique; -pub mod include; -pub mod query_raw; -pub mod select; -pub mod update; -pub mod update_many; -pub mod upsert; +mod batch; +mod count; +mod create; +mod create_many; +mod delete; +mod delete_many; +mod error; +mod execute_raw; +mod find_first; +mod find_many; +mod find_unique; +mod include; +mod query; +mod query_raw; +mod select; +mod update; +mod update_many; +mod upsert; pub use batch::*; pub use count::*; @@ -21,18 +23,20 @@ pub use create::*; pub use create_many::*; pub use delete::*; pub use delete_many::*; +pub use error::*; pub use execute_raw::*; pub use find_first::*; pub use find_many::*; pub use find_unique::*; +pub use include::*; +pub use query::*; pub use query_raw::*; +pub use select::*; pub use update::*; pub use update_many::*; pub use upsert::*; pub use query_core::{schema::QuerySchemaRef, Operation, Selection}; -use thiserror::Error; -use user_facing_errors::UserFacingError; pub enum SerializedWhereValue { Object(Vec<(String, prisma_models::PrismaValue)>), @@ -94,39 +98,11 @@ impl Into<(String, prisma_models::PrismaValue)> for SerializedWhereInput { } } -#[derive(Debug, Error)] -pub enum QueryError { - #[error("Error executing query: {} - {}", .0.as_known().map(|k| k.error_code.to_string()).unwrap_or("Unknown".to_string()), .0.message())] - Execute(user_facing_errors::Error), +pub(crate) async fn exec<'a, Q: Query<'a>>( + query: Q, +) -> error::Result<>::ReturnType> { + let (op, client) = query.graphql(); + let res = client.execute(op).await; - #[error("Error serializing query result: {0}")] - Serialize(#[from] serde_value::SerializerError), - - #[error("Error deserializing query result into return type: {0}")] - Deserialize(#[from] serde_value::DeserializerError), -} - -impl QueryError { - pub fn is_prisma_error(&self) -> bool { - match self { - Self::Execute(error) => error - .as_known() - .map(|e| e.error_code == ::ERROR_CODE) - .unwrap_or(false), - _ => false, - } - } -} - -pub type Result = std::result::Result; - -#[cfg(feature = "rspc")] -impl From for rspc::Error { - fn from(err: QueryError) -> Self { - rspc::Error::with_cause( - rspc::ErrorCode::InternalServerError, - "Internal server error occurred while completing database operation!".into(), - err, - ) - } + res.map(Q::convert) } diff --git a/src/queries/query.rs b/src/queries/query.rs new file mode 100644 index 00000000..cc720c62 --- /dev/null +++ b/src/queries/query.rs @@ -0,0 +1,135 @@ +use dml::PrismaValue; +use query_core::{Operation, Selection, SelectionArgument}; +use serde::de::DeserializeOwned; + +use crate::{PrismaClientInternals, WhereInput}; + +pub trait Query<'a> { + type RawType: DeserializeOwned; + type ReturnType: 'static; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals); + + /// Function for converting between raw database data and the type expected by the user. + /// Necessary for things like raw queries + fn convert(raw: Self::RawType) -> Self::ReturnType; +} + +pub trait ModelActions { + type Data: Data; + type Where: WhereInput; + type Set: Into<(String, PrismaValue)>; + type With: Into; + type OrderBy: Into<(String, PrismaValue)>; + type Cursor: Into; + + const MODEL: &'static str; + + fn scalar_selections() -> Vec; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ModelReadOperation { + FindUnique, + FindFirst, + FindMany, + Count, +} + +impl ModelReadOperation { + pub fn name(&self) -> &'static str { + match self { + Self::FindUnique => "findUnique", + Self::FindFirst => "findFirst", + Self::FindMany => "findMany", + Self::Count => "aggregate", + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ModelWriteOperation { + Create, + CreateMany, + Update, + UpdateMany, + Delete, + DeleteMany, + Upsert, +} + +impl ModelWriteOperation { + pub fn name(&self) -> &'static str { + match self { + Self::Create => "createOne", + Self::CreateMany => "createMany", + Self::Update => "updateOne", + Self::UpdateMany => "updateMany", + Self::Delete => "deleteOne", + Self::DeleteMany => "deleteMany", + Self::Upsert => "upsertOne", + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ModelOperation { + Read(ModelReadOperation), + Write(ModelWriteOperation), +} + +impl ModelOperation { + pub fn name(&self) -> &'static str { + match self { + Self::Read(q) => q.name(), + Self::Write(q) => q.name(), + } + } +} + +pub trait ModelQuery<'a>: Query<'a> { + type Actions: ModelActions; + + const TYPE: ModelOperation; + + fn base_selection( + arguments: impl IntoIterator, + nested_selections: impl IntoIterator, + ) -> Selection { + Selection::new( + format!("{}{}", Self::TYPE.name(), Self::Actions::MODEL), + Some("result".to_string()), + arguments.into_iter().collect::>(), + nested_selections.into_iter().collect::>(), + ) + } +} + +pub trait WhereQuery<'a>: ModelQuery<'a> { + fn add_where(&mut self, param: <>::Actions as ModelActions>::Where); +} + +pub trait WithQuery<'a>: ModelQuery<'a> { + fn add_with( + &mut self, + param: impl Into<<>::Actions as ModelActions>::With>, + ); +} + +pub trait OrderByQuery<'a>: ModelQuery<'a> { + fn add_order_by(&mut self, param: <>::Actions as ModelActions>::OrderBy); +} + +pub trait PaginatedQuery<'a>: ModelQuery<'a> { + fn add_cursor(&mut self, param: <>::Actions as ModelActions>::Cursor); + fn set_skip(&mut self, skip: i64); + fn set_take(&mut self, take: i64); +} + +pub trait SetQuery<'a>: ModelQuery<'a> { + fn add_set(&mut self, param: <>::Actions as ModelActions>::Set); +} + +pub trait Data: DeserializeOwned + 'static {} + +impl Data for T {} diff --git a/src/queries/query_raw.rs b/src/queries/query_raw.rs index 24fd4b2d..f8b1d239 100644 --- a/src/queries/query_raw.rs +++ b/src/queries/query_raw.rs @@ -7,7 +7,7 @@ use serde_json::Value; use crate::{ raw::{Raw, RawOperationData, RawPrismaValue}, - BatchQuery, PrismaClientInternals, + PrismaClientInternals, Query, }; pub struct QueryRaw<'a, Data> @@ -22,7 +22,7 @@ where impl<'a, Data> QueryRaw<'a, Data> where - Data: DeserializeOwned, + Data: DeserializeOwned + 'static, { pub fn new(client: &'a PrismaClientInternals, query: Raw, database: &'static str) -> Self { let (sql, params) = query.convert(database); @@ -35,24 +35,6 @@ where } } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { - ( - Operation::Write(Selection::new( - "queryRaw".to_string(), - None, - [ - ("query".to_string(), PrismaValue::String(self.sql).into()), - ( - "parameters".to_string(), - PrismaValue::String(serde_json::to_string(&self.params).unwrap()).into(), - ), - ], - [], - )), - self.client, - ) - } - pub(crate) fn convert(raw: RawOperationData) -> super::Result> { let typed_data: Vec> = raw .into_iter() @@ -74,21 +56,33 @@ where } pub async fn exec(self) -> super::Result> { - let (op, client) = self.exec_operation(); - - client.execute(op).await.and_then(Self::convert) + super::exec(self).await } } -impl<'a, Data> BatchQuery for QueryRaw<'a, Data> +impl<'a, Data> Query<'a> for QueryRaw<'a, Data> where - Data: DeserializeOwned, + Data: DeserializeOwned + 'static, { type RawType = RawOperationData; type ReturnType = Vec; - fn graphql(self) -> Operation { - self.exec_operation().0 + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { + ( + Operation::Write(Selection::new( + "queryRaw".to_string(), + None, + [ + ("query".to_string(), PrismaValue::String(self.sql).into()), + ( + "parameters".to_string(), + PrismaValue::String(serde_json::to_string(&self.params).unwrap()).into(), + ), + ], + [], + )), + self.client, + ) } fn convert(raw: Self::RawType) -> Self::ReturnType { diff --git a/src/queries/select.rs b/src/queries/select.rs index acc7f66c..f523a01e 100644 --- a/src/queries/select.rs +++ b/src/queries/select.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use query_core::{Operation, Selection}; use serde::de::DeserializeOwned; -use crate::{BatchQuery, PrismaClientInternals}; +use crate::{PrismaClientInternals, Query}; pub trait SelectType { // TODO: ModelActions @@ -33,12 +33,12 @@ impl<'a, Data: DeserializeOwned> Select<'a, Data> { } } -impl<'a, Data: DeserializeOwned> BatchQuery for Select<'a, Data> { +impl<'a, Data: DeserializeOwned + 'static> Query<'a> for Select<'a, Data> { type RawType = Data; type ReturnType = Self::RawType; - fn graphql(self) -> Operation { - self.operation + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { + (self.operation, self.client) } fn convert(raw: Self::RawType) -> Self::ReturnType { diff --git a/src/queries/update.rs b/src/queries/update.rs index a55859d4..b79c4f94 100644 --- a/src/queries/update.rs +++ b/src/queries/update.rs @@ -2,36 +2,19 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use crate::{ - include::{Include, IncludeType}, - merge_fields, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelMutationType, - PrismaClientInternals, WhereInput, + merge_fields, Include, IncludeType, ModelActions, ModelOperation, ModelQuery, + ModelWriteOperation, PrismaClientInternals, Query, Select, SelectType, SetQuery, WhereInput, + WithQuery, }; -pub struct Update<'a, Actions> -where - Actions: ModelActions, -{ +pub struct Update<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_param: Actions::Where, pub set_params: Vec, pub with_params: Vec, } -impl<'a, Actions> ModelAction for Update<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::Update); -} - -impl<'a, Actions> Update<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> Update<'a, Actions> { pub fn new( client: &'a PrismaClientInternals, where_param: Actions::Where, @@ -102,7 +85,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for Update<'a, Actions> { + type RawType = Actions::Data; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -117,30 +109,25 @@ where ) } - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); - - Ok(res) + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw } } -impl<'a, Actions> BatchQuery for Update<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Actions::Data; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> ModelQuery<'a> for Update<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::Update); +} + +impl<'a, Actions: ModelActions> SetQuery<'a> for Update<'a, Actions> { + fn add_set(&mut self, param: Actions::Set) { + self.set_params.push(param); } +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> WithQuery<'a> for Update<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); } } diff --git a/src/queries/update_many.rs b/src/queries/update_many.rs index ca3ce479..e8e1d095 100644 --- a/src/queries/update_many.rs +++ b/src/queries/update_many.rs @@ -2,32 +2,17 @@ use prisma_models::PrismaValue; use query_core::Operation; use crate::{ - merge_fields, BatchQuery, BatchResult, ModelAction, ModelActionType, ModelActions, - ModelMutationType, PrismaClientInternals, WhereInput, + merge_fields, BatchResult, ModelActions, ModelOperation, ModelQuery, ModelWriteOperation, + PrismaClientInternals, Query, SetQuery, WhereInput, WhereQuery, }; -pub struct UpdateMany<'a, Actions> -where - Actions: ModelActions, -{ +pub struct UpdateMany<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_params: Vec, pub set_params: Vec, } -impl<'a, Actions> ModelAction for UpdateMany<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::UpdateMany); -} - -impl<'a, Actions> UpdateMany<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> UpdateMany<'a, Actions> { pub fn new( client: &'a PrismaClientInternals, where_params: Vec, @@ -40,7 +25,16 @@ where } } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for UpdateMany<'a, Actions> { + type RawType = BatchResult; + type ReturnType = i64; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { ( Operation::Write(Self::base_selection( [ @@ -73,34 +67,25 @@ where ) } - pub(crate) fn convert(raw: BatchResult) -> i64 { + fn convert(raw: Self::RawType) -> Self::ReturnType { raw.count } +} - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await.map(Self::convert)?; - - #[cfg(feature = "mutation-callbacks")] - client.notify_model_mutation::(); +impl<'a, Actions: ModelActions> ModelQuery<'a> for UpdateMany<'a, Actions> { + type Actions = Actions; - Ok(res) - } + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::UpdateMany); } -impl<'a, Actions> BatchQuery for UpdateMany<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = BatchResult; - type ReturnType = i64; - - fn graphql(self) -> Operation { - self.exec_operation().0 +impl<'a, Actions: ModelActions> WhereQuery<'a> for UpdateMany<'a, Actions> { + fn add_where(&mut self, param: Actions::Where) { + self.where_params.push(param); } +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - Self::convert(raw) +impl<'a, Actions: ModelActions> SetQuery<'a> for UpdateMany<'a, Actions> { + fn add_set(&mut self, param: Actions::Set) { + self.set_params.push(param); } } diff --git a/src/queries/upsert.rs b/src/queries/upsert.rs index 1a9ed1a9..a6327dbe 100644 --- a/src/queries/upsert.rs +++ b/src/queries/upsert.rs @@ -2,16 +2,11 @@ use prisma_models::PrismaValue; use query_core::{Operation, Selection}; use crate::{ - include::{Include, IncludeType}, - select::{Select, SelectType}, - BatchQuery, ModelAction, ModelActionType, ModelActions, ModelMutationType, - PrismaClientInternals, WhereInput, + Include, IncludeType, ModelActions, ModelOperation, ModelQuery, ModelWriteOperation, + PrismaClientInternals, Query, Select, SelectType, WhereInput, WithQuery, }; -pub struct Upsert<'a, Actions> -where - Actions: ModelActions, -{ +pub struct Upsert<'a, Actions: ModelActions> { client: &'a PrismaClientInternals, pub where_param: Actions::Where, pub create_params: Vec, @@ -19,19 +14,7 @@ where pub with_params: Vec, } -impl<'a, Actions> ModelAction for Upsert<'a, Actions> -where - Actions: ModelActions, -{ - type Actions = Actions; - - const TYPE: ModelActionType = ModelActionType::Mutation(ModelMutationType::Upsert); -} - -impl<'a, Actions> Upsert<'a, Actions> -where - Actions: ModelActions, -{ +impl<'a, Actions: ModelActions> Upsert<'a, Actions> { pub fn new( client: &'a PrismaClientInternals, where_param: Actions::Where, @@ -107,7 +90,16 @@ where ) } - pub(crate) fn exec_operation(self) -> (Operation, &'a PrismaClientInternals) { + pub async fn exec(self) -> super::Result { + super::exec(self).await + } +} + +impl<'a, Actions: ModelActions> Query<'a> for Upsert<'a, Actions> { + type RawType = Actions::Data; + type ReturnType = Self::RawType; + + fn graphql(self) -> (Operation, &'a PrismaClientInternals) { let mut scalar_selections = Actions::scalar_selections(); scalar_selections.extend(self.with_params.into_iter().map(Into::into)); @@ -123,28 +115,19 @@ where ) } - pub async fn exec(self) -> super::Result { - let (op, client) = self.exec_operation(); - - let res = client.execute(op).await?; - client.notify_model_mutation::(); - - Ok(res) + fn convert(raw: Self::RawType) -> Self::ReturnType { + raw } } -impl<'a, Actions> BatchQuery for Upsert<'a, Actions> -where - Actions: ModelActions, -{ - type RawType = Actions::Data; - type ReturnType = Self::RawType; +impl<'a, Actions: ModelActions> ModelQuery<'a> for Upsert<'a, Actions> { + type Actions = Actions; - fn graphql(self) -> Operation { - self.exec_operation().0 - } + const TYPE: ModelOperation = ModelOperation::Write(ModelWriteOperation::Upsert); +} - fn convert(raw: Self::RawType) -> Self::ReturnType { - raw +impl<'a, Actions: ModelActions> WithQuery<'a> for Upsert<'a, Actions> { + fn add_with(&mut self, param: impl Into) { + self.with_params.push(param.into()); } }