add follows
This commit is contained in:
parent
cd4c81af57
commit
57026cb21d
8 changed files with 204 additions and 0 deletions
9
migrations/20221207230140_create_follows.sql
Normal file
9
migrations/20221207230140_create_follows.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE follows (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
follower_id BIGINT REFERENCES accounts (id) ON DELETE CASCADE,
|
||||
followee_id BIGINT REFERENCES accounts (id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX index_follows_on_follower_id ON follows (follower_id);
|
||||
CREATE INDEX index_follows_on_followee_id ON follows (followee_id);
|
3
src/data/follow/mod.rs
Normal file
3
src/data/follow/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod pg;
|
||||
|
||||
pub use pg::PgFollowDataSource;
|
79
src/data/follow/pg.rs
Normal file
79
src/data/follow/pg.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use sqlx::PgPool;
|
||||
|
||||
use crate::core::*;
|
||||
use crate::model::follow::{Follow, NewFollow};
|
||||
|
||||
pub struct PgFollowDataSource {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
impl PgFollowDataSource {
|
||||
pub fn new(pool: PgPool) -> PgFollowDataSource {
|
||||
PgFollowDataSource { pool }
|
||||
}
|
||||
|
||||
pub async fn by_id<I>(&self, id: I) -> Result<Follow>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
let follow: Follow = sqlx::query_as("SELECT * FROM follows WHERE id = $1")
|
||||
.bind(id.into())
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(follow)
|
||||
}
|
||||
|
||||
pub async fn create<T>(&self, new: T) -> Result<Follow>
|
||||
where
|
||||
T: Into<NewFollow> + Send,
|
||||
{
|
||||
let new = new.into();
|
||||
let follow: Follow =
|
||||
sqlx::query_as("INSERT INTO follows (follower_id, followee_id) VALUES ($1, $2)")
|
||||
.bind(new.follower_id)
|
||||
.bind(new.followee_id)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(follow)
|
||||
}
|
||||
|
||||
pub async fn by_follower_and_followee<I, J>(
|
||||
&self,
|
||||
follower_id: I,
|
||||
followee_id: J,
|
||||
) -> Result<Follow>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
J: Into<Id> + Send,
|
||||
{
|
||||
let follow: Follow =
|
||||
sqlx::query_as("SELECT * FROM follows WHERE follower_id = $1 AND followee_id = $2")
|
||||
.bind(follower_id.into())
|
||||
.bind(followee_id.into())
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(follow)
|
||||
}
|
||||
|
||||
pub async fn followees_of<I>(&self, account_id: I) -> Result<Vec<Follow>>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
let followees: Vec<Follow> = sqlx::query_as("SELECT * FROM follows WHERE follower_id = $1")
|
||||
.bind(account_id.into())
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
Ok(followees)
|
||||
}
|
||||
|
||||
pub async fn followers_of<I>(&self, account_id: I) -> Result<Vec<Follow>>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
let followers: Vec<Follow> = sqlx::query_as("SELECT * FROM follows WHERE followee_id = $1")
|
||||
.bind(account_id.into())
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
Ok(followers)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
pub mod account;
|
||||
pub mod follow;
|
||||
pub mod status;
|
||||
pub mod user;
|
||||
|
||||
pub use account::*;
|
||||
pub use follow::*;
|
||||
pub use status::*;
|
||||
pub use user::*;
|
||||
|
|
31
src/model/follow.rs
Normal file
31
src/model/follow.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use chrono::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
|
||||
use crate::core::*;
|
||||
use crate::util::validate::{ResultBuilder, Validate};
|
||||
|
||||
#[derive(Deserialize, Serialize, FromRow)]
|
||||
pub struct Follow {
|
||||
pub id: Id,
|
||||
pub follower_id: Id,
|
||||
pub followee_id: Id,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
pub struct NewFollow {
|
||||
pub follower_id: Id,
|
||||
pub followee_id: Id,
|
||||
}
|
||||
|
||||
impl From<Follow> for Id {
|
||||
fn from(follow: Follow) -> Id {
|
||||
follow.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Validate for NewFollow {
|
||||
fn validate(&self, builder: ResultBuilder) -> ResultBuilder {
|
||||
builder
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
pub mod account;
|
||||
pub mod follow;
|
||||
pub mod status;
|
||||
pub mod user;
|
||||
|
||||
pub use account::{Account, NewAccount};
|
||||
pub use follow::{Follow, NewFollow};
|
||||
pub use status::{NewStatus, Status};
|
||||
pub use user::{NewUser, User};
|
||||
|
|
74
src/repo/follow.rs
Normal file
74
src/repo/follow.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use sqlx::PgPool;
|
||||
|
||||
use crate::core::*;
|
||||
use crate::data::follow::PgFollowDataSource;
|
||||
use crate::model::follow::{Follow, NewFollow};
|
||||
|
||||
pub struct FollowRepo {
|
||||
db: PgFollowDataSource,
|
||||
}
|
||||
|
||||
impl FollowRepo {
|
||||
pub fn new(db_pool: PgPool) -> FollowRepo {
|
||||
FollowRepo {
|
||||
db: PgFollowDataSource::new(db_pool),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn by_id<I>(&self, id: I) -> Result<Follow>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
self.db.by_id(id).await
|
||||
}
|
||||
|
||||
pub async fn create<T>(&self, new: T) -> Result<Follow>
|
||||
where
|
||||
T: Into<NewFollow> + Send,
|
||||
{
|
||||
self.db.create(new).await
|
||||
}
|
||||
|
||||
pub async fn by_follower_and_followee<I, J>(
|
||||
&self,
|
||||
follower_id: I,
|
||||
followee_id: J,
|
||||
) -> Result<Follow>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
J: Into<Id> + Send,
|
||||
{
|
||||
self.db
|
||||
.by_follower_and_followee(follower_id, followee_id)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn follows<I, J>(&self, follower_id: I, followee_id: J) -> Result<bool>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
J: Into<Id> + Send,
|
||||
{
|
||||
match self
|
||||
.by_follower_and_followee(follower_id, followee_id)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(Error::NotFound) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn followees_of<I>(&self, account_id: I) -> Result<Vec<Follow>>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
self.db.followees_of(account_id).await
|
||||
}
|
||||
|
||||
pub async fn followers_of<I>(&self, account_id: I) -> Result<Vec<Follow>>
|
||||
where
|
||||
I: Into<Id> + Send,
|
||||
{
|
||||
self.db.followers_of(account_id).await
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
use sqlx::PgPool;
|
||||
|
||||
pub mod account;
|
||||
pub mod follow;
|
||||
pub mod status;
|
||||
pub mod user;
|
||||
use account::AccountRepo;
|
||||
use follow::FollowRepo;
|
||||
use status::StatusRepo;
|
||||
use user::UserRepo;
|
||||
|
||||
|
@ -12,6 +14,7 @@ use user::UserRepo;
|
|||
/// All interactions with resident data must happen through this interface.
|
||||
pub struct AppRepo {
|
||||
pub accounts: AccountRepo,
|
||||
pub follows: FollowRepo,
|
||||
pub statuses: StatusRepo,
|
||||
pub users: UserRepo,
|
||||
}
|
||||
|
@ -20,6 +23,7 @@ impl AppRepo {
|
|||
pub fn new(db_pool: PgPool) -> AppRepo {
|
||||
AppRepo {
|
||||
accounts: AccountRepo::new(db_pool.clone()),
|
||||
follows: FollowRepo::new(db_pool.clone()),
|
||||
statuses: StatusRepo::new(db_pool.clone()),
|
||||
users: UserRepo::new(db_pool.clone()),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue