Repository: Brendonovich/prisma-client-rust (language: rust)

Issue:
Record counts



Discussion / hints:
Doesn't hurt to add an extra field to each data struct for counts.
Relation counts will be a separate issue

Fix patch:
diff --git a/cli/src/generator/models.rs b/cli/src/generator/models.rs
index 851e0e9f..3dd6c62d 100644
--- a/cli/src/generator/models.rs
+++ b/cli/src/generator/models.rs
@@ -1268,6 +1268,7 @@ pub fn generate(args: &GenerateArgs) -> Vec<TokenStream> {
                 pub type UniqueArgs = prisma_client_rust::UniqueArgs<WithParam>;
                 pub type ManyArgs = prisma_client_rust::ManyArgs<WhereParam, WithParam, OrderByParam, Cursor>;
                 
+                pub type Count<'a> = prisma_client_rust::Count<'a, WhereParam, OrderByParam, Cursor>;
                 pub type Create<'a> = prisma_client_rust::Create<'a, SetParam, WithParam, Data>;
                 pub type FindUnique<'a> = prisma_client_rust::FindUnique<'a, WhereParam, WithParam, SetParam, Data>;
                 pub type FindMany<'a> = prisma_client_rust::FindMany<'a, WhereParam, WithParam, OrderByParam, Cursor, SetParam, Data>;
@@ -1293,31 +1294,31 @@ pub fn generate(args: &GenerateArgs) -> Vec<TokenStream> {
                         )
                     }
 
-                    pub fn find_unique(self, param: UniqueWhereParam) -> FindUnique<'a> {
+                    pub fn find_unique(self, r#where: UniqueWhereParam) -> FindUnique<'a> {
                         FindUnique::new(
                             self.client._new_query_context(),
                             QueryInfo::new(#model_name_string, _outputs()),
-                            param.into()
+                            r#where.into()
                         )
                     }
 
-                    pub fn find_first(self, params: Vec<WhereParam>) -> FindFirst<'a> {
+                    pub fn find_first(self, r#where: Vec<WhereParam>) -> FindFirst<'a> {
                         FindFirst::new(
                             self.client._new_query_context(),
                             QueryInfo::new(#model_name_string, _outputs()),
-                            params
+                            r#where
                         )
                     }
 
-                    pub fn find_many(self, params: Vec<WhereParam>) -> FindMany<'a> {
+                    pub fn find_many(self, r#where: Vec<WhereParam>) -> FindMany<'a> {
                         FindMany::new(
                             self.client._new_query_context(),
                             QueryInfo::new(#model_name_string, _outputs()),
-                            params
+                            r#where
                         )
                     }
 
-                    pub fn upsert(self, _where: UniqueWhereParam, _create: (#(#create_args_tuple_types)* Vec<SetParam>), _update: Vec<SetParam>) -> Upsert<'a> {
+                    pub fn upsert(self, r#where: UniqueWhereParam, _create: (#(#create_args_tuple_types)* Vec<SetParam>), _update: Vec<SetParam>) -> Upsert<'a> {
                         let (
                             #(#create_args_destructured)*
                             mut _params
@@ -1328,11 +1329,19 @@ pub fn generate(args: &GenerateArgs) -> Vec<TokenStream> {
                         Upsert::new(
                             self.client._new_query_context(),
                             QueryInfo::new(#model_name_string, _outputs()),
-                            _where.into(),
+                            r#where.into(),
                             _params,
                             _update
                         )
                     }
+                    
+                    pub fn count(self, r#where: Vec<WhereParam>) -> Count<'a> {
+                        Count::new(
+                            self.client._new_query_context(),
+                            QueryInfo::new(#model_name_string, _outputs()),
+                            r#where
+                        )
+                    }
                 }
             }
         }
diff --git a/src/queries/count.rs b/src/queries/count.rs
new file mode 100644
index 00000000..6f133230
--- /dev/null
+++ b/src/queries/count.rs
@@ -0,0 +1,121 @@
+use prisma_models::PrismaValue;
+use query_core::{Operation, Selection};
+use serde::Deserialize;
+
+use crate::{transform_equals, SerializedWhere};
+
+use super::{QueryContext, QueryInfo};
+
+pub struct Count<'a, Where, OrderBy, Cursor> {
+    ctx: QueryContext<'a>,
+    info: QueryInfo,
+    pub where_params: Vec<Where>,
+    pub order_by_params: Vec<OrderBy>,
+    pub cursor_params: Vec<Cursor>,
+    pub skip: Option<i64>,
+    pub take: Option<i64>,
+}
+
+impl<'a, Where, OrderBy, Cursor> Count<'a, Where, OrderBy, Cursor>
+where
+    Where: Into<SerializedWhere>,
+    OrderBy: Into<(String, PrismaValue)>,
+    Cursor: Into<(String, PrismaValue)>,
+{
+    pub fn new(ctx: QueryContext<'a>, info: QueryInfo, where_params: Vec<Where>) -> Self {
+        Self {
+            ctx,
+            info,
+            where_params,
+            order_by_params: vec![],
+            cursor_params: vec![],
+            skip: None,
+            take: None,
+        }
+    }
+
+    pub fn order_by(mut self, param: impl Into<OrderBy>) -> Self {
+        self.order_by_params.push(param.into());
+        self
+    }
+
+    pub fn cursor(mut self, param: impl Into<Cursor>) -> Self {
+        self.cursor_params.push(param.into());
+        self
+    }
+
+    pub fn skip(mut self, skip: i64) -> Self {
+        self.skip = Some(skip);
+        self
+    }
+
+    pub fn take(mut self, take: i64) -> Self {
+        self.take = Some(take);
+        self
+    }
+
+    pub async fn exec(self) -> super::Result<i64> {
+        let Self {
+            ctx,
+            info,
+            where_params,
+            order_by_params,
+            cursor_params,
+            skip,
+            take,
+            ..
+        } = self;
+
+        let QueryInfo { model, .. } = info;
+
+        let mut selection = Selection::builder(format!("aggregate{}", model));
+
+        selection.alias("result");
+
+        if where_params.len() > 0 {
+            selection.push_argument(
+                "where",
+                PrismaValue::Object(transform_equals(where_params.into_iter())),
+            );
+        }
+
+        selection.push_nested_selection({
+            let mut count_builder = Selection::builder("_count");
+            count_builder.push_nested_selection(Selection::builder("_all").build());
+            count_builder.build()
+        });
+
+        if order_by_params.len() > 0 {
+            selection.push_argument(
+                "orderBy".to_string(),
+                PrismaValue::Object(order_by_params.into_iter().map(Into::into).collect()),
+            );
+        }
+
+        if cursor_params.len() > 0 {
+            selection.push_argument(
+                "cursor".to_string(),
+                PrismaValue::Object(cursor_params.into_iter().map(Into::into).collect()),
+            );
+        }
+
+        skip.map(|skip| selection.push_argument("skip".to_string(), PrismaValue::Int(skip as i64)));
+        take.map(|take| selection.push_argument("take".to_string(), PrismaValue::Int(take as i64)));
+
+        let op = Operation::Read(selection.build());
+
+        #[derive(Deserialize)]
+        struct CountAggregateResult {
+            _count: CountResult,
+        }
+
+        #[derive(Deserialize)]
+        struct CountResult {
+            _all: i64,
+        }
+
+        ctx.execute(op)
+            .await
+            .map(|res: CountAggregateResult| res._count._all)
+    }
+}
diff --git a/src/queries/mod.rs b/src/queries/mod.rs
index f3ea1d71..7de1cbc6 100644
--- a/src/queries/mod.rs
+++ b/src/queries/mod.rs
@@ -1,3 +1,4 @@
+pub mod count;
 pub mod create;
 pub mod delete;
 pub mod delete_many;
@@ -10,6 +11,7 @@ pub mod update;
 pub mod update_many;
 pub mod upsert;
 
+pub use count::*;
 pub use create::*;
 pub use delete::*;
 pub use delete_many::*;


Test patch:
diff --git a/integration-tests/tests/count.rs b/integration-tests/tests/count.rs
new file mode 100644
index 00000000..192a9e27
--- /dev/null
+++ b/integration-tests/tests/count.rs
@@ -0,0 +1,120 @@
+use crate::db::*;
+use crate::utils::*;
+
+#[tokio::test]
+async fn test_count() -> TestResult {
+    let client = client().await;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    let count = client.post().count(vec![]).exec().await?;
+
+    assert_eq!(count, 1);
+
+    cleanup(client).await
+}
+
+#[tokio::test]
+async fn test_count_no_results() -> TestResult {
+    let client = client().await;
+
+    let count = client
+        .post()
+        .count(vec![post::title::equals("lkdjkfsldkf".to_string())])
+        .exec()
+        .await?;
+
+    assert_eq!(count, 0);
+
+    cleanup(client).await
+}
+
+#[tokio::test]
+async fn test_take() -> TestResult {
+    let client = client().await;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    let count = client.post().count(vec![]).take(1).exec().await?;
+
+    assert_eq!(count, 1);
+
+    cleanup(client).await
+}
+
+#[tokio::test]
+async fn test_skip() -> TestResult {
+    let client = client().await;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    client
+        .post()
+        .create(
+            post::title::set("Hi from Prisma!".to_string()),
+            post::published::set(true),
+            vec![],
+        )
+        .exec()
+        .await?;
+
+    let count = client.post().count(vec![]).skip(1).exec().await?;
+
+    assert_eq!(count, 2);
+
+    cleanup(client).await
+}
diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs
index 954f39c2..ee745b67 100644
--- a/integration-tests/tests/lib.rs
+++ b/integration-tests/tests/lib.rs
@@ -1,14 +1,14 @@
 mod db;
-// mod db_gql;
 mod utils;
 
+mod count;
 mod create;
 mod delete;
 mod delete_many;
 mod find_first;
 mod find_many;
 mod find_unique;
+mod raw;
 mod update;
 mod upsert;
 mod with;
-mod raw;