Our Personal Data Server from scratch! tranquil.farm
oauth atproto pds rust postgresql objectstorage fun
at main 99 lines 2.9 kB view raw
1use async_trait::async_trait; 2use sqlx::PgPool; 3use tranquil_db_traits::{Backlink, BacklinkRepository, DbError}; 4use tranquil_types::{AtUri, Nsid}; 5use uuid::Uuid; 6 7use super::user::map_sqlx_error; 8 9pub struct PostgresBacklinkRepository { 10 pool: PgPool, 11} 12 13impl PostgresBacklinkRepository { 14 pub fn new(pool: PgPool) -> Self { 15 Self { pool } 16 } 17} 18 19#[async_trait] 20impl BacklinkRepository for PostgresBacklinkRepository { 21 async fn get_backlink_conflicts( 22 &self, 23 repo_id: Uuid, 24 collection: &Nsid, 25 backlinks: &[Backlink], 26 ) -> Result<Vec<AtUri>, DbError> { 27 if backlinks.is_empty() { 28 return Ok(Vec::new()); 29 } 30 31 let paths: Vec<&str> = backlinks.iter().map(|b| b.path.as_str()).collect(); 32 let link_tos: Vec<&str> = backlinks.iter().map(|b| b.link_to.as_str()).collect(); 33 let collection_pattern = format!("%/{}/%", collection.as_str()); 34 35 let results = sqlx::query_scalar!( 36 r#" 37 SELECT DISTINCT uri 38 FROM backlinks 39 WHERE repo_id = $1 40 AND uri LIKE $4 41 AND (path, link_to) IN (SELECT unnest($2::text[]), unnest($3::text[])) 42 "#, 43 repo_id, 44 &paths as &[&str], 45 &link_tos as &[&str], 46 collection_pattern 47 ) 48 .fetch_all(&self.pool) 49 .await 50 .map_err(map_sqlx_error)?; 51 52 Ok(results.into_iter().map(Into::into).collect()) 53 } 54 55 async fn add_backlinks(&self, repo_id: Uuid, backlinks: &[Backlink]) -> Result<(), DbError> { 56 if backlinks.is_empty() { 57 return Ok(()); 58 } 59 60 let uris: Vec<&str> = backlinks.iter().map(|b| b.uri.as_str()).collect(); 61 let paths: Vec<&str> = backlinks.iter().map(|b| b.path.as_str()).collect(); 62 let link_tos: Vec<&str> = backlinks.iter().map(|b| b.link_to.as_str()).collect(); 63 64 sqlx::query!( 65 r#" 66 INSERT INTO backlinks (uri, path, link_to, repo_id) 67 SELECT unnest($1::text[]), unnest($2::text[]), unnest($3::text[]), $4 68 ON CONFLICT (uri, path) DO NOTHING 69 "#, 70 &uris as &[&str], 71 &paths as &[&str], 72 &link_tos as &[&str], 73 repo_id 74 ) 75 .execute(&self.pool) 76 .await 77 .map_err(map_sqlx_error)?; 78 79 Ok(()) 80 } 81 82 async fn remove_backlinks_by_uri(&self, uri: &AtUri) -> Result<(), DbError> { 83 sqlx::query!("DELETE FROM backlinks WHERE uri = $1", uri.as_str()) 84 .execute(&self.pool) 85 .await 86 .map_err(map_sqlx_error)?; 87 88 Ok(()) 89 } 90 91 async fn remove_backlinks_by_repo(&self, repo_id: Uuid) -> Result<(), DbError> { 92 sqlx::query!("DELETE FROM backlinks WHERE repo_id = $1", repo_id) 93 .execute(&self.pool) 94 .await 95 .map_err(map_sqlx_error)?; 96 97 Ok(()) 98 } 99}