mirror of
https://git.dirksys.ovh/dirk/bankserver.git
synced 2026-01-06 11:12:41 +01:00
167 lines
4.7 KiB
Rust
167 lines
4.7 KiB
Rust
use deadpool_postgres::GenericClient;
|
|
use serde::Serialize;
|
|
use tokio_postgres::Row;
|
|
use tracing::instrument;
|
|
use uuid::{NoContext, Timestamp, Uuid};
|
|
|
|
use crate::api::{Pagination, PaginationType, RequestPagination};
|
|
|
|
use super::count;
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[cfg_attr(feature = "schemas", derive(schemars::JsonSchema))]
|
|
pub struct Account {
|
|
pub id: Uuid,
|
|
pub user: Uuid,
|
|
pub name: String,
|
|
pub balance: u64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[cfg_attr(feature = "schemas", derive(schemars::JsonSchema))]
|
|
pub struct AccountInfo {
|
|
pub id: Uuid,
|
|
pub user: Uuid,
|
|
pub name: String,
|
|
}
|
|
#[derive(Debug, Serialize)]
|
|
#[cfg_attr(feature = "schemas", derive(schemars::JsonSchema))]
|
|
pub struct UserAccountInfo {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub balance: u64,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[cfg_attr(feature = "schemas", derive(schemars::JsonSchema))]
|
|
pub struct ReducedAccountInfo {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
}
|
|
|
|
impl PaginationType for UserAccountInfo {
|
|
const NAME: &str = "UserAccounts";
|
|
}
|
|
impl PaginationType for AccountInfo {
|
|
const NAME: &str = "Accounts";
|
|
}
|
|
|
|
impl From<Row> for AccountInfo {
|
|
fn from(value: Row) -> Self {
|
|
Self {
|
|
id: value.get("id"),
|
|
user: value.get("user"),
|
|
name: value.get("name"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Row> for UserAccountInfo {
|
|
fn from(value: Row) -> Self {
|
|
Self {
|
|
id: value.get("id"),
|
|
name: value.get("name"),
|
|
balance: value.get::<_, i64>("balance") as u64,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Account {
|
|
#[instrument(skip(client))]
|
|
pub async fn create(
|
|
client: &mut impl GenericClient,
|
|
id: Option<Uuid>,
|
|
user: Uuid,
|
|
name: &str,
|
|
) -> Result<Uuid, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached(
|
|
"insert into accounts(id, \"user\", name, balance) values ($1, $2, $3, $4)",
|
|
)
|
|
.await?;
|
|
let id = id.unwrap_or_else(|| Uuid::new_v7(Timestamp::now(NoContext)));
|
|
let balance: i64 = if id == user { 1000 * 100 } else { 0 };
|
|
client
|
|
.execute(&stmt, &[&id, &user, &name, &balance])
|
|
.await?;
|
|
Ok(id)
|
|
}
|
|
|
|
#[instrument(skip(client))]
|
|
pub async fn list_all(
|
|
client: &impl GenericClient,
|
|
pagination: RequestPagination,
|
|
) -> Result<Pagination<AccountInfo>, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached("select id,\"user\",name from accounts limit $1 offset $2")
|
|
.await?;
|
|
let stmt_count = client
|
|
.prepare_cached("select count(*) from accounts")
|
|
.await?;
|
|
let users = client
|
|
.query(&stmt, &[&pagination.limit(), &pagination.offset()])
|
|
.await?
|
|
.into_iter()
|
|
.map(AccountInfo::from)
|
|
.collect();
|
|
let total = count(client, &stmt_count, &[]).await?;
|
|
Ok(Pagination::new(users, total, pagination))
|
|
}
|
|
|
|
#[instrument(skip(client))]
|
|
pub async fn list_for_user(
|
|
client: &impl GenericClient,
|
|
user: Uuid,
|
|
) -> Result<Vec<UserAccountInfo>, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached("select id,name,balance from accounts where \"user\" = $1")
|
|
.await?;
|
|
let users = client
|
|
.query(&stmt, &[&user])
|
|
.await?
|
|
.into_iter()
|
|
.map(UserAccountInfo::from)
|
|
.collect();
|
|
Ok(users)
|
|
}
|
|
|
|
pub async fn by_id(
|
|
client: &impl GenericClient,
|
|
id: Uuid,
|
|
) -> Result<Option<UserAccountInfo>, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached("select id,name,balance from accounts where id =$1")
|
|
.await?;
|
|
let res = client.query_opt(&stmt, &[&id]).await?;
|
|
Ok(res.map(UserAccountInfo::from))
|
|
}
|
|
|
|
pub async fn owned_by(
|
|
client: &impl GenericClient,
|
|
account: Uuid,
|
|
user: Uuid,
|
|
) -> Result<Option<bool>, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached("select user from accounts where id = $1")
|
|
.await?;
|
|
let Some(res) = client.query_opt(&stmt, &[&account]).await? else {
|
|
return Ok(None);
|
|
};
|
|
Ok(Some(res.get::<_, Uuid>(0) == user))
|
|
}
|
|
|
|
pub async fn get_for_user(
|
|
client: &impl GenericClient,
|
|
user: Uuid,
|
|
name: &str,
|
|
) -> Result<Option<UserAccountInfo>, tokio_postgres::Error> {
|
|
let stmt = client
|
|
.prepare_cached(
|
|
"select id,name,balance from accounts where \"user\" = $1 and name = $2",
|
|
)
|
|
.await?;
|
|
let res = client.query_opt(&stmt, &[&user, &name]).await?;
|
|
Ok(res.map(UserAccountInfo::from))
|
|
}
|
|
}
|