use std::borrow::Cow; use std::fmt; use std::fmt::Display; use crate::schema::backie_tasks; use chrono::DateTime; use chrono::Utc; use diesel::prelude::*; use typed_builder::TypedBuilder; use uuid::Uuid; use serde::Serialize; use diesel_derive_newtype::DieselNewType; use sha2::{Digest, Sha256}; /// States of a task. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum TaskState { /// The task is ready to be executed. Ready, /// The task is running. Running, /// The task has failed to execute. Failed, /// The task finished successfully. Done, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, DieselNewType, Serialize)] pub struct TaskId(Uuid); impl Display for TaskId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Clone, Debug, Hash, PartialEq, Eq, DieselNewType, Serialize)] pub struct TaskType(Cow<'static, str>); impl Default for TaskType { fn default() -> Self { Self(Cow::from("default")) } } impl From for TaskType where S: AsRef + 'static, { fn from(s: S) -> Self { TaskType(Cow::from(s.as_ref().to_owned())) } } #[derive(Clone, Debug, Hash, PartialEq, Eq, DieselNewType, Serialize)] pub struct TaskHash(Cow<'static, str>); impl TaskHash { pub fn default_for_task(value: &T) -> Result where T: Serialize { let value = serde_json::to_value(value)?; let mut hasher = Sha256::new(); hasher.update(serde_json::to_string(&value)?.as_bytes()); let result = hasher.finalize(); Ok(TaskHash(Cow::from(hex::encode(result)))) } } #[derive(Queryable, Identifiable, Debug, Eq, PartialEq, Clone, TypedBuilder)] #[diesel(table_name = backie_tasks)] pub struct Task { #[builder(setter(into))] pub id: TaskId, #[builder(setter(into))] pub payload: serde_json::Value, #[builder(setter(into))] pub error_message: Option, #[builder(setter(into))] pub task_type: TaskType, #[builder(setter(into))] pub uniq_hash: Option, #[builder(setter(into))] pub retries: i32, #[builder(setter(into))] pub created_at: DateTime, #[builder(setter(into))] pub running_at: Option>, #[builder(setter(into))] pub done_at: Option>, } impl Task { pub fn state(&self) -> TaskState { if self.done_at.is_some() { if self.error_message.is_some() { TaskState::Failed } else { TaskState::Done } } else if self.running_at.is_some() { TaskState::Running } else { TaskState::Ready } } } #[derive(Insertable, Debug, Eq, PartialEq, Clone, TypedBuilder)] #[diesel(table_name = backie_tasks)] pub struct NewTask { #[builder(setter(into))] payload: serde_json::Value, #[builder(setter(into))] task_type: TaskType, #[builder(setter(into))] uniq_hash: Option, } pub struct TaskInfo { id: TaskId, error_message: Option, retries: i32, created_at: DateTime, }