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>); impl Users { pub fn new(client: Arc>) -> Self { Self(client) } pub async fn create_user(&self, u: User) -> Result { 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", &[&sec::new_id(), &u.username, &u.host, &u.display_name, &u.password_hash, &u.email], ).await?; Ok(User { id: row.get("id"), username: u.username, host: u.host, display_name: u.display_name, password_hash: u.password_hash, email: u.email, }) } pub async fn user(&self, by: UserSelect) -> Result { let where_param: String; let where_clause = match by { UserSelect::ID(id) => { where_param = id; "id = $1" } UserSelect::Username(username) => { where_param = username; "username = $1" } UserSelect::FullUsername(full) => { where_param = full; "(username || '@' || host) = $1" } }; let rows = self .0 .lock() .await .query( format!( "select id, username, host, display_name, password_hash, email from users where {}", where_clause ) .as_str(), &[&where_param], ) .await?; if let Some(row) = rows.first() && rows.len() == 1 { Ok(User::from(row)) } else { Err(db::DBError::NotFound) } } } pub struct User { pub id: String, pub username: String, pub host: Option, pub display_name: Option, pub password_hash: String, pub email: 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"), } } } pub enum UserSelect { ID(String), Username(String), FullUsername(String), }