use std::collections::HashMap; use sqlx::postgres::PgRow; use sqlx::{PgPool, Row}; use uuid::Uuid; use orlok::pg::{PgDb, PgRepo, Value}; use orlok::{Repo, F}; #[derive(Debug, PartialEq)] struct User { id: Uuid, name: String, emails: Vec, } impl User { fn new(name: &str, emails: Vec) -> Self { Self { id: Uuid::new_v4(), name: name.to_string(), emails, } } } fn dump_user(entity: &User) -> HashMap { HashMap::from([ ("id".to_string(), entity.id.into()), ("name".to_string(), entity.name.clone().into()), ]) } fn load_user(row: &PgRow) -> User { User { id: row.get("id"), name: row.get("name"), emails: row.get("emails"), } } fn users_repo() -> PgRepo { PgRepo::new("users_with_emails", dump_user, load_user) .query( "select u.id, u.name, array_agg(emails.email) as emails from users_with_emails as u left join emails on emails.user_id = u.id group by u.id, u.name", ) .after_add(|u| { u.emails .iter() .map(|e| { sqlx::query("insert into emails (id, user_id, email) values ($1, $2, $3)") .bind(Uuid::new_v4()) .bind(u.id) .bind(e) }) .collect() }) .after_update(|u| { let mut queries = vec![sqlx::query("delete from emails where user_id = $1").bind(u.id)]; queries.extend(u.emails.iter().map(|e| { sqlx::query("insert into emails (id, user_id, email) values ($1, $2, $3)") .bind(Uuid::new_v4()) .bind(u.id) .bind(e) })); queries }) } pub async fn db<'a>() -> PgDb<'a> { let pool = PgPool::connect("postgresql://orlok:orlok@localhost/orlok") .await .unwrap(); sqlx::query( "create table if not exists users_with_emails ( id uuid primary key, name text )", ) .execute(&pool) .await .unwrap(); sqlx::query( "create table if not exists emails ( id uuid primary key, user_id uuid references users_with_emails(id) on delete cascade, email text )", ) .execute(&pool) .await .unwrap(); sqlx::query("delete from users_with_emails") .execute(&pool) .await .unwrap(); PgDb::new(pool) } #[tokio::test] async fn test_one_to_many() { let db = db().await; let repo = users_repo(); let bob = User::new( "Bob", vec!["bob@test.com".to_string(), "bob123@test.com".to_string()], ); repo.add(&db, &bob).await.unwrap(); let alice = User::new("Alice", vec!["alice@test.com".to_string()]); repo.add(&db, &alice).await.unwrap(); let b = repo.get(&db, &F::eq("id", bob.id)).await.unwrap().unwrap(); assert_eq!(b, bob); let a = repo .get(&db, &F::eq("id", alice.id)) .await .unwrap() .unwrap(); assert_eq!(a, alice); }