Something went wrong. Try again.
A simple to-do app focused on tasks that can be completed within a specific time span. taskline.tobinio.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201use chrono::Local;use orm::{OrmConnection, entities::category};
use sea_orm::{ ActiveValue::{NotSet, Set}, ConnectionTrait, EntityTrait, ExprTrait, IntoActiveModel, Order, QueryFilter, QueryOrder, TransactionSession, TransactionTrait, sea_query::Expr,};use thiserror::Error;use types::tag::{Category, HexColor};use uuid::Uuid;
use crate::User;
#[derive(Error, Debug, PartialEq)]pub enum CategoryServiceError { #[error("database error - caused by: {0}")] DbError(#[from] sea_orm::DbErr),
#[error("invalid category - caused by: {0}")] InvalidCategory(String),
#[error("category not found")] NotFound,}
pub struct CategoryService;
impl CategoryService { pub async fn get_all( user: &User, db: &OrmConnection, ) -> Result<Vec<Category>, CategoryServiceError> { let categories = category::Entity::find() .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .filter(category::COLUMN.deleted.is_null()) .order_by(category::Column::Order, Order::Asc) .all(db) .await?;
categories .into_iter() .map(|c| to_category_dto(&c.into_ex())) .collect() }
pub async fn get_by_id( user: &User, db: &OrmConnection, category_id: Uuid, ) -> Result<Option<Category>, CategoryServiceError> { let category = category::Entity::find_by_id(category_id) .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .one(db) .await?;
category.map(|c| to_category_dto(&c.into_ex())).transpose() }
pub async fn delete_by_id( user: &User, db: &OrmConnection, category_id: Uuid, ) -> Result<(), CategoryServiceError> { category::Entity::update(category::ActiveModel { id: Set(category_id), deleted: Set(Some(Local::now().into())), ..Default::default() }) .validate()? .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .exec(db) .await?;
Ok(()) }
pub async fn create( user: &User, db: &OrmConnection, category: Category, ) -> Result<Category, CategoryServiceError> { let tx = db.begin().await?;
let position_index = category::Entity::find() .order_by_asc(category::COLUMN.order) .one(&tx) .await? .map_or(0, |todo| todo.order - 1);
let category = category::Entity::insert(category::ActiveModel { id: Set(category.uuid), owner_id: Set(user.id().to_string()), name: Set(category.name), color: Set(category.color.as_str().to_string()), icon: Set(category.icon), deleted: NotSet, order: Set(position_index), }) .exec_with_returning(&tx) .await?;
tx.commit().await?;
to_category_dto(&category.into_ex()) }
pub async fn update( user: &User, db: &OrmConnection, category: Category, ) -> Result<Category, CategoryServiceError> { let category = category::Entity::update(category::ActiveModel { id: Set(category.uuid), owner_id: Set(user.id().to_string()), name: Set(category.name), color: Set(category.color.as_str().to_string()), icon: Set(category.icon), deleted: NotSet, order: NotSet, }) .validate()? .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .exec(db) .await?;
to_category_dto(&category.into_ex()) }
pub async fn move_category( user: &User, db: &OrmConnection, category: Uuid, target: Uuid, ) -> Result<(), CategoryServiceError> { let tx = db.begin().await?;
let category = category::Entity::find_by_id(category) .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .one(&tx) .await? .ok_or(CategoryServiceError::NotFound)?;
let target = category::Entity::find_by_id(target) .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .one(&tx) .await? .ok_or(CategoryServiceError::NotFound)?;
if category.order < target.order { Self::move_to(user, &tx, category, target.order + 1).await?; } else { Self::move_to(user, &tx, category, target.order).await?; }
tx.commit().await?; Ok(()) }
async fn move_to<T: TransactionTrait + ConnectionTrait>( user: &User, db: &T, category: category::Model, target: i32, ) -> Result<(), sea_orm::DbErr> { let tx = db.begin().await?;
let mut model = category.into_active_model(); model.order = Set(target);
category::Entity::update_many() .col_expr( category::COLUMN.order, Expr::col(category::COLUMN.order).add(1), ) .filter(category::COLUMN.order.gte(target)) .filter(category::COLUMN.owner_id.eq(user.id().to_string())) .exec(db) .await?;
category::Entity::update(model).exec(db).await?;
tx.commit().await?; Ok(()) }}
pub fn to_category_dto(value: &category::ModelEx) -> Result<Category, CategoryServiceError> { let category = Category { uuid: value.id, name: value.name.clone(), color: HexColor::from_text(&value.color).ok_or(CategoryServiceError::InvalidCategory( "invalid color".to_string(), ))?, icon: value.icon.clone(), };
Ok(category)}