//! Persistence for users. Functions take a connection so callers can group
//! several of them in one transaction.
use slave_framework::turso::{Connection, Result, Row};
use crate::modules::user::models::user::User;
const COLUMNS: &str = "id, name, email, password_hash, created_at";
pub async fn all(conn: &Connection) -> Result<Vec<User>> {
let mut rows = conn
.query(format!("SELECT {COLUMNS} FROM users ORDER BY id"), ())
.await?;
let mut users = Vec::new();
while let Some(row) = rows.next().await? {
users.push(from_row(&row)?);
}
Ok(users)
}
pub async fn find(conn: &Connection, id: i64) -> Result<Option<User>> {
let mut rows = conn
.query(format!("SELECT {COLUMNS} FROM users WHERE id = ?1"), (id,))
.await?;
rows.next().await?.map(|row| from_row(&row)).transpose()
}
pub async fn find_by_email(conn: &Connection, email: &str) -> Result<Option<User>> {
let mut rows = conn
.query(
format!("SELECT {COLUMNS} FROM users WHERE email = ?1"),
(email,),
)
.await?;
rows.next().await?.map(|row| from_row(&row)).transpose()
}
pub async fn count(conn: &Connection) -> Result<i64> {
let mut rows = conn.query("SELECT count(*) FROM users", ()).await?;
match rows.next().await? {
Some(row) => row.get(0),
None => Ok(0),
}
}
pub async fn insert(
conn: &Connection,
name: &str,
email: &str,
password_hash: &str,
) -> Result<User> {
let mut rows = conn
.query(
format!(
"INSERT INTO users (name, email, password_hash) VALUES (?1, ?2, ?3) RETURNING {COLUMNS}"
),
(name, email, password_hash),
)
.await?;
match rows.next().await? {
Some(row) => from_row(&row),
None => Err(slave_framework::turso::Error::QueryReturnedNoRows),
}
}
/// Deletes a user; their role assignments and tokens are removed by cascade.
pub async fn delete(conn: &Connection, id: i64) -> Result<bool> {
Ok(conn
.execute("DELETE FROM users WHERE id = ?1", (id,))
.await?
> 0)
}
fn from_row(row: &Row) -> Result<User> {
Ok(User {
id: row.get(0)?,
name: row.get(1)?,
email: row.get(2)?,
password_hash: row.get(3)?,
created_at: row.get(4)?,
})
}