enums, migration change

This commit is contained in:
Brandon Thomas committed 2025-08-07 21:56:14 -04:00
1 parent 0ea24480db
commit d5030f96ba
13 files changed
+304 -18

No files matched your search

@@ -10,29 +10,29 @@ CREATE TABLE tasks (
-- TaskStatus enum
-- This is how the job system manages job states.
-- pending, started, complete_success, dead, etc.
-- e.g. pending, started, complete_success, dead, etc.
task_status TEXT NOT NULL,
-- TaskType enum
-- image_generation, video_generation, etc.
-- e.g. image_generation, video_generation, etc.
task_type TEXT NOT NULL,
-- TaskModelType enum
-- flux_1_dev, veo_2, etc.
-- e.g. flux_1_dev, veo_2, etc.
model_type TEXT,
-- GenerationProvider enum
-- Together with `provider_job_id`, this creates a foreign key lookup to the job.
-- 'artcraft', 'fal', 'sora', etc.
-- e.g. 'artcraft', 'fal', 'sora', etc.
provider TEXT,
-- The primary key for the job in the provider's system.
provider_job_id TEXT,
-- OPTIONAL.
-- Tell the job system which event(s) to send to the frontend
-- upon task completion. Comma-separated list of event names.
frontend_subscriber_event_names TEXT,
-- Tell the job system which caller initiated the task.
-- e.g. 'canvas'
frontend_caller TEXT,
-- OPTIONAL.
-- An arbitrary opaque identifier that frontend can set.
@@ -1,4 +1,5 @@
use crate::core::events::sendable_event_error::SendableEventError;
use enums::tauri::ux::tauri_event_name::TauriEventName;
use log::info;
use serde::Serialize;
use std::fmt::Debug;
@@ -29,7 +30,7 @@ struct BasicEventWrapper<T: Serialize + Debug> {
pub trait BasicSendableEvent : Clone + Debug + Serialize {
/// This is the name of the event that the frontend subscribes to.
const FRONTEND_EVENT_NAME: &'static str;
const FRONTEND_EVENT_NAME: TauriEventName;
/// This is the status of the event: Success, Failure, etc.
const EVENT_STATUS: BasicEventStatus;
@@ -41,8 +42,8 @@ pub trait BasicSendableEvent : Clone + Debug + Serialize {
status: Self::EVENT_STATUS,
data: self.clone(),
};
info!("Sending event to frontend: {} ; payload = {:?}", Self::FRONTEND_EVENT_NAME, wrapped);
let result = app.emit(Self::FRONTEND_EVENT_NAME, wrapped);
info!("Sending event to frontend: {} ; payload = {:?}", Self::FRONTEND_EVENT_NAME.to_str(), wrapped);
let result = app.emit(Self::FRONTEND_EVENT_NAME.to_str(), wrapped);
if let Err(err) = result {
return Err(SendableEventError::from(err));
}
@@ -1,6 +1,7 @@
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
use serde_derive::Serialize;
use url::Url;
use enums::tauri::ux::tauri_event_name::TauriEventName;
use tokens::tokens::media_files::MediaFileToken;
#[derive(Clone, Debug, Serialize)]
@@ -11,6 +12,6 @@ pub struct CanvasBackgroundRemovalCompleteEvent {
}
impl BasicSendableEvent for CanvasBackgroundRemovalCompleteEvent {
const FRONTEND_EVENT_NAME: &'static str = "canvas-background-removal-complete-event";
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::CanvasBgRemovedEvent;
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
}
@@ -1,5 +1,6 @@
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
use enums::tauri::ux::tauri_event_name::TauriEventName;
use serde_derive::Serialize;
#[derive(Clone, Debug, Serialize)]
@@ -11,6 +12,6 @@ pub struct GenerationCompleteEvent {
}
impl BasicSendableEvent for GenerationCompleteEvent {
const FRONTEND_EVENT_NAME: &'static str = "generation-complete-event";
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::GenerationCompleteEvent;
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
}
@@ -1,5 +1,6 @@
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
use enums::tauri::ux::tauri_event_name::TauriEventName;
use serde_derive::Serialize;
#[derive(Clone, Debug, Serialize)]
@@ -14,7 +15,7 @@ pub struct GenerationEnqueueFailureEvent {
}
impl BasicSendableEvent for GenerationEnqueueFailureEvent {
const FRONTEND_EVENT_NAME: &'static str = "generation-enqueue-failure-event";
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::GenerationEnqueueFailureEvent;
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Failure;
}
@@ -1,5 +1,6 @@
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
use enums::tauri::ux::tauri_event_name::TauriEventName;
use serde_derive::Serialize;
#[derive(Clone, Debug, Serialize)]
@@ -11,6 +12,6 @@ pub struct GenerationEnqueueSuccessEvent {
}
impl BasicSendableEvent for GenerationEnqueueSuccessEvent {
const FRONTEND_EVENT_NAME: &'static str = "generation-enqueue-success-event";
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::GenerationEnqueueSuccessEvent;
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
}
@@ -1,5 +1,6 @@
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
use crate::core::events::generation_events::common::{GenerationAction, GenerationModel, GenerationServiceProvider};
use enums::tauri::ux::tauri_event_name::TauriEventName;
use serde_derive::Serialize;
#[derive(Clone, Debug, Serialize)]
@@ -14,6 +15,6 @@ pub struct GenerationFailedEvent {
}
impl BasicSendableEvent for GenerationFailedEvent {
const FRONTEND_EVENT_NAME: &'static str = "generation-failed-event";
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::GenerationFailedEvent;
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Failure;
}
@@ -37,7 +37,7 @@ impl GenerationProvider {
"artcraft" => Ok(Self::Artcraft),
"fal" => Ok(Self::Fal),
"sora" => Ok(Self::Sora),
_ => Err(format!("invalid task_state: {:?}", job_status)),
_ => Err(format!("invalid generation_provider: {:?}", job_status)),
}
}
@@ -108,7 +108,7 @@ impl ModelType {
"hunyuan_3d_2p0" => Ok(Self::Hunyuan3d2_0),
"hunyuan_3d_2p1" => Ok(Self::Hunyuan3d2_1),
_ => Err(format!("invalid task_state: {:?}", job_status)),
_ => Err(format!("invalid model_type: {:?}", job_status)),
}
}
+2 -1
View File
@@ -1 +1,2 @@
pub mod tasks;
pub mod tasks;
pub mod ux;
@@ -0,0 +1,2 @@
pub mod tauri_command_caller;
pub mod tauri_event_name;
@@ -0,0 +1,109 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Defines the names of the Tauri-sent events that the frontend subscribes to.
/// These event names are also stored in the database, so keep them short-ish.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum TauriCommandCaller {
/// The 2D canvas
Canvas,
}
impl_enum_display_and_debug_using_to_str!(TauriCommandCaller);
impl_mysql_enum_coders!(TauriCommandCaller);
impl_mysql_from_row!(TauriCommandCaller);
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
impl TauriCommandCaller {
pub fn to_str(&self) -> &'static str {
match self {
Self::Canvas => "canvas",
}
}
pub fn from_str(job_status: &str) -> Result<Self, String> {
match job_status {
"canvas" => Ok(Self::Canvas),
_ => Err(format!("invalid tauri_command_caller: {:?}", job_status)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
Self::Canvas,
])
}
}
#[cfg(test)]
mod tests {
use crate::test_helpers::assert_serialization;
use crate::tauri::ux::tauri_command_caller::TauriCommandCaller;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(TauriCommandCaller::Canvas, "canvas");
}
#[test]
fn to_str() {
assert_eq!(TauriCommandCaller::Canvas.to_str(), "canvas");
}
#[test]
fn from_str() {
assert_eq!(TauriCommandCaller::from_str("canvas").unwrap(), TauriCommandCaller::Canvas);
}
#[test]
fn all_variants() {
let mut variants = TauriCommandCaller::all_variants();
assert_eq!(variants.len(), 1);
assert_eq!(variants.pop_first(), Some(TauriCommandCaller::Canvas));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
use strum::IntoEnumIterator;
#[test]
fn variant_length() {
assert_eq!(TauriCommandCaller::all_variants().len(), TauriCommandCaller::iter().len());
}
#[test]
fn round_trip() {
for variant in TauriCommandCaller::all_variants() {
// Test to_str(), from_str(), Display, and Debug.
assert_eq!(variant, TauriCommandCaller::from_str(variant.to_str()).unwrap());
assert_eq!(variant, TauriCommandCaller::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, TauriCommandCaller::from_str(&format!("{:?}", variant)).unwrap());
}
}
//#[test]
//fn serialized_length_ok_for_database() {
// const MAX_LENGTH : usize = 16;
// for variant in TauriCommandCaller::all_variants() {
// let serialized = variant.to_str();
// assert!(serialized.len() > 0, "variant {:?} is too short", variant);
// assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
// }
//}
}
}
@@ -0,0 +1,168 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Defines the names of the Tauri-sent events that the frontend subscribes to.
/// These event names are also stored in the database, so keep them short-ish.
///
/// NB: Events should end in "_event" so they're easy to grep for in Javascript.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum TauriEventName {
// TODO: Get rid of kebab case.
/// General purpose event:
/// Generation enqueued
#[serde(rename = "generation-enqueue-success-event")]
GenerationEnqueueSuccessEvent,
// TODO: Get rid of kebab case.
/// General purpose event:
/// Generation failed to enqueue
#[serde(rename = "generation-enqueue-failure-event")]
GenerationEnqueueFailureEvent,
// TODO: Get rid of kebab case.
/// General purpose event:
/// Generation completed successfully
#[serde(rename = "generation-complete-event")]
GenerationCompleteEvent,
// TODO: Get rid of kebab case.
/// General purpose event:
/// Generation failed
#[serde(rename = "generation-failed-event")]
GenerationFailedEvent,
/// Special event:
/// Background removal complete
#[serde(rename = "canvas_bg_removed_event")]
CanvasBgRemovedEvent,
}
impl_enum_display_and_debug_using_to_str!(TauriEventName);
impl_mysql_enum_coders!(TauriEventName);
impl_mysql_from_row!(TauriEventName);
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
impl TauriEventName {
pub fn to_str(&self) -> &'static str {
match self {
Self::GenerationEnqueueSuccessEvent => "generation-enqueue-success-event",
Self::GenerationEnqueueFailureEvent => "generation-enqueue-failure-event",
Self::GenerationCompleteEvent => "generation-complete-event",
Self::GenerationFailedEvent => "generation-failed-event",
Self::CanvasBgRemovedEvent => "canvas_bg_removed_event",
}
}
pub fn from_str(job_status: &str) -> Result<Self, String> {
match job_status {
"generation-enqueue-success-event" => Ok(Self::GenerationEnqueueSuccessEvent),
"generation-enqueue-failure-event" => Ok(Self::GenerationEnqueueFailureEvent),
"generation-complete-event" => Ok(Self::GenerationCompleteEvent),
"generation-failed-event" => Ok(Self::GenerationFailedEvent),
"canvas_bg_removed_event" => Ok(Self::CanvasBgRemovedEvent),
_ => Err(format!("invalid tauri_event_name: {:?}", job_status)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
Self::GenerationEnqueueSuccessEvent,
Self::GenerationEnqueueFailureEvent,
Self::GenerationCompleteEvent,
Self::GenerationFailedEvent,
Self::CanvasBgRemovedEvent,
])
}
}
#[cfg(test)]
mod tests {
use crate::tauri::ux::tauri_event_name::TauriEventName;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(TauriEventName::GenerationEnqueueSuccessEvent, "generation-enqueue-success-event");
assert_serialization(TauriEventName::GenerationEnqueueFailureEvent, "generation-enqueue-failure-event");
assert_serialization(TauriEventName::GenerationCompleteEvent, "generation-complete-event");
assert_serialization(TauriEventName::GenerationFailedEvent, "generation-failed-event");
assert_serialization(TauriEventName::CanvasBgRemovedEvent, "canvas_bg_removed_event");
}
#[test]
fn to_str() {
assert_eq!(TauriEventName::GenerationEnqueueSuccessEvent.to_str(), "generation-enqueue-success-event");
assert_eq!(TauriEventName::GenerationEnqueueFailureEvent.to_str(), "generation-enqueue-failure-event");
assert_eq!(TauriEventName::GenerationCompleteEvent.to_str(), "generation-complete-event");
assert_eq!(TauriEventName::GenerationFailedEvent.to_str(), "generation-failed-event");
assert_eq!(TauriEventName::CanvasBgRemovedEvent.to_str(), "canvas_bg_removed_event");
}
#[test]
fn from_str() {
assert_eq!(TauriEventName::from_str("generation-enqueue-success-event").unwrap(), TauriEventName::GenerationEnqueueSuccessEvent);
assert_eq!(TauriEventName::from_str("generation-enqueue-failure-event").unwrap(), TauriEventName::GenerationEnqueueFailureEvent);
assert_eq!(TauriEventName::from_str("generation-complete-event").unwrap(), TauriEventName::GenerationCompleteEvent);
assert_eq!(TauriEventName::from_str("generation-failed-event").unwrap(), TauriEventName::GenerationFailedEvent);
assert_eq!(TauriEventName::from_str("canvas_bg_removed_event").unwrap(), TauriEventName::CanvasBgRemovedEvent);
}
#[test]
fn all_variants() {
let mut variants = TauriEventName::all_variants();
assert_eq!(variants.len(), 5);
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueSuccessEvent));
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueFailureEvent));
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationCompleteEvent));
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationFailedEvent));
assert_eq!(variants.pop_first(), Some(TauriEventName::CanvasBgRemovedEvent));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
use strum::IntoEnumIterator;
#[test]
fn variant_length() {
assert_eq!(TauriEventName::all_variants().len(), TauriEventName::iter().len());
}
#[test]
fn round_trip() {
for variant in TauriEventName::all_variants() {
// Test to_str(), from_str(), Display, and Debug.
assert_eq!(variant, TauriEventName::from_str(variant.to_str()).unwrap());
assert_eq!(variant, TauriEventName::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, TauriEventName::from_str(&format!("{:?}", variant)).unwrap());
}
}
//#[test]
//fn serialized_length_ok_for_database() {
// const MAX_LENGTH : usize = 16;
// for variant in TauriEventName::all_variants() {
// let serialized = variant.to_str();
// assert!(serialized.len() > 0, "variant {:?} is too short", variant);
// assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
// }
//}
}
}