148 lines
3.9 KiB
Rust
148 lines
3.9 KiB
Rust
use std::sync::Arc;
|
|
|
|
use tokio::sync::Mutex;
|
|
use tokio_postgres::{Client, Row};
|
|
|
|
use crate::sec;
|
|
|
|
use super::db;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Users(Arc<Mutex<Client>>);
|
|
|
|
impl Users {
|
|
pub fn new(client: Arc<Mutex<Client>>) -> Self {
|
|
Self(client)
|
|
}
|
|
|
|
pub async fn create_user(&self, u: User) -> Result<User, db::DBError> {
|
|
let row = self.0.lock().await.query_one(
|
|
"insert into users (id, username, host, display_name, password_hash, email) values ($1, $2, $3, $4, $5, $6) returning (id, username, host, display_name, password_hash, email, avatar_uri, bio)",
|
|
&[&sec::new_id(), &u.username, &u.host, &u.display_name, &u.password_hash, &u.email],
|
|
).await?;
|
|
Ok(User::from(&row))
|
|
}
|
|
|
|
pub async fn user_stats(&self, by: UserSelect) -> Result<UserStats, db::DBError> {
|
|
let (clause, param) = by.into();
|
|
let rows = self
|
|
.0
|
|
.lock()
|
|
.await
|
|
.query(
|
|
format!(r#"select count(follows.*) as following, count(followed.*) as followers from users
|
|
left join follows on follows.user_id = users.id
|
|
left join follows followed on followed.follows_id = users.id
|
|
where {}"#, clause).as_str(),
|
|
&[¶m],
|
|
)
|
|
.await?;
|
|
|
|
Ok(rows.first().ok_or(db::DBError::NotFound)?.into())
|
|
}
|
|
|
|
pub async fn user(&self, by: UserSelect) -> Result<User, db::DBError> {
|
|
let (clause, param) = by.into();
|
|
let rows = self
|
|
.0
|
|
.lock()
|
|
.await
|
|
.query(
|
|
format!(
|
|
"select id, username, host, display_name, password_hash, email, avatar_uri, bio from users where {}",
|
|
clause,
|
|
)
|
|
.as_str(),
|
|
&[¶m],
|
|
)
|
|
.await?;
|
|
|
|
if let Some(row) = rows.first() && rows.len() == 1 {
|
|
Ok(User::from(row))
|
|
} else {
|
|
Err(db::DBError::NotFound)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct User {
|
|
pub id: String,
|
|
pub username: String,
|
|
pub host: Option<String>,
|
|
pub display_name: Option<String>,
|
|
pub password_hash: String,
|
|
pub email: String,
|
|
pub avatar_uri: Option<String>,
|
|
pub bio: Option<String>,
|
|
}
|
|
|
|
impl From<&Row> for User {
|
|
fn from(row: &Row) -> Self {
|
|
Self {
|
|
id: row.get("id"),
|
|
username: row.get("username"),
|
|
host: row.get("host"),
|
|
display_name: row.get("display_name"),
|
|
password_hash: row.get("password_hash"),
|
|
email: row.get("email"),
|
|
avatar_uri: row.get("avatar_uri"),
|
|
bio: row.get("bio"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum UserSelect {
|
|
ID(String),
|
|
Username(String),
|
|
FullUsername(String),
|
|
}
|
|
|
|
impl From<String> for UserSelect {
|
|
fn from(username: String) -> Self {
|
|
if !username.contains("@") {
|
|
Self::Username(username)
|
|
} else {
|
|
Self::FullUsername(username)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Into<(String, String)> for UserSelect {
|
|
fn into(self) -> (String, String) {
|
|
let where_param: String;
|
|
let where_clause = match self {
|
|
UserSelect::ID(id) => {
|
|
where_param = id;
|
|
"users.id = $1"
|
|
}
|
|
UserSelect::Username(username) => {
|
|
where_param = username;
|
|
"users.username = $1"
|
|
}
|
|
UserSelect::FullUsername(full) => {
|
|
where_param = full;
|
|
"(users.username || '@' || users.host) = $1"
|
|
}
|
|
};
|
|
(where_clause.to_owned(), where_param)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct UserStats {
|
|
pub post_count: i64,
|
|
pub following: i64,
|
|
pub followers: i64,
|
|
}
|
|
|
|
impl From<&Row> for UserStats {
|
|
fn from(row: &Row) -> Self {
|
|
Self {
|
|
post_count: 100,
|
|
following: row.get("following"),
|
|
followers: row.get("followers"),
|
|
}
|
|
}
|
|
}
|