sqlx-repo 0.2.2

repository pattern on top of sqlx
Documentation
use anyhow::Result;
use futures::{StreamExt, stream::FuturesUnordered};
use sqlx_repo::prelude::*;

mod migrations;

#[repo(Send + Sync + std::fmt::Debug)]
impl SelectRepo for DatabaseRepository {
    async fn migrate(&self) -> Result<()> {
        let migrator = migrator!(&migrations::all_migrations()).await?;
        migrator.run(&self.pool).await?;
        Ok(())
    }

    async fn select_order_asc(&self) -> Result<Vec<i32>> {
        let q = query!("select * from select_items order by id asc");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_order_desc(&self) -> Result<Vec<i32>> {
        let q = query!("select * from select_items order by id desc");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_limit(&self) -> Result<Vec<i32>> {
        let q = query!("select * from select_items order by id limit 3");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_limit_offset(&self) -> Result<Vec<i32>> {
        let q = query!("select * from select_items order by id limit 2 offset 2");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_order_desc_limit(&self) -> Result<Vec<i32>> {
        let q = query!("select * from select_items order by id desc limit 3");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_where_eq(&self, id: i32) -> Result<Vec<i32>> {
        let q = query!("select * from select_items where id = ? order by id");
        Ok(sqlx::query(q)
            .bind(id)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_where_or(&self, a: i32, b: i32) -> Result<Vec<i32>> {
        let q = query!("select * from select_items where id = ? or id = ? order by id");
        Ok(sqlx::query(q)
            .bind(a)
            .bind(b)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn count_all(&self) -> Result<i64> {
        let q = query!("select count(*) from select_items");
        Ok(sqlx::query(q).fetch_one(&self.pool).await?.get::<i64, _>(0))
    }

    async fn select_distinct_category(&self) -> Result<Vec<i32>> {
        let q = query!("select distinct category from select_pairs order by category");
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }

    async fn select_group_by_count(&self) -> Result<Vec<i64>> {
        let q = query!("select category, count(*) from select_pairs group by category");
        let mut counts: Vec<i64> = sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i64, _>(1))
            .collect();
        counts.sort_unstable();
        Ok(counts)
    }

    async fn select_join(&self) -> Result<Vec<i32>> {
        let q = query!(
            "select select_items.id from select_items join select_pairs on select_items.id = select_pairs.category order by select_items.id"
        );
        Ok(sqlx::query(q)
            .fetch_all(&self.pool)
            .await?
            .into_iter()
            .map(|r| r.get::<i32, _>(0))
            .collect())
    }
}

#[tokio::test]
async fn test_select() {
    let urls = &[
        "sqlite::memory:",
        "postgres://postgres:root@127.0.0.1:5432/postgres",
        "mysql://root:root@127.0.0.1:3306/mysql",
    ];
    let mut repos = urls
        .iter()
        .map(|url| async move {
            let repo = <dyn SelectRepo>::new(url).await.unwrap();
            repo.migrate().await.unwrap();

            assert_eq!(
                vec![1, 2, 3, 4, 5],
                repo.select_order_asc().await.unwrap(),
                "order asc at {url}"
            );
            assert_eq!(
                vec![5, 4, 3, 2, 1],
                repo.select_order_desc().await.unwrap(),
                "order desc at {url}"
            );
            assert_eq!(
                vec![1, 2, 3],
                repo.select_limit().await.unwrap(),
                "limit at {url}"
            );
            assert_eq!(
                vec![3, 4],
                repo.select_limit_offset().await.unwrap(),
                "limit+offset at {url}"
            );
            assert_eq!(
                vec![5, 4, 3],
                repo.select_order_desc_limit().await.unwrap(),
                "order desc+limit at {url}"
            );
            assert_eq!(
                vec![3],
                repo.select_where_eq(3).await.unwrap(),
                "where at {url}"
            );
            assert_eq!(
                vec![2, 4],
                repo.select_where_or(2, 4).await.unwrap(),
                "where or at {url}"
            );
            assert_eq!(5, repo.count_all().await.unwrap(), "count at {url}");

            assert_eq!(
                vec![1, 2, 3],
                repo.select_distinct_category().await.unwrap(),
                "distinct at {url}"
            );
            assert_eq!(
                vec![1, 2, 2],
                repo.select_group_by_count().await.unwrap(),
                "group by at {url}"
            );
            assert_eq!(
                vec![1, 1, 2, 2, 3],
                repo.select_join().await.unwrap(),
                "join at {url}"
            );
        })
        .collect::<FuturesUnordered<_>>();
    while let Some(()) = repos.next().await {}
}