bookforge/src/models/user.rs
2026-01-30 14:24:00 +01:00

160 lines
4.9 KiB
Rust

use crate::models::book;
use crate::routes::book::BookForm;
use crate::routes::user::IndexQuery;
use crate::routes::user::UserForm;
use crate::state::AppState;
use crate::state::error::UserSnafu;
use sea_orm::ActiveValue::Set;
use sea_orm::Condition;
use sea_orm::DeleteResult;
use sea_orm::entity::prelude::*;
use snafu::ResultExt;
use snafu::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
// #[sea_orm(has_many, relation_enum = "Owner", from = "id", to = "owner_id")]
// pub books: HasMany<super::book::Entity>,
// #[sea_orm(
// has_many,
// relation_enum = "CurrentHolder",
// from = "id",
// to = "current_holder_id"
// )]
// pub books_borrowed: HasMany<super::book::Entity>,
}
#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {}
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum UserError {
// #[snafu(display("The Content Folder (Path: {path}) does not exist"))]
// NotFound { path: String },
#[snafu(display("Database error"))]
DB { source: sea_orm::DbErr },
#[snafu(display("User with id {id} not found"))]
NotFound { id: i32 },
#[snafu(display("Book error"))]
Book { source: super::book::BookError },
}
#[derive(Debug)]
pub struct UserOperator {
pub state: AppState,
}
impl UserOperator {
pub fn new(state: AppState) -> Self {
Self { state }
}
pub async fn all(&self) -> Result<Vec<Model>, UserError> {
Entity::find().all(&self.state.db).await.context(DBSnafu)
}
pub async fn all_filtered(&self, query: IndexQuery) -> Result<Vec<Model>, UserError> {
let mut conditions = Condition::all();
if let Some(name) = query.name {
conditions = conditions.add(Column::Name.contains(name))
}
Entity::find()
.filter(conditions)
.all(&self.state.db)
.await
.context(DBSnafu)
}
pub async fn find_by_id(&self, id: i32) -> Result<Model, UserError> {
let user: Option<Model> = Entity::find_by_id(id)
.one(&self.state.db)
.await
.context(DBSnafu)?;
if let Some(user) = user {
Ok(user)
} else {
Err(UserError::NotFound { id })
}
}
pub async fn create(&self, form: UserForm) -> Result<Model, UserError> {
let user = ActiveModel {
name: Set(form.name),
..Default::default()
};
user.insert(&self.state.db).await.context(DBSnafu)
}
pub async fn update(&self, id: i32, form: UserForm) -> Result<Model, UserError> {
let user_by_id = Self::find_by_id(&self, id).await.context(UserSnafu);
if let Ok(user) = user_by_id {
let mut user: ActiveModel = user.into();
user.name = Set(form.name);
user.update(&self.state.db).await.context(DBSnafu)
} else {
Err(UserError::NotFound { id })
}
}
/// Delete user by ID.
/// Before deleting the user, you must search for all the books they own in order to delete them beforehand,
/// then search for all the books they have borrowed in order to update the current holder to None.
pub async fn delete(&self, user_id: i32) -> Result<DeleteResult, UserError> {
// get all
let owner_books = book::BookOperator::new(self.state.clone())
.find_all_by_owner(user_id)
.await
.context(BookSnafu)?;
// Delete all book with owner_id = current_user
for owner_book in owner_books {
book::BookOperator::new(self.state.clone())
.delete(owner_book.id)
.await
.context(BookSnafu)?;
}
let current_holder_books = book::BookOperator::new(self.state.clone())
.find_all_by_current_holder(user_id)
.await
.context(BookSnafu)?;
// Update all book with current Holder = current user
for current_holder_book in current_holder_books {
let form = BookForm {
title: current_holder_book.title,
authors: current_holder_book.authors,
owner_id: current_holder_book.owner_id,
description: current_holder_book.description,
comment: current_holder_book.comment,
current_holder_id: None,
};
book::BookOperator::new(self.state.clone())
.update(current_holder_book.id, form)
.await
.context(BookSnafu)?;
}
let user: Option<Model> = Entity::find_by_id(user_id)
.one(&self.state.db)
.await
.context(DBSnafu)?;
let user: Model = user.unwrap();
user.delete(&self.state.db).await.context(DBSnafu)
}
}