use crate::{
MyError,
db::RowID,
emit_db_error,
lrs::{
Role, User,
users::{BatchUpdateForm, UpdateForm},
},
};
use chrono::{DateTime, Utc};
use sqlx::{AssertSqlSafe, FromRow, PgPool};
use tracing::info;
#[derive(Debug, FromRow)]
pub(crate) struct TUser {
pub(crate) id: i32,
pub(crate) email: String,
#[allow(dead_code)]
credentials: i64,
pub(crate) role: i16,
pub(crate) manager_id: i32,
pub(crate) enabled: bool,
pub(crate) created: DateTime<Utc>,
pub(crate) updated: DateTime<Utc>,
}
const FIND_ACTIVE_USER: &str = r#"SELECT * FROM users WHERE credentials = $1 AND enabled = true"#;
pub(crate) async fn find_active_user(
conn: &PgPool,
credentials: u32,
) -> Result<Option<User>, MyError> {
match sqlx::query_as::<_, TUser>(FIND_ACTIVE_USER)
.bind(i64::from(credentials))
.fetch_one(conn)
.await
{
Ok(x) => Ok(Some(User::from(x))),
Err(x) => match x {
sqlx::Error::RowNotFound => Ok(None),
x => emit_db_error!(x, "Failed find_active_user(..., {})", credentials),
},
}
}
const INSERT_USER: &str = r#"INSERT INTO users (email, credentials, role, manager_id)
VALUES ($1, $2, $3, $4) RETURNING *"#;
pub(crate) async fn insert_user(
conn: &PgPool,
user: (&str, &str, Role, i32),
) -> Result<User, MyError> {
let credentials = i64::from(User::credentials_from(user.0, user.1));
match sqlx::query_as::<_, TUser>(INSERT_USER)
.bind(user.0)
.bind(credentials)
.bind(i16::from(user.2))
.bind(user.3)
.fetch_one(conn)
.await
{
Ok(x) => Ok(User::from(x)),
Err(x) => emit_db_error!(x, "Failed insert_user(..., ({}, ...))", user.0),
}
}
const FIND_USER: &str = r#"SELECT * FROM users WHERE id = $1"#;
pub(crate) async fn find_user(conn: &PgPool, id: i32) -> Result<Option<User>, MyError> {
match sqlx::query_as::<_, TUser>(FIND_USER)
.bind(id)
.fetch_one(conn)
.await
{
Ok(x) => Ok(Some(User::from(x))),
Err(x) => match x {
sqlx::Error::RowNotFound => Ok(None),
x => emit_db_error!(x, "Failed find_user(..., {})", id),
},
}
}
const FIND_GROUP_USER: &str = r#"SELECT * FROM users WHERE id = $1 AND manager_id = $2"#;
pub(crate) async fn find_group_user(
conn: &PgPool,
id: i32,
manager_id: i32,
) -> Result<Option<User>, MyError> {
match sqlx::query_as::<_, TUser>(FIND_GROUP_USER)
.bind(id)
.bind(manager_id)
.fetch_one(conn)
.await
{
Ok(x) => Ok(Some(User::from(x))),
Err(x) => match x {
sqlx::Error::RowNotFound => Ok(None),
x => emit_db_error!(x, "Failed find_group_user(..., {}, {})", id, manager_id),
},
}
}
const FIND_ALL_IDS: &str = r#"SELECT id FROM users WHERE role != 4"#;
pub(crate) async fn find_all_ids(conn: &PgPool) -> Result<Vec<i32>, MyError> {
match sqlx::query_as::<_, RowID>(FIND_ALL_IDS)
.fetch_all(conn)
.await
{
Ok(x) => {
let result = x.iter().map(|y| y.0).collect::<Vec<i32>>();
Ok(result)
}
Err(x) => emit_db_error!(x, "Failed find_all_ids(...)"),
}
}
const FIND_GROUP_MEMBER_IDS: &str = r#"SELECT id FROM users WHERE manager_id = $1"#;
pub(crate) async fn find_group_member_ids(conn: &PgPool, id: i32) -> Result<Vec<i32>, MyError> {
match sqlx::query_as::<_, RowID>(FIND_GROUP_MEMBER_IDS)
.bind(id)
.fetch_all(conn)
.await
{
Ok(x) => {
let result = x.iter().map(|y| y.0).collect::<Vec<i32>>();
Ok(result)
}
Err(x) => emit_db_error!(x, "Failed find_group_member_ids(..., {})", id),
}
}
pub(crate) async fn update_user(
conn: &PgPool,
id: i32,
form: UpdateForm<'_>,
) -> Result<User, MyError> {
let q = if let Some(z_enabled) = form.enabled {
sqlx::query_as::<_, TUser>(r#"UPDATE users SET enabled = $2 WHERE id = $1 RETURNING *"#)
.bind(id)
.bind(z_enabled)
.fetch_one(conn)
} else if let Some(z_email) = form.email {
let z_password = form.password.unwrap();
let z_credentials = i64::from(User::credentials_from(z_email, z_password));
sqlx::query_as::<_, TUser>(
r#"UPDATE users SET email = $2, credentials = $3 WHERE id = $1 RETURNING *"#,
)
.bind(id)
.bind(z_email)
.bind(z_credentials)
.fetch_one(conn)
} else if let Some(z_role) = form.role {
let z_role = i16::try_from(z_role.0).ok().unwrap();
sqlx::query_as::<_, TUser>(r#"UPDATE users SET role = $2 WHERE id = $1 RETURNING *"#)
.bind(id)
.bind(z_role)
.fetch_one(conn)
} else if let Some(z_manager_id) = form.manager_id {
sqlx::query_as::<_, TUser>(r#"UPDATE users SET manager_id = $2 WHERE id = $1 RETURNING *"#)
.bind(id)
.bind(z_manager_id)
.fetch_one(conn)
} else {
panic!("Unexpected update_user call");
};
match q.await {
Ok(x) => Ok(User::from(x)),
Err(x) => {
emit_db_error!(x, "Failed update_user(..., {}, ...)", id)
}
}
}
pub(crate) async fn batch_update_users(
conn: &PgPool,
form: BatchUpdateForm,
) -> Result<(), MyError> {
let ids = &form
.ids
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(",");
let where_clause = format!("WHERE id IN ({ids})");
if let Some(enabled) = form.enabled {
let sql = format!("UPDATE users SET enabled = $1 {where_clause}");
let safe_sql = AssertSqlSafe(sql);
match sqlx::query(safe_sql).bind(enabled).execute(conn).await {
Ok(x) => {
info!("Success: {:?}", x);
Ok(())
}
Err(x) => emit_db_error!(x, "Failed batch_update_users(..., enabled)"),
}
} else if let Some(z_role) = form.role.as_ref() {
let sql = format!("UPDATE users SET role = $1 {where_clause}");
let safe_sql = AssertSqlSafe(sql);
let role = i16::try_from(z_role.0).expect("Failed coercing role");
match sqlx::query(safe_sql).bind(role).execute(conn).await {
Ok(x) => {
info!("Success: {:?}", x);
Ok(())
}
Err(x) => emit_db_error!(x, "Failed batch_update_users(..., role)"),
}
} else if let Some(manager_id) = form.manager_id {
let sql = format!("UPDATE users SET manager_id = $1 {where_clause}");
let safe_sql = AssertSqlSafe(sql);
match sqlx::query(safe_sql).bind(manager_id).execute(conn).await {
Ok(x) => {
info!("Success: {:?}", x);
Ok(())
}
Err(x) => emit_db_error!(x, "Failed batch_update_users(..., manager_id)"),
}
} else {
panic!("Unexpected batch_update_users(..., {form:?}) call");
}
}