use std::sync::Arc; use rand::Rng; use tokio::sync::Mutex; use tokio_postgres::{Client, Row}; use super::db; #[derive(Clone)] pub struct Users(Arc>); impl Users { pub fn new(client: Arc>) -> Self { Self(client) } fn new_id() -> String { let bytes = rand::thread_rng().gen::<[u8; 16]>(); base_62::encode(&bytes) } 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) values ($1, $2, $3, $4) returning id", &[&Self::new_id(), &u.username, &u.host, &u.display_name], ).await?; Ok(User { id: row.get("id"), username: u.username, host: u.host, display_name: u.display_name, }) } pub async fn user(&self, by: UserSelect) -> Result, anyhow::Error> { 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 from users where {}", where_clause ) .as_str(), &[&where_param], ) .await?; if let Some(row) = rows.first() && rows.len() == 1 { Ok(Some(User::from(row))) } else { Ok(None) } } } pub struct User { pub id: String, pub username: String, pub host: Option, pub display_name: Option, } 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"), } } } pub enum UserSelect { ID(String), Username(String), FullUsername(String), }